From: Willy Tarreau Date: Wed, 7 Oct 2020 13:58:50 +0000 (+0200) Subject: MINOR: listeners: add a new stop_listener() function X-Git-Tag: v2.3-dev6~21 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=caa7df1296b71f891f166ae9b3dbfcc9b0886aae;p=thirdparty%2Fhaproxy.git MINOR: listeners: add a new stop_listener() function This function will be used to definitely stop a listener (e.g. during a soft_stop). This is actually tricky because it may be called for a proxy or for a protocol, both of which require locks and already hold some. The function takes booleans indicating which ones are already held, hoping this will be enough. It's not well defined wether proto->disable() and proto->rx_disable() are supposed to be called with any lock held, and they are used from do_unbind_listener() with all these locks. Some back annotations ought to be added on this point. The proxy's listeners count is updated, and the proxy is marked as disabled and woken up after the last one is gone. Note that a listener in listen state is already not attached anymore since it was disabled. --- diff --git a/include/haproxy/listener.h b/include/haproxy/listener.h index 857ecb76a5..bb6f38f9f4 100644 --- a/include/haproxy/listener.h +++ b/include/haproxy/listener.h @@ -48,6 +48,15 @@ int pause_listener(struct listener *l); */ int resume_listener(struct listener *l); +/* + * This function completely stops a listener. It will need to operate under the + * proxy's lock, the protocol's lock, and the listener's lock. The caller is + * responsible for indicating in lpx, lpr, lli whether the respective locks are + * already held (non-zero) or not (zero) so that the function picks the missing + * ones, in this order. + */ +void stop_listener(struct listener *l, int lpx, int lpr, int lli); + /* 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 * LI_FULL state depending on its number of connections. In daemon mode, we diff --git a/src/listener.c b/src/listener.c index e44c82cdd3..e8f2a95b6d 100644 --- a/src/listener.c +++ b/src/listener.c @@ -309,6 +309,68 @@ void enable_listener(struct listener *listener) HA_SPIN_UNLOCK(LISTENER_LOCK, &listener->lock); } +/* + * This function completely stops a listener. It will need to operate under the + * proxy's lock, the protocol's lock, and the listener's lock. The caller is + * responsible for indicating in lpx, lpr, lli whether the respective locks are + * already held (non-zero) or not (zero) so that the function picks the missing + * ones, in this order. The proxy's listeners count is updated and the proxy is + * disabled and woken up after the last one is gone. + */ +void stop_listener(struct listener *l, int lpx, int lpr, int lli) +{ + struct proxy *px = l->bind_conf->frontend; + int must_close; + + if (l->options & LI_O_NOSTOP) { + /* master-worker sockpairs are never closed but don't count as a + * job. + */ + return; + } + + /* There are several cases where we must not close an FD: + * - we're starting up and we have socket transfers enabled; + * - we're the master and this FD was inherited; + */ + if ((global.tune.options & GTUNE_SOCKET_TRANSFER && global.mode & MODE_STARTING) || + (master && (l->rx.flags & RX_F_INHERITED))) + must_close = 0; + else + must_close = 1; + + if (!lpx) + HA_SPIN_LOCK(PROXY_LOCK, &px->lock); + + if (!lpr) + HA_SPIN_LOCK(PROTO_LOCK, &proto_lock); + + if (!lli) + HA_SPIN_LOCK(LISTENER_LOCK, &l->lock); + + if (l->state > LI_INIT) { + do_unbind_listener(l, must_close); + + if (l->state >= LI_ASSIGNED) + __delete_listener(l); + + if (px->li_ready + px->li_paused == 0) { + px->disabled = 1; + if (px->task) + task_wakeup(px->task, TASK_WOKEN_MSG); + } + } + + if (!lli) + HA_SPIN_UNLOCK(LISTENER_LOCK, &l->lock); + + if (!lpr) + HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock); + + if (!lpx) + HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock); +} + /* This function tries to temporarily disable a listener, depending on the OS * capabilities. Linux unbinds the listen socket after a SHUT_RD, and ignores * SHUT_WR. Solaris refuses either shutdown(). OpenBSD ignores SHUT_RD but