diff --git a/README b/README
index 9a551f76fa..5553915be0 100644
--- a/README
+++ b/README
@@ -67,13 +67,14 @@ REQUIREMENTS:
of systemd. Taint flag 'old-kernel' will be set. systemd will most likely
still function, but upstream support and testing are limited.
- Linux kernel ≥ 6.3 for MFD_EXEC/MFD_NOEXEC_SEAL and tmpfs noswap option
+ Linux kernel ≥ 5.15 for MPOL_PREFERRED_MANY NUMA policy
+ ≥ 6.3 for MFD_EXEC/MFD_NOEXEC_SEAL and tmpfs noswap option
≥ 6.5 for name_to_handle_at() AT_HANDLE_FID, SO_PEERPIDFD/SO_PASSPIDFD,
and MOVE_MOUNT_BENEATH
≥ 6.6 for quota support on tmpfs
≥ 6.7 for cgroup2fs memory_hugetlb_accounting option
≥ 6.8 for STATX_MNT_ID_UNIQUE
- ≥ 6.9 for pidfs
+ ≥ 6.9 for pidfs and MPOL_WEIGHTED_INTERLEAVE NUMA policy
≥ 6.10 for fcntl(F_DUPFD_QUERY), unprivileged linkat(AT_EMPTY_PATH),
and block device 'partscan' sysfs attribute
≥ 6.12 for AT_HANDLE_MNT_ID_UNIQUE
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 6524ba631a..0b43bc6960 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -1408,12 +1408,16 @@ CapabilityBoundingSet=~CAP_B CAP_C
NUMAPolicy=
Controls the NUMA memory policy of the executed processes. Takes a policy type, one of:
- , , , and
- . A list of NUMA nodes that should be associated with the policy must be specified
- in NUMAMask=. For more details on each policy please see,
+ , , , ,
+ , (requires Linux 5.15 or newer) and
+ (requires Linux 6.9 or newer, weights are configured via
+ /sys/kernel/mm/mempolicy/weighted_interleave/). A list of NUMA nodes that should be
+ associated with the policy must be specified in NUMAMask=. For more details on each
+ policy please see,
set_mempolicy2. For overall
overview of NUMA support in Linux see,
numa7.
+ If the kernel does not support the requested policy, a warning is logged and the setting is ignored.
diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c
index c45c807198..2b0ced7b7d 100644
--- a/src/core/exec-invoke.c
+++ b/src/core/exec-invoke.c
@@ -5646,8 +5646,10 @@ int exec_invoke(
if (mpol_is_valid(numa_policy_get_type(&context->numa_policy))) {
r = apply_numa_policy(&context->numa_policy);
- if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
+ if (r == -ENOSYS)
log_debug_errno(r, "NUMA support not available, ignoring.");
+ else if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
+ log_warning_errno(r, "NUMA policy not supported by kernel, ignoring.");
else if (r < 0) {
*exit_status = EXIT_NUMA_POLICY;
return log_error_errno(r, "Failed to set NUMA memory policy: %m");
diff --git a/src/core/main.c b/src/core/main.c
index 6c83953f30..810da526d7 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -2029,8 +2029,10 @@ static void update_numa_policy(bool skip_setup) {
}
r = apply_numa_policy(&arg_numa_policy);
- if (r == -EOPNOTSUPP)
+ if (r == -ENOSYS)
log_debug_errno(r, "NUMA support not available, ignoring.");
+ else if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
+ log_warning_errno(r, "NUMA policy not supported by kernel, ignoring.");
else if (r < 0)
log_warning_errno(r, "Failed to set NUMA memory policy, ignoring: %m");
}
diff --git a/src/core/varlink-execute.c b/src/core/varlink-execute.c
index 7056439a22..a80fa96927 100644
--- a/src/core/varlink-execute.c
+++ b/src/core/varlink-execute.c
@@ -315,7 +315,11 @@ static int numa_policy_build_json(sd_json_variant **ret, const char *name, void
return 0;
}
- return sd_json_variant_new_string(ret, mpol_to_string(t));
+ _cleanup_free_ char *s = strdup(mpol_to_string(t));
+ if (!s)
+ return -ENOMEM;
+
+ return sd_json_variant_new_string(ret, json_underscorify(s));
}
static int numa_mask_build_json(sd_json_variant **ret, const char *name, void *userdata) {
diff --git a/src/shared/numa-util.c b/src/shared/numa-util.c
index 34ddc0e547..af8770106e 100644
--- a/src/shared/numa-util.c
+++ b/src/shared/numa-util.c
@@ -73,7 +73,11 @@ int apply_numa_policy(const NUMAPolicy *policy) {
assert(policy);
if (get_mempolicy(NULL, NULL, 0, NULL, 0) < 0 && errno == ENOSYS)
- return -EOPNOTSUPP;
+ /* NUMA syscall interface not available (kernel compiled without NUMA support).
+ * Return -ENOSYS so callers can distinguish this from -EOPNOTSUPP, which we
+ * return below when the syscall interface exists but the requested policy is
+ * not supported by this kernel version. */
+ return -ENOSYS;
if (!numa_policy_is_valid(policy))
return -EINVAL;
@@ -83,8 +87,14 @@ int apply_numa_policy(const NUMAPolicy *policy) {
return r;
r = set_mempolicy(numa_policy_get_type(policy), nodes, maxnode);
- if (r < 0)
+ if (r < 0) {
+ // FIXME: This compatibility code path shall be removed once kernel 6.9
+ // becomes the new minimal baseline (MPOL_WEIGHTED_INTERLEAVE).
+ if (errno == EINVAL && IN_SET(numa_policy_get_type(policy),
+ MPOL_PREFERRED_MANY, MPOL_WEIGHTED_INTERLEAVE))
+ return -EOPNOTSUPP;
return -errno;
+ }
return 0;
}
@@ -241,11 +251,13 @@ int numa_mask_add_all(CPUSet *mask) {
}
static const char* const mpol_table[] = {
- [MPOL_DEFAULT] = "default",
- [MPOL_PREFERRED] = "preferred",
- [MPOL_BIND] = "bind",
- [MPOL_INTERLEAVE] = "interleave",
- [MPOL_LOCAL] = "local",
+ [MPOL_DEFAULT] = "default",
+ [MPOL_PREFERRED] = "preferred",
+ [MPOL_BIND] = "bind",
+ [MPOL_INTERLEAVE] = "interleave",
+ [MPOL_LOCAL] = "local",
+ [MPOL_PREFERRED_MANY] = "preferred-many",
+ [MPOL_WEIGHTED_INTERLEAVE] = "weighted-interleave",
};
DEFINE_STRING_TABLE_LOOKUP(mpol, int);
diff --git a/src/shared/numa-util.h b/src/shared/numa-util.h
index 01079351b0..8b9a81852f 100644
--- a/src/shared/numa-util.h
+++ b/src/shared/numa-util.h
@@ -7,7 +7,7 @@
#include "shared-forward.h"
static inline bool mpol_is_valid(int t) {
- return t >= MPOL_DEFAULT && t <= MPOL_LOCAL;
+ return t >= MPOL_DEFAULT && t <= MPOL_WEIGHTED_INTERLEAVE;
}
typedef struct NUMAPolicy {
diff --git a/src/shared/varlink-io.systemd.Unit.c b/src/shared/varlink-io.systemd.Unit.c
index 3fc2d6fc79..5a8e7c9833 100644
--- a/src/shared/varlink-io.systemd.Unit.c
+++ b/src/shared/varlink-io.systemd.Unit.c
@@ -173,7 +173,9 @@ SD_VARLINK_DEFINE_ENUM_TYPE(
SD_VARLINK_DEFINE_ENUM_VALUE(preferred),
SD_VARLINK_DEFINE_ENUM_VALUE(bind),
SD_VARLINK_DEFINE_ENUM_VALUE(interleave),
- SD_VARLINK_DEFINE_ENUM_VALUE(local));
+ SD_VARLINK_DEFINE_ENUM_VALUE(local),
+ SD_VARLINK_DEFINE_ENUM_VALUE(preferred_many),
+ SD_VARLINK_DEFINE_ENUM_VALUE(weighted_interleave));
SD_VARLINK_DEFINE_ENUM_TYPE(
MountPropagationFlag,
diff --git a/test/units/TEST-36-NUMAPOLICY.sh b/test/units/TEST-36-NUMAPOLICY.sh
index 1e2d7ac8ac..3eb9b2410f 100755
--- a/test/units/TEST-36-NUMAPOLICY.sh
+++ b/test/units/TEST-36-NUMAPOLICY.sh
@@ -227,6 +227,26 @@ else
pid1ReloadWithStrace
grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
+ echo "PID1 NUMAPolicy support - Preferred-many policy w/o mask"
+ writePID1NUMAPolicy "preferred-many"
+ pid1ReloadWithJournal
+ grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog"
+
+ echo "PID1 NUMAPolicy support - Preferred-many policy w/ mask"
+ writePID1NUMAPolicy "preferred-many" "0"
+ pid1ReloadWithStrace
+ grep -E "set_mempolicy\((MPOL_PREFERRED_MANY|0x5 [^,]*), \[0x0*1\]" "$straceLog"
+
+ echo "PID1 NUMAPolicy support - Weighted-interleave policy w/o mask"
+ writePID1NUMAPolicy "weighted-interleave"
+ pid1ReloadWithJournal
+ grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog"
+
+ echo "PID1 NUMAPolicy support - Weighted-interleave policy w/ mask"
+ writePID1NUMAPolicy "weighted-interleave" "0"
+ pid1ReloadWithStrace
+ grep -E "set_mempolicy\((MPOL_WEIGHTED_INTERLEAVE|0x6 [^,]*), \[0x0*1\]" "$straceLog"
+
echo "Unit file NUMAPolicy support - Default policy w/o mask"
writeTestUnitNUMAPolicy "default"
pid1StartUnitWithStrace "$testUnit"
@@ -297,6 +317,34 @@ else
# Mask must be ignored
grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
+ echo "Unit file NUMAPolicy support - Preferred-many policy w/o mask"
+ writeTestUnitNUMAPolicy "preferred-many"
+ pid1StartUnitWithStrace "$testUnit"
+ pid1StopUnit "$testUnit"
+ [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]]
+
+ echo "Unit file NUMAPolicy support - Preferred-many policy w/ mask"
+ writeTestUnitNUMAPolicy "preferred-many" "0"
+ pid1StartUnitWithStrace "$testUnit"
+ systemctlCheckNUMAProperties "$testUnit" "preferred-many" "0"
+ varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "{\"name\":\"$testUnit\"}" | jq -e '.context.Exec.NUMAPolicy == "preferred_many"'
+ pid1StopUnit "$testUnit"
+ grep -E "set_mempolicy\((MPOL_PREFERRED_MANY|0x5 [^,]*), \[0x0*1\]" "$straceLog"
+
+ echo "Unit file NUMAPolicy support - Weighted-interleave policy w/o mask"
+ writeTestUnitNUMAPolicy "weighted-interleave"
+ pid1StartUnitWithStrace "$testUnit"
+ pid1StopUnit "$testUnit"
+ [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]]
+
+ echo "Unit file NUMAPolicy support - Weighted-interleave policy w/ mask"
+ writeTestUnitNUMAPolicy "weighted-interleave" "0"
+ pid1StartUnitWithStrace "$testUnit"
+ systemctlCheckNUMAProperties "$testUnit" "weighted-interleave" "0"
+ varlinkctl call /run/systemd/io.systemd.Manager io.systemd.Unit.List "{\"name\":\"$testUnit\"}" | jq -e '.context.Exec.NUMAPolicy == "weighted_interleave"'
+ pid1StopUnit "$testUnit"
+ grep -E "set_mempolicy\((MPOL_WEIGHTED_INTERLEAVE|0x6 [^,]*), \[0x0*1\]" "$straceLog"
+
echo "Unit file CPUAffinity=NUMA support"
writeTestUnitNUMAPolicy "bind" "0"
echo "CPUAffinity=numa" >>"$testUnitNUMAConf"
@@ -343,6 +391,14 @@ else
systemctlCheckNUMAProperties "$runUnit" "local" ""
systemctl cat "$runUnit" | grep 'CPUAffinity=numa' >/dev/null
pid1StopUnit "$runUnit"
+
+ systemd-run -p NUMAPolicy=preferred-many -p NUMAMask=0 --unit "$runUnit" sleep 1000
+ systemctlCheckNUMAProperties "$runUnit" "preferred-many" "0"
+ pid1StopUnit "$runUnit"
+
+ systemd-run -p NUMAPolicy=weighted-interleave -p NUMAMask=0 --unit "$runUnit" sleep 1000
+ systemctlCheckNUMAProperties "$runUnit" "weighted-interleave" "0"
+ pid1StopUnit "$runUnit"
fi
# Cleanup