]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: pipes: Have one pool of free pipes per thread group
authorOlivier Houchard <ohouchard@haproxy.com>
Tue, 28 Jul 2026 11:37:12 +0000 (13:37 +0200)
committerOlivier Houchard <cognet@ci0.org>
Thu, 30 Jul 2026 11:35:22 +0000 (13:35 +0200)
The pipes used for splicing are kept in a shared pool of free pipes, so
that they can be reused by any thread. However, with per-thread-group
FD tables, a pipe created by one group only exists in that group's
kernel table, so its FDs must never be handed over to a thread of
another group.
To fix that, replace the global list of free pipes with one list per
thread group if the fd tables are not shared across thread groups.

src/pipe.c

index 794b881ec644873d5e69c7caff9047eac74c9ac1..2a1dd92f7cc39b84167ee9ecc057f8d571e69074 100644 (file)
 #include <haproxy/pipe-t.h>
 #include <haproxy/pool.h>
 #include <haproxy/thread.h>
+#include <haproxy/tinfo.h>
 
 
 DECLARE_STATIC_TYPED_POOL(pool_head_pipe, "pipe", struct pipe);
 
-struct pipe *pipes_live = NULL; /* pipes which are still ready to use */
-
-__decl_spinlock(pipes_lock); /* lock used to protect pipes list */
+/* The pools of pipes which are still ready to use, one per thread group:
+ * with per-thread-group FD tables, a pipe's FDs are only valid within the
+ * kernel table of the group that created it, so free pipes must never
+ * travel across groups. With shared FD tables any pipe is usable by any
+ * thread, so only the first pool is used then. The spinlocks rely on
+ * being zero-initialized.
+ */
+static struct {
+       struct pipe *live;        /* pipes which are still ready to use */
+       __decl_thread(HA_SPINLOCK_T lock); /* lock used to protect the list */
+} pipe_pools[MAX_TGROUPS] __attribute__((aligned(64)));
 
 static THREAD_LOCAL int local_pipes_free = 0;  /* #cache objects   */
 static THREAD_LOCAL struct pipe *local_pipes = NULL;
@@ -39,6 +48,7 @@ struct pipe *get_pipe()
 {
        struct pipe *ret = NULL;
        int pipefd[2];
+       int grp;
 
        ret = local_pipes;
        if (likely(ret)) {
@@ -49,12 +59,14 @@ struct pipe *get_pipe()
                goto out;
        }
 
-       if (likely(pipes_live)) {
-               HA_SPIN_LOCK(PIPES_LOCK, &pipes_lock);
-               ret = pipes_live;
+       grp = (global.tune.options & GTUNE_NO_TG_FD_SHARING) ? tgid - 1 : 0;
+
+       if (likely(pipe_pools[grp].live)) {
+               HA_SPIN_LOCK(PIPES_LOCK, &pipe_pools[grp].lock);
+               ret = pipe_pools[grp].live;
                if (likely(ret))
-                       pipes_live = ret->next;
-               HA_SPIN_UNLOCK(PIPES_LOCK, &pipes_lock);
+                       pipe_pools[grp].live = ret->next;
+               HA_SPIN_UNLOCK(PIPES_LOCK, &pipe_pools[grp].lock);
                if (ret) {
                        HA_ATOMIC_DEC(&pipes_free);
                        HA_ATOMIC_INC(&pipes_used);
@@ -107,6 +119,8 @@ void kill_pipe(struct pipe *p)
  */
 void put_pipe(struct pipe *p)
 {
+       int grp;
+
        if (unlikely(p->data)) {
                kill_pipe(p);
                return;
@@ -119,10 +133,12 @@ void put_pipe(struct pipe *p)
                goto out;
        }
 
-       HA_SPIN_LOCK(PIPES_LOCK, &pipes_lock);
-       p->next = pipes_live;
-       pipes_live = p;
-       HA_SPIN_UNLOCK(PIPES_LOCK, &pipes_lock);
+       grp = (global.tune.options & GTUNE_NO_TG_FD_SHARING) ? tgid - 1 : 0;
+
+       HA_SPIN_LOCK(PIPES_LOCK, &pipe_pools[grp].lock);
+       p->next = pipe_pools[grp].live;
+       pipe_pools[grp].live = p;
+       HA_SPIN_UNLOCK(PIPES_LOCK, &pipe_pools[grp].lock);
  out:
        HA_ATOMIC_INC(&pipes_free);
        HA_ATOMIC_DEC(&pipes_used);