From 425d7ad89daa223ab00479119c8b6c41ba033314 Mon Sep 17 00:00:00 2001 From: Patrick Hemmer Date: Tue, 23 May 2023 13:02:08 -0400 Subject: [PATCH] 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. --- doc/configuration.txt | 7 +++++++ include/haproxy/global-t.h | 1 + src/cfgparse-global.c | 18 ++++++++++++++++++ src/haproxy.c | 8 ++++++++ 4 files changed, 34 insertions(+) diff --git a/doc/configuration.txt b/doc/configuration.txt index f8d44cbeb..5f8474adf 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1107,6 +1107,7 @@ The following keywords are supported in the "global" section : - pidfile - pp2-never-send-local - presetenv + - prealloc-fd - resetenv - set-dumpable - set-var @@ -2084,6 +2085,12 @@ presetenv 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 [ ...] 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 e7d02fe2f..0bcfa577a 100644 --- a/include/haproxy/global-t.h +++ b/include/haproxy/global-t.h @@ -188,6 +188,7 @@ struct global { } 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 cc643f46e..30334ba13 100644 --- a/src/cfgparse-global.c +++ b/src/cfgparse-global.c @@ -1357,3 +1357,21 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm) 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 0eb78d857..040564701 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -3522,6 +3522,14 @@ int main(int argc, char **argv) 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();