summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilly Tarreau <w@1wt.eu>2020-10-07 15:58:50 +0200
committerWilly Tarreau <w@1wt.eu>2020-10-07 19:42:32 +0200
commita426bfd85edeb9cac81f86e4ecc640f36a13c91e (patch)
tree1c8bdd92c04ca7a72945294ccb3ae8cff6a11369
parentdd23543608a4e619827e2d2546dc909406e751e6 (diff)
downloadhaproxy-a426bfd85edeb9cac81f86e4ecc640f36a13c91e.tar.gz
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.
-rw-r--r--include/haproxy/listener.h9
-rw-r--r--src/listener.c63
2 files changed, 72 insertions, 0 deletions
diff --git a/include/haproxy/listener.h b/include/haproxy/listener.h
index 857ecb76a..bb6f38f9f 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 5638c17b7..7f51b8d64 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -309,6 +309,69 @@ 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.
+ */
+ HA_ATOMIC_ADD(&unstoppable_jobs, 1);
+ 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_bound + 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