]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: fd: Make it possible to have one fdtab per thread-group
authorOlivier Houchard <ohouchard@haproxy.com>
Mon, 29 Jun 2026 12:32:19 +0000 (14:32 +0200)
committerOlivier Houchard <cognet@ci0.org>
Thu, 30 Jul 2026 11:35:22 +0000 (13:35 +0200)
Add a new global flag, GTUNE_NO_TG_FD_SHARING. When set, the goal is to
have to a different file descriptor table for each thread group, if
the platform supports separate file descriptor tables for each thread
(so right now, only linux with the unshare() syscall), as with many
threads, the kernel lock for the file descriptor table is a huge source
of contention.
This commit just makes it so if the flag is set, we will have one fdtab
per thread group, and we will call unshare() for each thread group, so
that each has its own file descriptor table.
Note that for now there is no way to set the flag, as a lot more work is
needed before the feature is usable. As such, this commit should have no
noticeable impact.

include/haproxy/fd.h
include/haproxy/global-t.h
include/haproxy/tinfo-t.h
src/fd.c
src/haproxy.c
src/thread.c

index 9d2ac0586d7d922580e80eeb815dacbb04be56ce..9307cfcf85532d8cb8211338ddd9cabfd2193ca4 100644 (file)
@@ -38,7 +38,7 @@
 extern struct poller cur_poller; /* the current poller */
 extern int nbpollers;
 extern struct poller pollers[MAX_POLLERS];   /* all registered pollers */
-extern struct fdtab *fdtab;             /* array of all the file descriptors */
+extern THREAD_LOCAL struct fdtab *fdtab;     /* array of all the file descriptors */
 extern int totalconn;                   /* total # of terminated sessions */
 extern int actconn;                     /* # of active sessions */
 
index 62144136c4814beda64536e382d9452427d6d289..b02462ddf8f46a51984eb5adb1d5ac725ed98108 100644 (file)
@@ -87,6 +87,7 @@
 #define GTUNE_LISTENER_MQ_ANY    (GTUNE_LISTENER_MQ_FAIR | GTUNE_LISTENER_MQ_OPT)
 #define GTUNE_NO_KTLS            (1<<29)
 #define GTUNE_NO_MAX_COUNTER     (1<<30)
+#define GTUNE_NO_TG_FD_SHARING   (1U<<31)
 
 /* subsystem-specific debugging options for tune.debug */
 #define GDBG_CPU_AFFINITY           (1U<< 0)
index e5ca44535a1ec3dbfbfd06e81f5feb396644a77b..153f506ebbe161eb94773d21ec73f6691e5409f6 100644 (file)
@@ -138,6 +138,7 @@ struct tgroup_ctx {
 
        uint niced_tasks;                 /* number of niced tasks in this group's run queues */
        uint committed_extra_streams;     /* sum of extra front streams committed by muxes in this group */
+       struct fdtab *fdtab;              /* fdtab to be used by that thread group */
 
        /* pad to cache line (64B) */
        char __pad[0];                    /* unused except to check remaining room */
index 5e5d759ce4ad4d587d4a3cca97fd31f4a9e8591d..158c2d480ab099b6fe83898c1cc15d4080ffb0a4 100644 (file)
--- a/src/fd.c
+++ b/src/fd.c
@@ -98,7 +98,8 @@
 #include <haproxy/tools.h>
 
 
-struct fdtab *fdtab             __read_mostly = NULL;  /* array of all the file descriptors */
+THREAD_LOCAL struct fdtab *fdtab = NULL;  /* array of all the file descriptors */
+static struct fdtab *_fdtab;
 struct polled_mask *polled_mask __read_mostly = NULL;  /* Array for the polled_mask of each fd */
 int totalconn;                  /* total # of terminated sessions */
 int actconn;                    /* # of active sessions */
@@ -1191,13 +1192,30 @@ int init_pollers()
 {
        int p;
        struct poller *bp;
+       int nbfdtab;
 
+
+       if (global.tune.options & GTUNE_NO_TG_FD_SHARING) {
+               nbfdtab = global.nbtgroups;
+       } else
+               nbfdtab = 1;
        /* always provide an aligned fdtab */
-       if ((fdtab = ha_aligned_zalloc(64, array_size_or_fail(global.maxsock, sizeof(*fdtab)))) == NULL) {
+       if ((_fdtab = ha_aligned_zalloc(64, array_size_or_fail(global.maxsock, nbfdtab * sizeof(*fdtab)))) == NULL) {
                ha_alert("Not enough memory to allocate %d entries for fdtab!\n", global.maxsock);
                goto fail_tab;
        }
-       vma_set_name(fdtab, global.maxsock * sizeof(*fdtab), "fd", "fdtab");
+
+       for (p = 0; p < global.nbtgroups; p++) {
+               if (global.tune.options & GTUNE_NO_TG_FD_SHARING) {
+                       ha_tgroup_ctx[p].fdtab = (void *)((char *)(void *)_fdtab + p * global.maxsock * sizeof(*fdtab));
+               } else
+                       ha_tgroup_ctx[p].fdtab = _fdtab;
+       }
+       /*
+        * Immediately set the fdtab for our thread, as we'll need it soon
+        */
+       fdtab = ha_tgroup_ctx[0].fdtab;
+       vma_set_name(_fdtab, global.maxsock * sizeof(*fdtab) * nbfdtab, "fd", "fdtab");
 
        if ((polled_mask = calloc(global.maxsock, sizeof(*polled_mask))) == NULL) {
                ha_alert("Not enough memory to allocate %d entries for polled_mask!\n", global.maxsock);
index b242be44b0f1c80fa303d4a286bccac74c47bd3b..a50baf98d6ebb90137ad94d0f8620ca248d43318 100644 (file)
@@ -3121,6 +3121,7 @@ void *run_thread_poll_loop(void *data)
        __decl_thread(static pthread_cond_t  init_cond  = PTHREAD_COND_INITIALIZER);
 
        ha_set_thread(data);
+       fdtab = tg_ctx->fdtab;
        set_thread_cpu_affinity();
        clock_set_local_source();
        ha_random_seed_thread();
index 026eda28223704746b54a0981404174da7b07349..3a6efe48264683aa4b7fe1989acefcda6991ac35 100644 (file)
@@ -239,6 +239,13 @@ void *start_extra_tgroup_threads(void *arg)
        struct tgroup_info *tgi = (struct tgroup_info *)arg;
        int i;
 
+#ifdef CLONE_FILES
+       if (global.tune.options & GTUNE_NO_TG_FD_SHARING)
+               if (unshare(CLONE_FILES) != 0) {
+                       ha_alert("unshare(CLONE_FILES) failed while trying to use one FD table per thread group (%s)\n", strerror(errno));
+                       exit(1);
+               }
+#endif
        /* Create nbthread-1 thread. The first thread is the current one */
        for (i = 1; i < tgi->count; i++)
                pthread_create(&ha_pthread[tgi->base + i], NULL, tgi->start, &ha_thread_info[tgi->base + i]);
@@ -277,6 +284,13 @@ void setup_extra_threads(void *(*handler)(void *))
         */
        for (i = 1; i < global.nbtgroups; i++) {
                ha_tgroup_info[i].start = handler;
+               /*
+                * Copy the fdtab to the new thread group, it should contain
+                * file descriptors that are common to all thread groups
+                */
+               if (global.tune.options & GTUNE_NO_TG_FD_SHARING)
+                       memcpy(ha_tgroup_ctx[i].fdtab, ha_tgroup_ctx[0].fdtab,
+                              global.maxsock * sizeof(*fdtab));
                pthread_create(&ha_pthread[ha_tgroup_info[i].base], NULL, &start_extra_tgroup_threads, &ha_tgroup_info[i]);
        }