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.
This commit is contained in:
Patrick Hemmer 2023-05-23 13:02:08 -04:00 committed by Willy Tarreau
parent 6626195ff2
commit 425d7ad89d
4 changed files with 34 additions and 0 deletions

View File

@ -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 <name> <value>
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

View File

@ -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 */

View File

@ -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);

View File

@ -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();