mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-07 15:47:01 +02:00
BUG/MEDIUM: threads/unix: Fix a deadlock when a listener is temporarily disabled
When a listener is temporarily disabled, we start by locking it and then we call .pause callback of the underlying protocol (tcp/unix). For TCP listeners, this is not a problem. But listeners bound on an unix socket are in fact closed instead. So .pause callback relies on unbind_listener function to do its job. Unfortunatly, unbind_listener hold the listener's lock and then call an internal function to unbind it. So, there is a deadlock here. This happens during a reload. To fix the problemn, the function do_unbind_listener, which is lockless, is now exported and is called when a listener bound on an unix socket is temporarily disabled. This patch must be backported in 1.8.
This commit is contained in:
parent
4288c5a9d8
commit
510c0d67ef
@ -60,6 +60,11 @@ int disable_all_listeners(struct protocol *proto);
|
|||||||
/* Dequeues all of the listeners waiting for a resource in wait queue <queue>. */
|
/* Dequeues all of the listeners waiting for a resource in wait queue <queue>. */
|
||||||
void dequeue_all_listeners(struct list *list);
|
void dequeue_all_listeners(struct list *list);
|
||||||
|
|
||||||
|
/* Must be called with the lock held. Depending on <do_close> value, it does
|
||||||
|
* what unbind_listener or unbind_listener_no_close should do.
|
||||||
|
*/
|
||||||
|
void do_unbind_listener(struct listener *listener, int do_close);
|
||||||
|
|
||||||
/* This function closes the listening socket for the specified listener,
|
/* This function closes the listening socket for the specified listener,
|
||||||
* provided that it's already in a listening state. The listener enters the
|
* provided that it's already in a listening state. The listener enters the
|
||||||
* LI_ASSIGNED state. This function is intended to be used as a generic
|
* LI_ASSIGNED state. This function is intended to be used as a generic
|
||||||
|
@ -49,8 +49,6 @@ static struct bind_kw_list bind_keywords = {
|
|||||||
|
|
||||||
struct xfer_sock_list *xfer_sock_list = NULL;
|
struct xfer_sock_list *xfer_sock_list = NULL;
|
||||||
|
|
||||||
static void __do_unbind_listener(struct listener *listener, int do_close);
|
|
||||||
|
|
||||||
/* This function adds the specified listener's file descriptor to the polling
|
/* This function adds the specified listener's file descriptor to the polling
|
||||||
* lists if it is in the LI_LISTEN state. The listener enters LI_READY or
|
* lists if it is in the LI_LISTEN state. The listener enters LI_READY or
|
||||||
* LI_FULL state depending on its number of connections. In deamon mode, we
|
* LI_FULL state depending on its number of connections. In deamon mode, we
|
||||||
@ -68,9 +66,9 @@ static void enable_listener(struct listener *listener)
|
|||||||
* want any fd event to reach it.
|
* want any fd event to reach it.
|
||||||
*/
|
*/
|
||||||
if (!(global.tune.options & GTUNE_SOCKET_TRANSFER))
|
if (!(global.tune.options & GTUNE_SOCKET_TRANSFER))
|
||||||
__do_unbind_listener(listener, 1);
|
do_unbind_listener(listener, 1);
|
||||||
else {
|
else {
|
||||||
__do_unbind_listener(listener, 0);
|
do_unbind_listener(listener, 0);
|
||||||
listener->state = LI_LISTEN;
|
listener->state = LI_LISTEN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -308,8 +306,10 @@ void dequeue_all_listeners(struct list *list)
|
|||||||
HA_SPIN_UNLOCK(LISTENER_QUEUE_LOCK, &lq_lock);
|
HA_SPIN_UNLOCK(LISTENER_QUEUE_LOCK, &lq_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* must be called with the lock held */
|
/* Must be called with the lock held. Depending on <do_close> value, it does
|
||||||
static void __do_unbind_listener(struct listener *listener, int do_close)
|
* what unbind_listener or unbind_listener_no_close should do.
|
||||||
|
*/
|
||||||
|
void do_unbind_listener(struct listener *listener, int do_close)
|
||||||
{
|
{
|
||||||
if (listener->state == LI_READY)
|
if (listener->state == LI_READY)
|
||||||
fd_stop_recv(listener->fd);
|
fd_stop_recv(listener->fd);
|
||||||
@ -331,13 +331,6 @@ static void __do_unbind_listener(struct listener *listener, int do_close)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_unbind_listener(struct listener *listener, int do_close)
|
|
||||||
{
|
|
||||||
HA_SPIN_LOCK(LISTENER_LOCK, &listener->lock);
|
|
||||||
__do_unbind_listener(listener, do_close);
|
|
||||||
HA_SPIN_UNLOCK(LISTENER_LOCK, &listener->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function closes the listening socket for the specified listener,
|
/* This function closes the listening socket for the specified listener,
|
||||||
* provided that it's already in a listening state. The listener enters the
|
* provided that it's already in a listening state. The listener enters the
|
||||||
* LI_ASSIGNED state. This function is intended to be used as a generic
|
* LI_ASSIGNED state. This function is intended to be used as a generic
|
||||||
@ -345,7 +338,9 @@ static void do_unbind_listener(struct listener *listener, int do_close)
|
|||||||
*/
|
*/
|
||||||
void unbind_listener(struct listener *listener)
|
void unbind_listener(struct listener *listener)
|
||||||
{
|
{
|
||||||
|
HA_SPIN_LOCK(LISTENER_LOCK, &listener->lock);
|
||||||
do_unbind_listener(listener, 1);
|
do_unbind_listener(listener, 1);
|
||||||
|
HA_SPIN_UNLOCK(LISTENER_LOCK, &listener->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function pretends the listener is dead, but keeps the FD opened, so
|
/* This function pretends the listener is dead, but keeps the FD opened, so
|
||||||
@ -353,7 +348,9 @@ void unbind_listener(struct listener *listener)
|
|||||||
*/
|
*/
|
||||||
void unbind_listener_no_close(struct listener *listener)
|
void unbind_listener_no_close(struct listener *listener)
|
||||||
{
|
{
|
||||||
|
HA_SPIN_LOCK(LISTENER_LOCK, &listener->lock);
|
||||||
do_unbind_listener(listener, 0);
|
do_unbind_listener(listener, 0);
|
||||||
|
HA_SPIN_UNLOCK(LISTENER_LOCK, &listener->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function closes all listening sockets bound to the protocol <proto>,
|
/* This function closes all listening sockets bound to the protocol <proto>,
|
||||||
|
@ -393,7 +393,9 @@ static int uxst_pause_listener(struct listener *l)
|
|||||||
if (((struct sockaddr_un *)&l->addr)->sun_path[0])
|
if (((struct sockaddr_un *)&l->addr)->sun_path[0])
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
unbind_listener(l);
|
/* Listener's lock already held. Call lockless version of
|
||||||
|
* unbind_listener. */
|
||||||
|
do_unbind_listener(l, 1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user