Skip to content

Commit 99d6c88

Browse files
committed
BUG/MAJOR: leastconn: never reuse the node after dropping the lock
On ARM with 80 cores and a single server, it's sometimes possible to see a segfault in fwlc_get_next_server() around 600-700k RPS. It seldom happens as well on x86 with 128 threads with the same config around 1M rps. It turns out that in fwlc_get_next_server(), before calling fwlc_srv_reposition(), we have to drop the lock and that one takes it back again. The problem is that anything can happen to our node during this time, and it can be freed. Then when continuing our work, we later iterate over it and its next to find a node with an acceptable key, and by doing so we can visit either uninitialized memory or simply nodes that are no longer in the tree. A first attempt at fixing this consisted in artificially incrementing the elements count before dropping the lock, but that turned out to be even worse because other threads could loop forever on such an element looking for an entry that does not exist. Maintaining a separate refcount didn't work well either, and it required to deal with the memory release while dropping it, which is really not convenient. Here we're taking a different approach consisting in simply not trusting this node anymore and going back to the beginning of the loop, as is done at a few other places as well. This way we can safely ignore the possibly released node, and the test runs reliably both on the arm and the x86 platforms mentioned above. No performance regression was observed either, likely because this operation is quite rare. No backport is needed since this appeared with the leastconn rework in 3.2.
1 parent d358da4 commit 99d6c88

File tree

1 file changed

+5
-1
lines changed

1 file changed

+5
-1
lines changed

src/lb_fwlc.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -786,12 +786,16 @@ struct server *fwlc_get_next_server(struct proxy *p, struct server *srvtoavoid)
786786
* The server has more requests than expected,
787787
* let's try to reposition it, to avoid too
788788
* many threads using the same server at the
789-
* same time.
789+
* same time. From the moment we release the
790+
* lock, we cannot trust the node nor tree_elt
791+
* anymore, so we need to loop back to the
792+
* beginning.
790793
*/
791794
if (i >= FWLC_LISTS_NB) {
792795
HA_RWLOCK_RDUNLOCK(LBPRM_LOCK, &p->lbprm.lock);
793796
fwlc_srv_reposition(s);
794797
HA_RWLOCK_RDLOCK(LBPRM_LOCK, &p->lbprm.lock);
798+
goto redo;
795799
}
796800
i++;
797801
continue;

0 commit comments

Comments
 (0)