diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 2b53002f78f..d2279a9b9ad 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1042,7 +1042,8 @@ CapabilityBoundingSet=~CAP_B CAP_C Controls the NUMA node list which will be applied alongside with selected NUMA policy. Takes a list of NUMA nodes and has the same syntax as a list of CPUs for CPUAffinity= - option. Note that the list of NUMA nodes is not required for and + option or special "all" value which will include all available NUMA nodes in the mask. Note that the list + of NUMA nodes is not required for and policies and for policy we expect a single NUMA node. diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index a93f12b27c4..633c7ca106a 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1370,10 +1370,18 @@ int config_parse_numa_mask(const char *unit, assert(rvalue); assert(data); - r = parse_cpu_set_extend(rvalue, &p->nodes, true, unit, filename, line, lvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse NUMA node mask, ignoring: %s", rvalue); - return 0; + if (streq(rvalue, "all")) { + r = numa_mask_add_all(&p->nodes); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to create NUMA mask representing \"all\" NUMA nodes, ignoring: %m"); + return 0; + } + } else { + r = parse_cpu_set_extend(rvalue, &p->nodes, true, unit, filename, line, lvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse NUMA node mask, ignoring: %s", rvalue); + return 0; + } } return r; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index b6def087f63..e579d606c36 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -1275,9 +1275,15 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con _cleanup_free_ uint8_t *array = NULL; size_t allocated; - r = parse_cpu_set(eq, &nodes); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value: %s", field, eq); + if (eq && streq(eq, "all")) { + r = numa_mask_add_all(&nodes); + if (r < 0) + return log_error_errno(r, "Failed to create NUMA mask representing \"all\" NUMA nodes: %m"); + } else { + r = parse_cpu_set(eq, &nodes); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value: %s", field, eq); + } r = cpu_set_to_dbus(&nodes, &array, &allocated); if (r < 0) diff --git a/src/shared/cpu-set-util.c b/src/shared/cpu-set-util.c index 9b9238362f2..8779d1d4d30 100644 --- a/src/shared/cpu-set-util.c +++ b/src/shared/cpu-set-util.c @@ -105,7 +105,7 @@ int cpu_set_realloc(CPUSet *cpu_set, unsigned ncpus) { return 0; } -static int cpu_set_add(CPUSet *cpu_set, unsigned cpu) { +int cpu_set_add(CPUSet *cpu_set, unsigned cpu) { int r; if (cpu >= 8192) diff --git a/src/shared/cpu-set-util.h b/src/shared/cpu-set-util.h index a60d4ec41b5..9ec83f69302 100644 --- a/src/shared/cpu-set-util.h +++ b/src/shared/cpu-set-util.h @@ -20,6 +20,7 @@ static inline void cpu_set_reset(CPUSet *a) { } int cpu_set_add_all(CPUSet *a, const CPUSet *b); +int cpu_set_add(CPUSet *a, unsigned cpu); char* cpu_set_to_string(const CPUSet *a); char *cpu_set_to_range_string(const CPUSet *a); diff --git a/src/shared/numa-util.c b/src/shared/numa-util.c index 187992dc69e..3ec8ffc5b26 100644 --- a/src/shared/numa-util.c +++ b/src/shared/numa-util.c @@ -5,6 +5,8 @@ #include "alloc-util.h" #include "cpu-set-util.h" +#include "dirent-util.h" +#include "fd-util.h" #include "fileio.h" #include "macro.h" #include "missing_syscall.h" @@ -124,6 +126,61 @@ int numa_to_cpu_set(const NUMAPolicy *policy, CPUSet *ret) { return 0; } +static int numa_max_node(void) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r, max_node = 0; + + d = opendir("/sys/devices/system/node"); + if (!d) + return -errno; + + FOREACH_DIRENT(de, d, break) { + int node; + const char *n; + + (void) dirent_ensure_type(d, de); + + if (de->d_type != DT_DIR) + continue; + + n = startswith(de->d_name, "node"); + if (!n) + continue; + + r = safe_atoi(n, &node); + if (r < 0) + continue; + + if (node > max_node) + max_node = node; + } + + return max_node; +} + +int numa_mask_add_all(CPUSet *mask) { + int m; + + assert(mask); + + m = numa_max_node(); + if (m < 0) { + log_debug_errno(m, "Failed to determine maximum NUMA node index, assuming 1023: %m"); + m = 1023; /* CONFIG_NODES_SHIFT is set to 10 on x86_64, i.e. 1024 NUMA nodes in total */ + } + + for (int i = 0; i <= m; i++) { + int r; + + r = cpu_set_add(mask, i); + if (r < 0) + return r; + } + + return 0; +} + static const char* const mpol_table[] = { [MPOL_DEFAULT] = "default", [MPOL_PREFERRED] = "preferred", diff --git a/src/shared/numa-util.h b/src/shared/numa-util.h index c99178903b3..2a9ced74e8d 100644 --- a/src/shared/numa-util.h +++ b/src/shared/numa-util.h @@ -29,5 +29,7 @@ static inline void numa_policy_reset(NUMAPolicy *p) { int apply_numa_policy(const NUMAPolicy *policy); int numa_to_cpu_set(const NUMAPolicy *policy, CPUSet *set); +int numa_mask_add_all(CPUSet *mask); + const char* mpol_to_string(int i) _const_; int mpol_from_string(const char *s) _pure_;