]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: listeners: add a new stop_listener() function
authorWilly Tarreau <w@1wt.eu>
Wed, 7 Oct 2020 13:58:50 +0000 (15:58 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 9 Oct 2020 16:27:48 +0000 (18:27 +0200)
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.

include/haproxy/listener.h
src/listener.c

index 857ecb76a5181419da4006b29a1a516c07ea5e93..bb6f38f9f48716156a10d092df6da2f98171ac65 100644 (file)
@@ -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
index e44c82cdd39182cf49fe24867220b1dd83e5d132..e8f2a95b6de31c0546687ae89a2a889d6a648c63 100644 (file)
@@ -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