MINOR: init: pre-allocate kernel data structures on init
The Linux kernel maintains data structures to track a processes' open file
descriptors, and it expands these structures as necessary when FD usage grows
(at every FD=2^X starting at 64). However when threading is in use, during
expansion the kernel will pause (observed up to 47ms) while it waits for thread
synchronization (see https://bugzilla.kernel.org/show_bug.cgi?id=217366).
This change addresses the issue and avoids the random pauses by opening the
maximum file descriptor during initialization, so that expansion will not occur
while processing traffic.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index f8d44cb..5f8474a 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1107,6 +1107,7 @@
- pidfile
- pp2-never-send-local
- presetenv
+ - prealloc-fd
- resetenv
- set-dumpable
- set-var
@@ -2084,6 +2085,12 @@
in the configuration file sees the new value. See also "setenv", "resetenv",
and "unsetenv".
+prealloc-fd
+ Performs a one-time open of the maximum file descriptor which results in a
+ pre-allocation of the kernel's data structures. This prevents short pauses
+ when nbthread>1 and HAProxy opens a file descriptor which requires the kernel
+ to expand its data structures.
+
resetenv [<name> ...]
Removes all environment variables except the ones specified in argument. It
allows to use a clean controlled environment before setting new values with
diff --git a/include/haproxy/global-t.h b/include/haproxy/global-t.h
index e7d02fe..0bcfa57 100644
--- a/include/haproxy/global-t.h
+++ b/include/haproxy/global-t.h
@@ -188,6 +188,7 @@
} unix_bind;
struct proxy *cli_fe; /* the frontend holding the stats settings */
int numa_cpu_mapping;
+ int prealloc_fd;
int cfg_curr_line; /* line number currently being parsed */
const char *cfg_curr_file; /* config file currently being parsed or NULL */
char *cfg_curr_section; /* config section name currently being parsed or NULL */
diff --git a/src/cfgparse-global.c b/src/cfgparse-global.c
index cc643f4..30334ba 100644
--- a/src/cfgparse-global.c
+++ b/src/cfgparse-global.c
@@ -1357,3 +1357,21 @@
return err_code;
}
+static int cfg_parse_prealloc_fd(char **args, int section_type, struct proxy *curpx,
+ const struct proxy *defpx, const char *file, int line,
+ char **err)
+{
+ if (too_many_args(0, args, err, NULL))
+ return -1;
+
+ global.prealloc_fd = 1;
+
+ return 0;
+}
+
+static struct cfg_kw_list cfg_kws = {ILH, {
+ { CFG_GLOBAL, "prealloc-fd", cfg_parse_prealloc_fd },
+ { 0, NULL, NULL },
+}};
+
+INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
diff --git a/src/haproxy.c b/src/haproxy.c
index 0eb78d8..0405647 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -3522,6 +3522,14 @@
global.maxsock);
}
+ if (global.prealloc_fd && fcntl((int)limit.rlim_cur - 1, F_GETFD) == -1) {
+ if (dup2(0, (int)limit.rlim_cur - 1) == -1)
+ ha_warning("[%s.main()] Unable to preallocate file descriptor %lu : %s",
+ argv[0], limit.rlim_cur-1, strerror(errno));
+ else
+ close((int)limit.rlim_cur - 1);
+ }
+
/* update the ready date a last time to also account for final setup time */
clock_update_date(0, 1);
clock_adjust_now_offset();