mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-21 22:01:31 +02:00
There is a very difficult to reproduce race in the listener's accept code, which is much easier to reproduce once connection limits are properly enforced. It's an ABBA lock issue : - the following functions take l->lock then lq_lock : disable_listener, pause_listener, listener_full, limit_listener, do_unbind_listener - the following ones take lq_lock then l->lock : resume_listener, dequeue_all_listener This is because __resume_listener() only takes the listener's lock and expects to be called with lq_lock held. The problem can easily happen when listener_full() and limit_listener() are called a lot while in parallel another thread releases sessions for the same listener using listener_release() which in turn calls resume_listener(). This scenario is more prevalent in 2.0-dev since the removal of the accept lock in listener_accept(). However in 1.9 and before, a different but extremely unlikely scenario can happen : thread1 thread2 ............................ enter listener_accept() limit_listener() ............................ long pause before taking the lock session_free() dequeue_all_listeners() lock(lq_lock) [1] ............................ try_lock(l->lock) [2] __resume_listener() spin_lock(l->lock) =>WAIT[2] ............................ accept() l->accept() nbconn==maxconn => listener_full() state==LI_LIMITED => lock(lq_lock) =>DEADLOCK[1]! In practice it is almost impossible to trigger it because it requires to limit both on the listener's maxconn and the frontend's rate limit, at the same time, and to release the listener when the connection rate goes below the limit between poll() returns the FD and the lock is taken (a few nanoseconds). But maybe with threads competing on the same core it has more chances to appear. This patch removes the lq_lock and replaces it with a lockless queue for the listener's wait queue (well, technically speaking a self-locked queue) brought by commit a8434ec14 ("MINOR: lists: Implement locked variations.") and its few subsequent fixes. This relieves us from the need of the lq_lock and removes the deadlock. It also gets rid of the distinction between __resume_listener() and resume_listener() since the only difference was the lq_lock. All listener removals from the list are now unconditional to avoid races on the state. It's worth noting that the list used to never be initialized and that it used to work only thanks to the state tests, so the initialization has now been added. This patch must carefully be backported to 1.9 and very likely 1.8. It is mandatory to be careful about replacing all manipulations of l->wait_queue, global.listener_queue and p->listener_queue.
The HAProxy documentation has been split into a number of different files for ease of use. Please refer to the following files depending on what you're looking for : - INSTALL for instructions on how to build and install HAProxy - LICENSE for the project's license - CONTRIBUTING for the process to follow to submit contributions The more detailed documentation is located into the doc/ directory : - doc/intro.txt for a quick introduction on HAProxy - doc/configuration.txt for the configuration's reference manual - doc/lua.txt for the Lua's reference manual - doc/SPOE.txt for how to use the SPOE engine - doc/network-namespaces.txt for how to use network namespaces under Linux - doc/management.txt for the management guide - doc/regression-testing.txt for how to use the regression testing suite - doc/peers.txt for the peers protocol reference - doc/coding-style.txt for how to adopt HAProxy's coding style - doc/internals for developer-specific documentation (not all up to date)
Description
Languages
C
98.1%
Shell
0.8%
Makefile
0.5%
Lua
0.2%
Python
0.2%