MEDIUM: config: use platform independent type hap_cpuset for cpu-map

Use the platform independent type hap_cpuset for the cpu-map statement
parsing. This allow to address CPU index greater than LONGBITS.

Update the documentation to reflect the removal of this limit except for
platforms without cpu_set_t type or equivalent.
This commit is contained in:
Amaury Denoyelle 2021-04-21 18:39:58 +02:00
parent c90932bc8e
commit 982fb53390
4 changed files with 68 additions and 83 deletions

View File

@ -1063,14 +1063,16 @@ cpu-map [auto:]<process-set>[/<thread-set>] <cpu-set>...
a dash ('-'). It also is possible to specify all processes at once using
"all", only odd numbers using "odd" or even numbers using "even", just like
with the "bind-process" directive. The second and forthcoming arguments are
CPU sets. Each CPU set is either a unique number between 0 and 31 or 63 or a
range with two such numbers delimited by a dash ('-'). Multiple CPU numbers
or ranges may be specified, and the processes or threads will be allowed to
bind to all of them. Obviously, multiple "cpu-map" directives may be
specified. Each "cpu-map" directive will replace the previous ones when they
overlap. A thread will be bound on the intersection of its mapping and the
one of the process on which it is attached. If the intersection is null, no
specific binding will be set for the thread.
CPU sets. Each CPU set is either a unique number starting at 0 for the first
CPU or a range with two such numbers delimited by a dash ('-'). Outside of
Linux and BSDs, there may be a limitation on the maximum CPU index to either
31 or 63. Multiple CPU numbers or ranges may be specified, and the processes
or threads will be allowed to bind to all of them. Obviously, multiple
"cpu-map" directives may be specified. Each "cpu-map" directive will replace
the previous ones when they overlap. A thread will be bound on the
intersection of its mapping and the one of the process on which it is
attached. If the intersection is null, no specific binding will be set for
the thread.
Ranges can be partially defined. The higher bound can be omitted. In such
case, it is replaced by the corresponding maximum value, 32 or 64 depending

View File

@ -24,6 +24,7 @@
#include <haproxy/api-t.h>
#include <haproxy/buf-t.h>
#include <haproxy/cpuset-t.h>
#include <haproxy/freq_ctr-t.h>
#include <haproxy/vars-t.h>
@ -160,9 +161,9 @@ struct global {
struct proxy *cli_fe; /* the frontend holding the stats settings */
#ifdef USE_CPU_AFFINITY
struct {
unsigned long proc[MAX_PROCS]; /* list of CPU masks for the 32/64 first processes */
unsigned long proc_t1[MAX_PROCS]; /* list of CPU masks for the 1st thread of each process */
unsigned long thread[MAX_THREADS]; /* list of CPU masks for the 32/64 first threads of the 1st process */
struct hap_cpuset proc[MAX_PROCS]; /* list of CPU masks for the 32/64 first processes */
struct hap_cpuset proc_t1[MAX_PROCS]; /* list of CPU masks for the 1st thread of each process */
struct hap_cpuset thread[MAX_THREADS]; /* list of CPU masks for the 32/64 first threads of the 1st process */
} cpu_map;
#endif
/* The info above is config stuff, it doesn't change during the process' life */

View File

@ -1,4 +1,3 @@
#define _GNU_SOURCE /* for CPU_* from cpuset.h */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -1028,9 +1027,9 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
/* map a process list to a CPU set */
#ifdef USE_CPU_AFFINITY
char *slash;
unsigned long proc = 0, thread = 0, cpus;
int i, j, n, k, autoinc;
struct hap_cpuset cpuset;
unsigned long proc = 0, thread = 0;
int i, j, n, autoinc;
struct hap_cpuset cpus, cpus_copy;
if (!*args[1] || !*args[2]) {
ha_alert("parsing [%s:%d] : %s expects a process number "
@ -1071,26 +1070,15 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
}
}
if (parse_cpu_set((const char **)args+2, &cpuset, &errmsg)) {
if (parse_cpu_set((const char **)args+2, &cpus, &errmsg)) {
ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
#if defined(CPUSET_USE_CPUSET)
k = 0;
while (CPU_COUNT(&cpuset.cpuset)) {
while (!CPU_ISSET(k, &cpuset.cpuset))
++k;
cpus |= 1 << k;
CPU_CLR(k, &cpuset.cpuset);
++k;
}
#elif defined(CPUSET_USE_ULONG)
cpus = cpuset.cpuset;
#endif
if (autoinc &&
my_popcountl(proc) != my_popcountl(cpus) &&
my_popcountl(thread) != my_popcountl(cpus)) {
my_popcountl(proc) != ha_cpuset_count(&cpus) &&
my_popcountl(thread) != ha_cpuset_count(&cpus)) {
ha_alert("parsing [%s:%d] : %s : PROC/THREAD range and CPU sets "
"must have the same size to be automatically bound\n",
file, linenum, args[0]);
@ -1111,16 +1099,19 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
*/
if (!thread) {
/* mapping for whole processes. E.g. cpu-map 1-4 0-3 */
ha_cpuset_assign(&cpus_copy, &cpus);
for (i = n = 0; i < MAX_PROCS; i++) {
/* No mapping for this process */
if (!(proc & (1UL << i)))
continue;
if (!autoinc)
global.cpu_map.proc[i] = cpus;
ha_cpuset_assign(&global.cpu_map.proc[i], &cpus);
else {
n += my_ffsl(cpus >> n);
global.cpu_map.proc[i] = (1UL << (n-1));
ha_cpuset_zero(&global.cpu_map.proc[i]);
n = ha_cpuset_ffs(&cpus_copy) - 1;
ha_cpuset_clr(&cpus_copy, n);
ha_cpuset_set(&global.cpu_map.proc[i], n);
}
}
} else {
@ -1129,44 +1120,48 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
* other combinations are silently ignored.
*/
if (thread == 0x1) {
int val;
/* first thread, iterate on processes. E.g. cpu-map 1-4/1 0-3 */
struct hap_cpuset *dst;
ha_cpuset_assign(&cpus_copy, &cpus);
for (i = n = 0; i < MAX_PROCS; i++) {
/* No mapping for this process */
if (!(proc & (1UL << i)))
continue;
if (!autoinc) {
val = cpus;
}
else {
n += my_ffsl(cpus >> n);
val = 1UL << (n - 1);
}
/* For first process, thread[0] is used.
* Use proc_t1[N] for all others
*/
if (!i)
global.cpu_map.thread[0] = val;
else
global.cpu_map.proc_t1[i] = val;
dst = i ? &global.cpu_map.proc_t1[i] :
&global.cpu_map.thread[0];
if (!autoinc) {
ha_cpuset_assign(dst, &cpus);
}
else {
ha_cpuset_zero(dst);
n = ha_cpuset_ffs(&cpus_copy) - 1;
ha_cpuset_clr(&cpus_copy, n);
ha_cpuset_set(dst, n);
}
}
}
if (proc == 0x1) {
/* first process, iterate on threads. E.g. cpu-map 1/1-4 0-3 */
ha_cpuset_assign(&cpus_copy, &cpus);
for (j = n = 0; j < MAX_THREADS; j++) {
/* No mapping for this thread */
if (!(thread & (1UL << j)))
continue;
if (!autoinc)
global.cpu_map.thread[j] = cpus;
ha_cpuset_assign(&global.cpu_map.thread[j], &cpus);
else {
n += my_ffsl(cpus >> n);
global.cpu_map.thread[j] = (1UL << (n-1));
ha_cpuset_zero(&global.cpu_map.thread[j]);
n = ha_cpuset_ffs(&cpus_copy) - 1;
ha_cpuset_clr(&cpus_copy, n);
ha_cpuset_set(&global.cpu_map.thread[j], n);
}
}
}

View File

@ -92,6 +92,7 @@
#include <haproxy/chunk.h>
#include <haproxy/cli.h>
#include <haproxy/connection.h>
#include <haproxy/cpuset.h>
#include <haproxy/dns.h>
#include <haproxy/dynbuf.h>
#include <haproxy/errors.h>
@ -1268,6 +1269,7 @@ static void init(int argc, char **argv)
struct proxy *px;
struct post_check_fct *pcf;
int ideal_maxconn;
int i;
global.mode = MODE_STARTING;
old_argv = copy_argv(argc, argv);
@ -1576,6 +1578,12 @@ static void init(int argc, char **argv)
global.maxsock = 10; /* reserve 10 fds ; will be incremented by socket eaters */
for (i = 0; i < MAX_PROCS; ++i) {
ha_cpuset_zero(&global.cpu_map.proc[i]);
ha_cpuset_zero(&global.cpu_map.proc_t1[i]);
ha_cpuset_zero(&global.cpu_map.thread[i]);
}
/* in wait mode, we don't try to read the configuration files */
if (!(global.mode & MODE_MWORKER_WAIT)) {
char *env_cfgfiles = NULL;
@ -2925,23 +2933,15 @@ int main(int argc, char **argv)
#ifdef USE_CPU_AFFINITY
if (proc < global.nbproc && /* child */
proc < MAX_PROCS && /* only the first 32/64 processes may be pinned */
global.cpu_map.proc[proc]) /* only do this if the process has a CPU map */
#ifdef __FreeBSD__
{
cpuset_t cpuset;
int i;
unsigned long cpu_map = global.cpu_map.proc[proc];
ha_cpuset_count(&global.cpu_map.proc[proc])) { /* only do this if the process has a CPU map */
CPU_ZERO(&cpuset);
while ((i = ffsl(cpu_map)) > 0) {
CPU_SET(i - 1, &cpuset);
cpu_map &= ~(1UL << (i - 1));
}
ret = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(cpuset), &cpuset);
}
struct hap_cpuset *set = &global.cpu_map.proc[proc];
#ifdef __FreeBSD__
ret = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(set->cpuset), &set->cpuset);
#elif defined(__linux__) || defined(__DragonFly__)
sched_setaffinity(0, sizeof(unsigned long), (void *)&global.cpu_map.proc[proc]);
sched_setaffinity(0, sizeof(set->cpuset), &set->cpuset);
#endif
}
#endif
/* close the pidfile both in children and father */
if (pidfd >= 0) {
@ -3179,14 +3179,14 @@ int main(int argc, char **argv)
global.cpu_map.thread[0] = global.cpu_map.proc_t1[relative_pid-1];
for (i = 0; i < global.nbthread; i++) {
if (global.cpu_map.proc[relative_pid-1])
global.cpu_map.thread[i] &= global.cpu_map.proc[relative_pid-1];
if (ha_cpuset_count(&global.cpu_map.proc[relative_pid-1]))
ha_cpuset_and(&global.cpu_map.thread[i], &global.cpu_map.proc[relative_pid-1]);
if (i < MAX_THREADS && /* only the first 32/64 threads may be pinned */
global.cpu_map.thread[i]) {/* only do this if the thread has a THREAD map */
ha_cpuset_count(&global.cpu_map.thread[i])) {/* only do this if the thread has a THREAD map */
#if defined(__APPLE__)
int j;
unsigned long cpu_map = global.cpu_map.thread[i];
unsigned long cpu_map = global.cpu_map.thread[i].cpuset;
while ((j = ffsl(cpu_map)) > 0) {
thread_affinity_policy_data_t cpu_set = { j - 1 };
@ -3195,22 +3195,9 @@ int main(int argc, char **argv)
cpu_map &= ~(1UL << (j - 1));
}
#else
#if defined(__FreeBSD__) || defined(__NetBSD__)
cpuset_t cpuset;
#else
cpu_set_t cpuset;
#endif
int j;
unsigned long cpu_map = global.cpu_map.thread[i];
CPU_ZERO(&cpuset);
while ((j = ffsl(cpu_map)) > 0) {
CPU_SET(j - 1, &cpuset);
cpu_map &= ~(1UL << (j - 1));
}
struct hap_cpuset *set = &global.cpu_map.thread[i];
pthread_setaffinity_np(ha_thread_info[i].pthread,
sizeof(cpuset), &cpuset);
sizeof(set->cpuset), &set->cpuset);
#endif
}
}