|
commit 0ff395c154ad827c0c30eefc9371ba7f7c171027
|
|
Author: Willy Tarreau <w@1wt.eu>
|
|
Date: Tue Jul 30 11:59:34 2019 +0200
|
|
|
|
BUG/MAJOR: queue/threads: avoid an AB/BA locking issue in process_srv_queue()
|
|
|
|
A problem involving server slowstart was reported by @max2k1 in issue #197.
|
|
The problem is that pendconn_grab_from_px() takes the proxy lock while
|
|
already under the server's lock while process_srv_queue() first takes the
|
|
proxy's lock then the server's lock.
|
|
|
|
While the latter seems more natural, it is fundamentally incompatible with
|
|
mayn other operations performed on servers, namely state change propagation,
|
|
where the proxy is only known after the server and cannot be locked around
|
|
the servers. Howwever reversing the lock in process_srv_queue() is trivial
|
|
and only the few functions related to dynamic cookies need to be adjusted
|
|
for this so that the proxy's lock is taken for each server operation. This
|
|
is possible because the proxy's server list is built once at boot time and
|
|
remains stable. So this is what this patch does.
|
|
|
|
The comments in the proxy and server structs were updated to mention this
|
|
rule that the server's lock may not be taken under the proxy's lock but
|
|
may enclose it.
|
|
|
|
Another approach could consist in using a second lock for the proxy's queue
|
|
which would be different from the regular proxy's lock, but given that the
|
|
operations above are rare and operate on small servers list, there is no
|
|
reason for overdesigning a solution.
|
|
|
|
This fix was successfully tested with 10000 servers in a backend where
|
|
adjusting the dyncookies in loops over the CLI didn't have a measurable
|
|
impact on the traffic.
|
|
|
|
The only workaround without the fix is to disable any occurrence of
|
|
"slowstart" on server lines, or to disable threads using "nbthread 1".
|
|
|
|
This must be backported as far as 1.8.
|
|
|
|
(cherry picked from commit 5e83d996cf965ee5ac625f702a446f4d8c80a220)
|
|
Signed-off-by: Willy Tarreau <w@1wt.eu>
|
|
|
|
diff --git a/include/types/proxy.h b/include/types/proxy.h
|
|
index ca24dbfe..2518f88d 100644
|
|
--- a/include/types/proxy.h
|
|
+++ b/include/types/proxy.h
|
|
@@ -487,7 +487,7 @@ struct proxy {
|
|
* name is used
|
|
*/
|
|
struct list filter_configs; /* list of the filters that are declared on this proxy */
|
|
- __decl_hathreads(HA_SPINLOCK_T lock);
|
|
+ __decl_hathreads(HA_SPINLOCK_T lock); /* may be taken under the server's lock */
|
|
};
|
|
|
|
struct switching_rule {
|
|
diff --git a/include/types/server.h b/include/types/server.h
|
|
index 4a077268..e0534162 100644
|
|
--- a/include/types/server.h
|
|
+++ b/include/types/server.h
|
|
@@ -319,7 +319,7 @@ struct server {
|
|
} ssl_ctx;
|
|
#endif
|
|
struct dns_srvrq *srvrq; /* Pointer representing the DNS SRV requeest, if any */
|
|
- __decl_hathreads(HA_SPINLOCK_T lock);
|
|
+ __decl_hathreads(HA_SPINLOCK_T lock); /* may enclose the proxy's lock, must not be taken under */
|
|
struct {
|
|
const char *file; /* file where the section appears */
|
|
struct eb32_node id; /* place in the tree of used IDs */
|
|
diff --git a/src/proxy.c b/src/proxy.c
|
|
index ae761ead..a537e0b1 100644
|
|
--- a/src/proxy.c
|
|
+++ b/src/proxy.c
|
|
@@ -1940,9 +1940,12 @@ static int cli_parse_enable_dyncookie_backend(char **args, char *payload, struct
|
|
if (!px)
|
|
return 1;
|
|
|
|
+ /* Note: this lock is to make sure this doesn't change while another
|
|
+ * thread is in srv_set_dyncookie().
|
|
+ */
|
|
HA_SPIN_LOCK(PROXY_LOCK, &px->lock);
|
|
-
|
|
px->ck_opts |= PR_CK_DYNAMIC;
|
|
+ HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
|
|
|
|
for (s = px->srv; s != NULL; s = s->next) {
|
|
HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
|
|
@@ -1950,8 +1953,6 @@ static int cli_parse_enable_dyncookie_backend(char **args, char *payload, struct
|
|
HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
|
|
}
|
|
|
|
- HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
|
|
-
|
|
return 1;
|
|
}
|
|
|
|
@@ -1971,9 +1972,12 @@ static int cli_parse_disable_dyncookie_backend(char **args, char *payload, struc
|
|
if (!px)
|
|
return 1;
|
|
|
|
+ /* Note: this lock is to make sure this doesn't change while another
|
|
+ * thread is in srv_set_dyncookie().
|
|
+ */
|
|
HA_SPIN_LOCK(PROXY_LOCK, &px->lock);
|
|
-
|
|
px->ck_opts &= ~PR_CK_DYNAMIC;
|
|
+ HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
|
|
|
|
for (s = px->srv; s != NULL; s = s->next) {
|
|
HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
|
|
@@ -1984,8 +1988,6 @@ static int cli_parse_disable_dyncookie_backend(char **args, char *payload, struc
|
|
HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
|
|
}
|
|
|
|
- HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
|
|
-
|
|
return 1;
|
|
}
|
|
|
|
@@ -2021,10 +2023,13 @@ static int cli_parse_set_dyncookie_key_backend(char **args, char *payload, struc
|
|
return 1;
|
|
}
|
|
|
|
+ /* Note: this lock is to make sure this doesn't change while another
|
|
+ * thread is in srv_set_dyncookie().
|
|
+ */
|
|
HA_SPIN_LOCK(PROXY_LOCK, &px->lock);
|
|
-
|
|
free(px->dyncookie_key);
|
|
px->dyncookie_key = newkey;
|
|
+ HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
|
|
|
|
for (s = px->srv; s != NULL; s = s->next) {
|
|
HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
|
|
@@ -2032,8 +2037,6 @@ static int cli_parse_set_dyncookie_key_backend(char **args, char *payload, struc
|
|
HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
|
|
}
|
|
|
|
- HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
|
|
-
|
|
return 1;
|
|
}
|
|
|
|
diff --git a/src/queue.c b/src/queue.c
|
|
index f4a94530..6aa54170 100644
|
|
--- a/src/queue.c
|
|
+++ b/src/queue.c
|
|
@@ -312,16 +312,16 @@ void process_srv_queue(struct server *s)
|
|
struct proxy *p = s->proxy;
|
|
int maxconn;
|
|
|
|
- HA_SPIN_LOCK(PROXY_LOCK, &p->lock);
|
|
HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
|
|
+ HA_SPIN_LOCK(PROXY_LOCK, &p->lock);
|
|
maxconn = srv_dynamic_maxconn(s);
|
|
while (s->served < maxconn) {
|
|
int ret = pendconn_process_next_strm(s, p);
|
|
if (!ret)
|
|
break;
|
|
}
|
|
- HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
|
|
HA_SPIN_UNLOCK(PROXY_LOCK, &p->lock);
|
|
+ HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
|
|
}
|
|
|
|
/* Adds the stream <strm> to the pending connection queue of server <strm>->srv
|
|
@@ -424,7 +424,8 @@ int pendconn_redistribute(struct server *s)
|
|
/* Check for pending connections at the backend, and assign some of them to
|
|
* the server coming up. The server's weight is checked before being assigned
|
|
* connections it may not be able to handle. The total number of transferred
|
|
- * connections is returned.
|
|
+ * connections is returned. It must be called with the server lock held, and
|
|
+ * will take the proxy's lock.
|
|
*/
|
|
int pendconn_grab_from_px(struct server *s)
|
|
{
|
|
diff --git a/src/server.c b/src/server.c
|
|
index a96f1ef6..236d6bae 100644
|
|
--- a/src/server.c
|
|
+++ b/src/server.c
|
|
@@ -125,7 +125,7 @@ static inline void srv_check_for_dup_dyncookie(struct server *s)
|
|
}
|
|
|
|
/*
|
|
- * Must be called with the server lock held.
|
|
+ * Must be called with the server lock held, and will grab the proxy lock.
|
|
*/
|
|
void srv_set_dyncookie(struct server *s)
|
|
{
|
|
@@ -137,15 +137,17 @@ void srv_set_dyncookie(struct server *s)
|
|
int addr_len;
|
|
int port;
|
|
|
|
+ HA_SPIN_LOCK(PROXY_LOCK, &p->lock);
|
|
+
|
|
if ((s->flags & SRV_F_COOKIESET) ||
|
|
!(s->proxy->ck_opts & PR_CK_DYNAMIC) ||
|
|
s->proxy->dyncookie_key == NULL)
|
|
- return;
|
|
+ goto out;
|
|
key_len = strlen(p->dyncookie_key);
|
|
|
|
if (s->addr.ss_family != AF_INET &&
|
|
s->addr.ss_family != AF_INET6)
|
|
- return;
|
|
+ goto out;
|
|
/*
|
|
* Buffer to calculate the cookie value.
|
|
* The buffer contains the secret key + the server IP address
|
|
@@ -174,7 +176,7 @@ void srv_set_dyncookie(struct server *s)
|
|
hash_value = XXH64(tmpbuf, buffer_len, 0);
|
|
memprintf(&s->cookie, "%016llx", hash_value);
|
|
if (!s->cookie)
|
|
- return;
|
|
+ goto out;
|
|
s->cklen = 16;
|
|
|
|
/* Don't bother checking if the dyncookie is duplicated if
|
|
@@ -183,6 +185,8 @@ void srv_set_dyncookie(struct server *s)
|
|
*/
|
|
if (!(s->next_admin & SRV_ADMF_FMAINT))
|
|
srv_check_for_dup_dyncookie(s);
|
|
+ out:
|
|
+ HA_SPIN_UNLOCK(PROXY_LOCK, &p->lock);
|
|
}
|
|
|
|
/*
|