]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: connection: extend takeover with release option
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 15 Mar 2024 14:36:33 +0000 (15:36 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 22 Mar 2024 15:12:36 +0000 (16:12 +0100)
Extend takeover API both for MUX and XPRT with a new boolean argument
<release>. Its purpose is to signal if the connection will be freed
immediately after the takeover, rendering new resources allocation
unnecessary.

For the moment, release argument is always false. However, it will be
set to true on delete server CLI handler to proactively close server
idle connections.

include/haproxy/connection-t.h
src/backend.c
src/mux_fcgi.c
src/mux_h1.c
src/mux_h2.c
src/ssl_sock.c

index 3c65013bfbc0fde9d11bcf6e0f35cfab64b19e90..00639dd6bc6d94229f458380905494ca88c12244 100644 (file)
@@ -393,7 +393,7 @@ struct xprt_ops {
        int  (*prepare_srv)(struct server *srv);    /* prepare a server context */
        void (*destroy_srv)(struct server *srv);    /* destroy a server context */
        int  (*get_alpn)(const struct connection *conn, void *xprt_ctx, const char **str, int *len); /* get application layer name */
-       int (*takeover)(struct connection *conn, void *xprt_ctx, int orig_tid); /* Let the xprt know the fd have been taken over */
+       int (*takeover)(struct connection *conn, void *xprt_ctx, int orig_tid, int release); /* Let the xprt know the fd have been taken over */
        void (*set_idle)(struct connection *conn, void *xprt_ctx); /* notify the xprt that the connection becomes idle. implies set_used. */
        void (*set_used)(struct connection *conn, void *xprt_ctx); /* notify the xprt that the connection leaves idle. implies set_idle. */
        char name[8];                               /* transport layer name, zero-terminated */
@@ -438,7 +438,12 @@ struct mux_ops {
        int (*used_streams)(struct connection *conn);  /* Returns the number of streams in use on a connection. */
        void (*destroy)(void *ctx); /* Let the mux know one of its users left, so it may have to disappear */
        int (*ctl)(struct connection *conn, enum mux_ctl_type mux_ctl, void *arg); /* Provides information about the mux connection */
-       int (*takeover)(struct connection *conn, int orig_tid); /* Attempts to migrate the connection to the current thread */
+
+       /* Attempts to migrate <conn> from <orig_tid> to the current thread. If
+        * <release> is true, it will be destroyed immediately after by caller.
+        */
+       int (*takeover)(struct connection *conn, int orig_tid, int release);
+
        unsigned int flags;                           /* some flags characterizing the mux's capabilities (MX_FL_*) */
        char name[8];                                 /* mux layer name, zero-terminated */
 };
index 7dd8ceb767289d77648cddcf6be13c0367ea925a..18df7ea0f40df919d2226bd8b67b217a1da2db63 100644 (file)
@@ -1232,7 +1232,7 @@ struct connection *conn_backend_get(struct stream *s, struct server *srv, int is
                        continue;
                conn = srv_lookup_conn(is_safe ? &srv->per_thr[i].safe_conns : &srv->per_thr[i].idle_conns, hash);
                while (conn) {
-                       if (conn->mux->takeover && conn->mux->takeover(conn, i) == 0) {
+                       if (conn->mux->takeover && conn->mux->takeover(conn, i, 0) == 0) {
                                conn_delete_from_tree(conn);
                                _HA_ATOMIC_INC(&activity[tid].fd_takeover);
                                found = 1;
@@ -1245,7 +1245,7 @@ struct connection *conn_backend_get(struct stream *s, struct server *srv, int is
                if (!found && !is_safe && srv->curr_safe_nb > 0) {
                        conn = srv_lookup_conn(&srv->per_thr[i].safe_conns, hash);
                        while (conn) {
-                               if (conn->mux->takeover && conn->mux->takeover(conn, i) == 0) {
+                               if (conn->mux->takeover && conn->mux->takeover(conn, i, 0) == 0) {
                                        conn_delete_from_tree(conn);
                                        _HA_ATOMIC_INC(&activity[tid].fd_takeover);
                                        found = 1;
index d192cf9d1fd4337ad4e6963dae634ec26f7ba5b7..ebd3032cdd12e3a9fe9c30b0ba058b0ead1c59e1 100644 (file)
@@ -4154,25 +4154,27 @@ static int fcgi_show_fd(struct buffer *msg, struct connection *conn)
  * Return 0 if successful, non-zero otherwise.
  * Expected to be called with the old thread lock held.
  */
-static int fcgi_takeover(struct connection *conn, int orig_tid)
+static int fcgi_takeover(struct connection *conn, int orig_tid, int release)
 {
        struct fcgi_conn *fcgi = conn->ctx;
        struct task *task;
-       struct task *new_task;
-       struct tasklet *new_tasklet;
+       struct task *new_task = NULL;
+       struct tasklet *new_tasklet = NULL;
 
        /* Pre-allocate tasks so that we don't have to roll back after the xprt
         * has been migrated.
         */
-       new_task = task_new_here();
-       new_tasklet = tasklet_new();
-       if (!new_task || !new_tasklet)
-               goto fail;
+       if (!release) {
+               new_task = task_new_here();
+               new_tasklet = tasklet_new();
+               if (!new_task || !new_tasklet)
+                       goto fail;
+       }
 
        if (fd_takeover(conn->handle.fd, conn) != 0)
                goto fail;
 
-       if (conn->xprt->takeover && conn->xprt->takeover(conn, conn->xprt_ctx, orig_tid) != 0) {
+       if (conn->xprt->takeover && conn->xprt->takeover(conn, conn->xprt_ctx, orig_tid, release) != 0) {
                /* We failed to takeover the xprt, even if the connection may
                 * still be valid, flag it as error'd, as we have already
                 * taken over the fd, and wake the tasklet, so that it will
@@ -4199,8 +4201,10 @@ static int fcgi_takeover(struct connection *conn, int orig_tid)
 
                fcgi->task = new_task;
                new_task = NULL;
-               fcgi->task->process = fcgi_timeout_task;
-               fcgi->task->context = fcgi;
+               if (!release) {
+                       fcgi->task->process = fcgi_timeout_task;
+                       fcgi->task->context = fcgi;
+               }
        }
 
        /* To let the tasklet know it should free itself, and do nothing else,
@@ -4210,10 +4214,12 @@ static int fcgi_takeover(struct connection *conn, int orig_tid)
        tasklet_wakeup_on(fcgi->wait_event.tasklet, orig_tid);
 
        fcgi->wait_event.tasklet = new_tasklet;
-       fcgi->wait_event.tasklet->process = fcgi_io_cb;
-       fcgi->wait_event.tasklet->context = fcgi;
-       fcgi->conn->xprt->subscribe(fcgi->conn, fcgi->conn->xprt_ctx,
-                                   SUB_RETRY_RECV, &fcgi->wait_event);
+       if (!release) {
+               fcgi->wait_event.tasklet->process = fcgi_io_cb;
+               fcgi->wait_event.tasklet->context = fcgi;
+               fcgi->conn->xprt->subscribe(fcgi->conn, fcgi->conn->xprt_ctx,
+                                           SUB_RETRY_RECV, &fcgi->wait_event);
+       }
 
        if (new_task)
                __task_free(new_task);
index 343c23e086eae50cbc6ef31e7f9bf1b07fbc8958..8e9ec78fd1b5241f1cfbb8220bccc43f9fd824a5 100644 (file)
@@ -5217,25 +5217,27 @@ static int add_hdr_case_adjust(const char *from, const char *to, char **err)
  * Return 0 if successful, non-zero otherwise.
  * Expected to be called with the old thread lock held.
  */
-static int h1_takeover(struct connection *conn, int orig_tid)
+static int h1_takeover(struct connection *conn, int orig_tid, int release)
 {
        struct h1c *h1c = conn->ctx;
        struct task *task;
-       struct task *new_task;
-       struct tasklet *new_tasklet;
+       struct task *new_task = NULL;
+       struct tasklet *new_tasklet = NULL;
 
        /* Pre-allocate tasks so that we don't have to roll back after the xprt
         * has been migrated.
         */
-       new_task = task_new_here();
-       new_tasklet = tasklet_new();
-       if (!new_task || !new_tasklet)
-               goto fail;
+       if (!release) {
+               new_task = task_new_here();
+               new_tasklet = tasklet_new();
+               if (!new_task || !new_tasklet)
+                       goto fail;
+       }
 
        if (fd_takeover(conn->handle.fd, conn) != 0)
                goto fail;
 
-       if (conn->xprt->takeover && conn->xprt->takeover(conn, conn->xprt_ctx, orig_tid) != 0) {
+       if (conn->xprt->takeover && conn->xprt->takeover(conn, conn->xprt_ctx, orig_tid, release) != 0) {
                /* We failed to takeover the xprt, even if the connection may
                 * still be valid, flag it as error'd, as we have already
                 * taken over the fd, and wake the tasklet, so that it will
@@ -5262,8 +5264,10 @@ static int h1_takeover(struct connection *conn, int orig_tid)
 
                h1c->task = new_task;
                new_task = NULL;
-               h1c->task->process = h1_timeout_task;
-               h1c->task->context = h1c;
+               if (!release) {
+                       h1c->task->process = h1_timeout_task;
+                       h1c->task->context = h1c;
+               }
        }
 
        /* To let the tasklet know it should free itself, and do nothing else,
@@ -5273,10 +5277,12 @@ static int h1_takeover(struct connection *conn, int orig_tid)
        tasklet_wakeup_on(h1c->wait_event.tasklet, orig_tid);
 
        h1c->wait_event.tasklet = new_tasklet;
-       h1c->wait_event.tasklet->process = h1_io_cb;
-       h1c->wait_event.tasklet->context = h1c;
-       h1c->conn->xprt->subscribe(h1c->conn, h1c->conn->xprt_ctx,
-                                  SUB_RETRY_RECV, &h1c->wait_event);
+       if (!release) {
+               h1c->wait_event.tasklet->process = h1_io_cb;
+               h1c->wait_event.tasklet->context = h1c;
+               h1c->conn->xprt->subscribe(h1c->conn, h1c->conn->xprt_ctx,
+                                          SUB_RETRY_RECV, &h1c->wait_event);
+       }
 
        if (new_task)
                __task_free(new_task);
index d72fe63442eae3203c143e4bcdb461d50b1219cf..e829c49478ffcd47964e3eadaf04d4875b7b460a 100644 (file)
@@ -7500,25 +7500,27 @@ static int h2_show_sd(struct buffer *msg, struct sedesc *sd, const char *pfx)
  * Return 0 if successful, non-zero otherwise.
  * Expected to be called with the old thread lock held.
  */
-static int h2_takeover(struct connection *conn, int orig_tid)
+static int h2_takeover(struct connection *conn, int orig_tid, int release)
 {
        struct h2c *h2c = conn->ctx;
        struct task *task;
-       struct task *new_task;
-       struct tasklet *new_tasklet;
+       struct task *new_task = NULL;
+       struct tasklet *new_tasklet = NULL;
 
        /* Pre-allocate tasks so that we don't have to roll back after the xprt
         * has been migrated.
         */
-       new_task = task_new_here();
-       new_tasklet = tasklet_new();
-       if (!new_task || !new_tasklet)
-               goto fail;
+       if (!release) {
+               new_task = task_new_here();
+               new_tasklet = tasklet_new();
+               if (!new_task || !new_tasklet)
+                       goto fail;
+       }
 
        if (fd_takeover(conn->handle.fd, conn) != 0)
                goto fail;
 
-       if (conn->xprt->takeover && conn->xprt->takeover(conn, conn->xprt_ctx, orig_tid) != 0) {
+       if (conn->xprt->takeover && conn->xprt->takeover(conn, conn->xprt_ctx, orig_tid, release) != 0) {
                /* We failed to takeover the xprt, even if the connection may
                 * still be valid, flag it as error'd, as we have already
                 * taken over the fd, and wake the tasklet, so that it will
@@ -7545,8 +7547,10 @@ static int h2_takeover(struct connection *conn, int orig_tid)
 
                h2c->task = new_task;
                new_task = NULL;
-               h2c->task->process = h2_timeout_task;
-               h2c->task->context = h2c;
+               if (!release) {
+                       h2c->task->process = h2_timeout_task;
+                       h2c->task->context = h2c;
+               }
        }
 
        /* To let the tasklet know it should free itself, and do nothing else,
@@ -7556,10 +7560,12 @@ static int h2_takeover(struct connection *conn, int orig_tid)
        tasklet_wakeup_on(h2c->wait_event.tasklet, orig_tid);
 
        h2c->wait_event.tasklet = new_tasklet;
-       h2c->wait_event.tasklet->process = h2_io_cb;
-       h2c->wait_event.tasklet->context = h2c;
-       h2c->conn->xprt->subscribe(h2c->conn, h2c->conn->xprt_ctx,
-                                  SUB_RETRY_RECV, &h2c->wait_event);
+       if (!release) {
+               h2c->wait_event.tasklet->process = h2_io_cb;
+               h2c->wait_event.tasklet->context = h2c;
+               h2c->conn->xprt->subscribe(h2c->conn, h2c->conn->xprt_ctx,
+                                          SUB_RETRY_RECV, &h2c->wait_event);
+       }
 
        if (new_task)
                __task_free(new_task);
index f1ee0836507927e89c0a3b0f0b8e1ca9c3e7147a..f5382492ee77ad1e32bd3c95c0be13ed0930c42f 100644 (file)
@@ -6151,19 +6151,26 @@ static int ssl_unsubscribe(struct connection *conn, void *xprt_ctx, int event_ty
  * It should be called with the takeover lock for the old thread held.
  * Returns 0 on success, and -1 on failure
  */
-static int ssl_takeover(struct connection *conn, void *xprt_ctx, int orig_tid)
+static int ssl_takeover(struct connection *conn, void *xprt_ctx, int orig_tid, int release)
 {
        struct ssl_sock_ctx *ctx = xprt_ctx;
-       struct tasklet *tl = tasklet_new();
+       struct tasklet *tl = NULL;
 
-       if (!tl)
-               return -1;
+       if (!release) {
+               tl = tasklet_new();
+               if (!tl)
+                       return -1;
+       }
 
        ctx->wait_event.tasklet->context = NULL;
        tasklet_wakeup_on(ctx->wait_event.tasklet, orig_tid);
+
        ctx->wait_event.tasklet = tl;
-       ctx->wait_event.tasklet->process = ssl_sock_io_cb;
-       ctx->wait_event.tasklet->context = ctx;
+       if (!release) {
+               ctx->wait_event.tasklet->process = ssl_sock_io_cb;
+               ctx->wait_event.tasklet->context = ctx;
+       }
+
        return 0;
 }