From e03204c8e1d6111fbbdef105e2cafe9b0a772881 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 9 Oct 2020 17:02:21 +0200 Subject: [PATCH] MEDIUM: listeners: implement protocol level ->suspend/resume() calls Now we have ->suspend() and ->resume() for listeners at the protocol level. This means that it now becomes possible for a protocol to redefine its own way to suspend and resume. The default functions are provided for TCP, UDP and unix, and they are pass-through to the receiver equivalent as it used to be till now. Nothing was defined for sockpair since it does not need to suspend/resume during reloads, hence it will succeed. --- include/haproxy/listener.h | 19 +++++++ include/haproxy/protocol-t.h | 2 + src/listener.c | 101 ++++++++++++++++++++++------------- src/proto_tcp.c | 4 ++ src/proto_udp.c | 4 ++ src/proto_uxst.c | 1 + 6 files changed, 93 insertions(+), 38 deletions(-) diff --git a/include/haproxy/listener.h b/include/haproxy/listener.h index 399e2669a3..24a126c2fc 100644 --- a/include/haproxy/listener.h +++ b/include/haproxy/listener.h @@ -130,6 +130,25 @@ void listener_release(struct listener *l); * still bound. This must be used under the listener's lock. */ void default_unbind_listener(struct listener *listener); + +/* default function called to suspend a listener: it simply passes the call to + * the underlying receiver. This is find for most socket-based protocols. This + * must be called under the listener's lock. It will return non-zero on success, + * 0 on failure. If no receiver-level suspend is provided, the operation is + * assumed to succeed. + */ +int default_suspend_listener(struct listener *l); + +/* Tries to resume a suspended listener, and returns non-zero on success or + * zero on failure. On certain errors, an alert or a warning might be displayed. + * It must be called with the listener's lock held. Depending on the listener's + * state and protocol, a listen() call might be used to resume operations, or a + * call to the receiver's resume() function might be used as well. This is + * suitable as a default function for TCP and UDP. This must be called with the + * listener's lock held. + */ +int default_resume_listener(struct listener *l); + /* * Registers the bind keyword list as a list of valid keywords for next * parsing sessions. diff --git a/include/haproxy/protocol-t.h b/include/haproxy/protocol-t.h index a7d2429899..2dea7326ea 100644 --- a/include/haproxy/protocol-t.h +++ b/include/haproxy/protocol-t.h @@ -92,6 +92,8 @@ struct protocol { void (*enable)(struct listener *l); /* enable receipt of new connections */ void (*disable)(struct listener *l); /* disable receipt of new connections */ void (*unbind)(struct listener *l); /* unbind the listener and possibly its receiver */ + int (*suspend)(struct listener *l); /* try to suspend the listener */ + int (*resume)(struct listener *l); /* try to resume a suspended listener */ /* functions acting on the receiver */ void (*rx_enable)(struct receiver *rx); /* enable receiving on the receiver */ diff --git a/src/listener.c b/src/listener.c index 3e305c73a9..1acf85f60c 100644 --- a/src/listener.c +++ b/src/listener.c @@ -358,6 +358,65 @@ void stop_listener(struct listener *l, int lpx, int lpr, int lli) HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock); } +/* default function called to suspend a listener: it simply passes the call to + * the underlying receiver. This is find for most socket-based protocols. This + * must be called under the listener's lock. It will return non-zero on success, + * 0 on failure. If no receiver-level suspend is provided, the operation is + * assumed to succeed. + */ +int default_suspend_listener(struct listener *l) +{ + int ret = 1; + + if (!l->rx.proto->rx_suspend) + return 1; + + ret = l->rx.proto->rx_suspend(&l->rx); + return ret > 0 ? ret : 0; +} + + +/* Tries to resume a suspended listener, and returns non-zero on success or + * zero on failure. On certain errors, an alert or a warning might be displayed. + * It must be called with the listener's lock held. Depending on the listener's + * state and protocol, a listen() call might be used to resume operations, or a + * call to the receiver's resume() function might be used as well. This is + * suitable as a default function for TCP and UDP. This must be called with the + * listener's lock held. + */ +int default_resume_listener(struct listener *l) +{ + int ret = 1; + + if (l->state == LI_ASSIGNED) { + char msg[100]; + int err; + + err = l->rx.proto->listen(l, msg, sizeof(msg)); + if (err & ERR_ALERT) + ha_alert("Resuming listener: %s\n", msg); + else if (err & ERR_WARN) + ha_warning("Resuming listener: %s\n", msg); + + if (err & (ERR_FATAL | ERR_ABORT)) { + ret = 0; + goto end; + } + } + + if (l->state < LI_PAUSED) { + ret = 0; + goto end; + } + + if (l->state == LI_PAUSED && l->rx.proto->rx_resume && + l->rx.proto->rx_resume(&l->rx) <= 0) + ret = 0; + end: + return ret; +} + + /* 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 @@ -379,18 +438,8 @@ int pause_listener(struct listener *l) if (l->state <= LI_PAUSED) goto end; - if (l->rx.proto->rx_suspend) { - /* Returns < 0 in case of failure, 0 if the listener - * was totally stopped, or > 0 if correctly paused. - */ - ret = l->rx.proto->rx_suspend(&l->rx); - - if (ret < 0) { - ret = 0; - goto end; - } - ret = 1; - } + if (l->rx.proto->suspend) + ret = l->rx.proto->suspend(l); MT_LIST_DEL(&l->wait_queue); @@ -436,32 +485,8 @@ int resume_listener(struct listener *l) if (l->state == LI_READY) goto end; - if (l->state == LI_ASSIGNED) { - char msg[100]; - int err; - - err = l->rx.proto->listen(l, msg, sizeof(msg)); - if (err & ERR_ALERT) - ha_alert("Resuming listener: %s\n", msg); - else if (err & ERR_WARN) - ha_warning("Resuming listener: %s\n", msg); - - if (err & (ERR_FATAL | ERR_ABORT)) { - ret = 0; - goto end; - } - } - - if (l->state < LI_PAUSED) { - ret = 0; - goto end; - } - - if (l->state == LI_PAUSED && l->rx.proto->rx_resume && - l->rx.proto->rx_resume(&l->rx) <= 0) { - ret = 0; - goto end; - } + if (l->rx.proto->resume) + ret = l->rx.proto->resume(l); if (l->maxconn && l->nbconn >= l->maxconn) { l->rx.proto->disable(l); diff --git a/src/proto_tcp.c b/src/proto_tcp.c index 71d13c8b77..9e6a3d7226 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -65,6 +65,8 @@ static struct protocol proto_tcpv4 = { .enable = tcp_enable_listener, .disable = tcp_disable_listener, .unbind = default_unbind_listener, + .suspend = default_suspend_listener, + .resume = default_resume_listener, .rx_enable = sock_enable, .rx_disable = sock_disable, .rx_unbind = sock_unbind, @@ -91,6 +93,8 @@ static struct protocol proto_tcpv6 = { .enable = tcp_enable_listener, .disable = tcp_disable_listener, .unbind = default_unbind_listener, + .suspend = default_suspend_listener, + .resume = default_resume_listener, .rx_enable = sock_enable, .rx_disable = sock_disable, .rx_unbind = sock_unbind, diff --git a/src/proto_udp.c b/src/proto_udp.c index 1c2477e719..13545ad58a 100644 --- a/src/proto_udp.c +++ b/src/proto_udp.c @@ -61,6 +61,8 @@ static struct protocol proto_udp4 = { .enable = udp_enable_listener, .disable = udp_disable_listener, .unbind = default_unbind_listener, + .suspend = default_suspend_listener, + .resume = default_resume_listener, .rx_enable = sock_enable, .rx_disable = sock_disable, .rx_unbind = sock_unbind, @@ -85,6 +87,8 @@ static struct protocol proto_udp6 = { .enable = udp_enable_listener, .disable = udp_disable_listener, .unbind = default_unbind_listener, + .suspend = default_suspend_listener, + .resume = default_resume_listener, .rx_enable = sock_enable, .rx_disable = sock_disable, .rx_unbind = sock_unbind, diff --git a/src/proto_uxst.c b/src/proto_uxst.c index 04f12faa02..047b75954c 100644 --- a/src/proto_uxst.c +++ b/src/proto_uxst.c @@ -60,6 +60,7 @@ static struct protocol proto_unix = { .enable = uxst_enable_listener, .disable = uxst_disable_listener, .unbind = default_unbind_listener, + .suspend = default_suspend_listener, .rx_enable = sock_enable, .rx_disable = sock_disable, .rx_unbind = sock_unbind, -- 2.39.5