MINOR: global: add option to disable numa detection

Render numa detection optional with a global configuration statement
'no numa-cpu-mapping'. This can be used if the applied affinity of the
algorithm is not optimal. Also complete the documentation with this new
keyword.
This commit is contained in:
Amaury Denoyelle 2021-03-26 18:50:33 +01:00
parent b56a7c89a8
commit 0f50cb9c73
5 changed files with 24 additions and 4 deletions

View File

@ -919,6 +919,7 @@ The following keywords are supported in the "global" section :
- nbproc - nbproc
- nbthread - nbthread
- node - node
- numa-cpu-mapping
- pidfile - pidfile
- pp2-never-send-local - pp2-never-send-local
- presetenv - presetenv
@ -1540,6 +1541,17 @@ nbthread <number>
like "taskset" or "cpuset". Otherwise, this value defaults to 1. The default like "taskset" or "cpuset". Otherwise, this value defaults to 1. The default
value is reported in the output of "haproxy -vv". See also "nbproc". value is reported in the output of "haproxy -vv". See also "nbproc".
numa-cpu-mapping
By default, if running on Linux, haproxy inspects on startup the CPU topology
of the machine. If a multi-socket machine is detected, the affinity is
automatically calculated to run on the CPUs of a single node. This is done in
order to not suffer from the performance penalties caused by the inter-socket
bus latency. However, if the applied binding is non optimal on a particular
architecture, it can be disabled with the statement 'no numa-cpu-mapping'.
This automatic binding is also not applied if a nbthread statement is present
in the configuration, or the affinity of the process is already specified,
for example via the 'cpu-map' directive or the taskset utility.
pidfile <pidfile> pidfile <pidfile>
Writes PIDs of all daemons into file <pidfile> when daemon mode or writes PID Writes PIDs of all daemons into file <pidfile> when daemon mode or writes PID
of master process into file <pidfile> when master-worker mode. This option is of master process into file <pidfile> when master-worker mode. This option is

View File

@ -166,6 +166,7 @@ struct global {
struct hap_cpuset thread[MAX_THREADS]; /* list of CPU masks for the 32/64 first threads of the 1st process */ struct hap_cpuset thread[MAX_THREADS]; /* list of CPU masks for the 32/64 first threads of the 1st process */
} cpu_map; } cpu_map;
#endif #endif
int numa_cpu_mapping;
/* The info above is config stuff, it doesn't change during the process' life */ /* The info above is config stuff, it doesn't change during the process' life */
/* A number of the elements below are updated by all threads in real time and /* A number of the elements below are updated by all threads in real time and
* suffer high contention, so we need to put them in their own cache lines, if * suffer high contention, so we need to put them in their own cache lines, if

View File

@ -42,7 +42,8 @@ static const char *common_kw_list[] = {
"log-send-hostname", "server-state-base", "server-state-file", "log-send-hostname", "server-state-base", "server-state-file",
"log-tag", "spread-checks", "max-spread-checks", "cpu-map", "setenv", "log-tag", "spread-checks", "max-spread-checks", "cpu-map", "setenv",
"presetenv", "unsetenv", "resetenv", "strict-limits", "localpeer", "presetenv", "unsetenv", "resetenv", "strict-limits", "localpeer",
"defaults", "listen", "frontend", "backend", "peers", "resolvers", "numa-cpu-mapping", "defaults", "listen", "frontend", "backend",
"peers", "resolvers",
NULL /* must be last */ NULL /* must be last */
}; };
@ -1288,6 +1289,9 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
} }
setenv("HAPROXY_LOCALPEER", localpeer, 1); setenv("HAPROXY_LOCALPEER", localpeer, 1);
} }
else if (strcmp(args[0], "numa-cpu-mapping") == 0) {
global.numa_cpu_mapping = (kwm == KWM_NO) ? 0 : 1;
}
else { else {
struct cfg_kw_list *kwl; struct cfg_kw_list *kwl;
const char *best; const char *best;

View File

@ -1846,10 +1846,12 @@ int readcfgfile(const char *file)
if (kwm != KWM_STD && strcmp(args[0], "option") != 0 && if (kwm != KWM_STD && strcmp(args[0], "option") != 0 &&
strcmp(args[0], "log") != 0 && strcmp(args[0], "busy-polling") != 0 && strcmp(args[0], "log") != 0 && strcmp(args[0], "busy-polling") != 0 &&
strcmp(args[0], "set-dumpable") != 0 && strcmp(args[0], "strict-limits") != 0 && strcmp(args[0], "set-dumpable") != 0 && strcmp(args[0], "strict-limits") != 0 &&
strcmp(args[0], "insecure-fork-wanted") != 0) { strcmp(args[0], "insecure-fork-wanted") != 0 &&
strcmp(args[0], "numa-cpu-mapping") != 0) {
ha_alert("parsing [%s:%d]: negation/default currently " ha_alert("parsing [%s:%d]: negation/default currently "
"supported only for options, log, busy-polling, " "supported only for options, log, busy-polling, "
"set-dumpable, strict-limits, and insecure-fork-wanted.\n", file, linenum); "set-dumpable, strict-limits, insecure-fork-wanted "
"and numa-cpu-mapping.\n", file, linenum);
err_code |= ERR_ALERT | ERR_FATAL; err_code |= ERR_ALERT | ERR_FATAL;
fatal++; fatal++;
} }
@ -2189,7 +2191,7 @@ int check_config_validity()
if (global.nbproc == 1) { if (global.nbproc == 1) {
int numa_cores = 0; int numa_cores = 0;
#if defined(__linux__) && defined USE_CPU_AFFINITY #if defined(__linux__) && defined USE_CPU_AFFINITY
if (!thread_cpu_mask_forced()) if (global.numa_cpu_mapping && !thread_cpu_mask_forced())
numa_cores = numa_detect_topology(); numa_cores = numa_detect_topology();
#endif #endif
global.nbthread = numa_cores ? numa_cores : global.nbthread = numa_cores ? numa_cores :

View File

@ -160,6 +160,7 @@ volatile unsigned long stopping_thread_mask = 0; /* Threads acknowledged stoppin
struct global global = { struct global global = {
.hard_stop_after = TICK_ETERNITY, .hard_stop_after = TICK_ETERNITY,
.nbproc = 1, .nbproc = 1,
.numa_cpu_mapping = 1,
.nbthread = 0, .nbthread = 0,
.req_count = 0, .req_count = 0,
.logsrvs = LIST_HEAD_INIT(global.logsrvs), .logsrvs = LIST_HEAD_INIT(global.logsrvs),