diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index 22bb3b705b..9a0e02187f 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -796,6 +796,15 @@
integer in the range -1000…1000.
+
+
+
+ Controls the CPU affinity of the container payload. Takes a comma separated list of CPU numbers
+ or number ranges (the latter's start and end value separated by dashes). See sched_setaffinity2 for
+ details.
+
+
diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml
index 748a633a33..1780bfd79a 100644
--- a/man/systemd.nspawn.xml
+++ b/man/systemd.nspawn.xml
@@ -322,6 +322,15 @@
details.
+
+ CPUAffinity=
+
+ Configures the CPU affinity. This is equivalent to the command
+ line switch, and takes the same argument. See
+ systemd-nspawn1 for
+ details.
+
+
Hostname=
diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf
index de00dbc243..f8234e75d4 100644
--- a/src/nspawn/nspawn-gperf.gperf
+++ b/src/nspawn/nspawn-gperf.gperf
@@ -52,6 +52,7 @@ Exec.LimitRTTIME, config_parse_rlimit, RLIMIT_RTTIME, of
Exec.Hostname, config_parse_hostname, 0, offsetof(Settings, hostname)
Exec.NoNewPrivileges, config_parse_tristate, 0, offsetof(Settings, no_new_privileges)
Exec.OOMScoreAdjust, config_parse_oom_score_adjust, 0, 0
+Exec.CPUAffinity, config_parse_cpu_affinity, 0, 0
Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only)
Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode)
Files.Bind, config_parse_bind, 0, 0
diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c
index c91ac73e2e..0acf718456 100644
--- a/src/nspawn/nspawn-settings.c
+++ b/src/nspawn/nspawn-settings.c
@@ -8,6 +8,7 @@
#include "alloc-util.h"
#include "cap-list.h"
#include "conf-parser.h"
+#include "cpu-set-util.h"
#include "hostname-util.h"
#include "nspawn-network.h"
#include "nspawn-settings.h"
@@ -85,6 +86,7 @@ Settings* settings_free(Settings *s) {
strv_free(s->syscall_blacklist);
rlimit_free_all(s->rlimit);
free(s->hostname);
+ s->cpuset = cpu_set_mfree(s->cpuset);
strv_free(s->network_interfaces);
strv_free(s->network_macvlan);
@@ -673,3 +675,52 @@ int config_parse_oom_score_adjust(
return 0;
}
+
+int config_parse_cpu_affinity(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
+ Settings *settings = data;
+ int ncpus;
+
+ assert(rvalue);
+ assert(settings);
+
+ ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue);
+ if (ncpus < 0)
+ return ncpus;
+
+ if (ncpus == 0) {
+ /* An empty assignment resets the CPU list */
+ settings->cpuset = cpu_set_mfree(settings->cpuset);
+ settings->cpuset_ncpus = 0;
+ return 0;
+ }
+
+ if (!settings->cpuset) {
+ settings->cpuset = TAKE_PTR(cpuset);
+ settings->cpuset_ncpus = (unsigned) ncpus;
+ return 0;
+ }
+
+ if (settings->cpuset_ncpus < (unsigned) ncpus) {
+ CPU_OR_S(CPU_ALLOC_SIZE(settings->cpuset_ncpus), cpuset, settings->cpuset, cpuset);
+ CPU_FREE(settings->cpuset);
+ settings->cpuset = TAKE_PTR(cpuset);
+ settings->cpuset_ncpus = (unsigned) ncpus;
+ return 0;
+ }
+
+ CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus), settings->cpuset, settings->cpuset, cpuset);
+
+ return 0;
+}
diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h
index 7cf5be625b..c786bf8c86 100644
--- a/src/nspawn/nspawn-settings.h
+++ b/src/nspawn/nspawn-settings.h
@@ -7,6 +7,7 @@
Copyright 2015 Lennart Poettering
***/
+#include
#include
#include "sd-id128.h"
@@ -52,9 +53,10 @@ typedef enum SettingsMask {
SETTING_HOSTNAME = UINT64_C(1) << 17,
SETTING_NO_NEW_PRIVILEGES = UINT64_C(1) << 18,
SETTING_OOM_SCORE_ADJUST = UINT64_C(1) << 19,
- SETTING_RLIMIT_FIRST = UINT64_C(1) << 20, /* we define one bit per resource limit here */
- SETTING_RLIMIT_LAST = UINT64_C(1) << (20 + _RLIMIT_MAX - 1),
- _SETTINGS_MASK_ALL = (UINT64_C(1) << (20 + _RLIMIT_MAX)) - 1
+ SETTING_CPU_AFFINITY = UINT64_C(1) << 20,
+ SETTING_RLIMIT_FIRST = UINT64_C(1) << 21, /* we define one bit per resource limit here */
+ SETTING_RLIMIT_LAST = UINT64_C(1) << (21 + _RLIMIT_MAX - 1),
+ _SETTINGS_MASK_ALL = (UINT64_C(1) << (21 + _RLIMIT_MAX)) - 1
} SettingsMask;
typedef struct Settings {
@@ -81,6 +83,8 @@ typedef struct Settings {
int no_new_privileges;
int oom_score_adjust;
bool oom_score_adjust_set;
+ cpu_set_t *cpuset;
+ unsigned cpuset_ncpus;
/* [Image] */
int read_only;
@@ -127,3 +131,4 @@ int config_parse_private_users(const char *unit, const char *filename, unsigned
int config_parse_syscall_filter(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_hostname(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_oom_score_adjust(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_cpu_affinity(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 517c750eca..460f109656 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -43,6 +43,7 @@
#include "capability-util.h"
#include "cgroup-util.h"
#include "copy.h"
+#include "cpu-set-util.h"
#include "dev-setup.h"
#include "dissect-image.h"
#include "env-util.h"
@@ -207,6 +208,8 @@ static struct rlimit *arg_rlimit[_RLIMIT_MAX] = {};
static bool arg_no_new_privileges = false;
static int arg_oom_score_adjust = 0;
static bool arg_oom_score_adjust_set = false;
+static cpu_set_t *arg_cpuset = NULL;
+static unsigned arg_cpuset_ncpus = 0;
static void help(void) {
@@ -278,6 +281,7 @@ static void help(void) {
" --rlimit=NAME=LIMIT Set a resource limit for the payload\n"
" --oom-score-adjust=VALUE\n"
" Adjust the OOM score value for the payload\n"
+ " --cpu-affinity=CPUS Adjust the CPU affinity of the container\n"
" --kill-signal=SIGNAL Select signal to use for shutting down PID 1\n"
" --link-journal=MODE Link up guest journal, one of no, auto, guest, \n"
" host, try-guest, try-host\n"
@@ -457,6 +461,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_HOSTNAME,
ARG_NO_NEW_PRIVILEGES,
ARG_OOM_SCORE_ADJUST,
+ ARG_CPU_AFFINITY,
};
static const struct option options[] = {
@@ -514,6 +519,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "system-call-filter", required_argument, NULL, ARG_SYSTEM_CALL_FILTER },
{ "rlimit", required_argument, NULL, ARG_RLIMIT },
{ "oom-score-adjust", required_argument, NULL, ARG_OOM_SCORE_ADJUST },
+ { "cpu-affinity", required_argument, NULL, ARG_CPU_AFFINITY },
{}
};
@@ -1186,6 +1192,22 @@ static int parse_argv(int argc, char *argv[]) {
arg_settings_mask |= SETTING_OOM_SCORE_ADJUST;
break;
+ case ARG_CPU_AFFINITY: {
+ _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
+
+ r = parse_cpu_set(optarg, &cpuset);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse CPU affinity mask: %s", optarg);
+
+ if (arg_cpuset)
+ CPU_FREE(arg_cpuset);
+
+ arg_cpuset = TAKE_PTR(cpuset);
+ arg_cpuset_ncpus = r;
+ arg_settings_mask |= SETTING_CPU_AFFINITY;
+ break;
+ }
+
case '?':
return -EINVAL;
@@ -2476,6 +2498,10 @@ static int inner_child(
return log_error_errno(r, "Failed to adjust OOM score: %m");
}
+ if (arg_cpuset)
+ if (sched_setaffinity(0, CPU_ALLOC_SIZE(arg_cpuset_ncpus), arg_cpuset) < 0)
+ return log_error_errno(errno, "Failed to set CPU affinity: %m");
+
r = drop_capabilities();
if (r < 0)
return log_error_errno(r, "drop_capabilities() failed: %m");
@@ -3397,6 +3423,19 @@ static int load_settings(void) {
}
}
+ if ((arg_settings_mask & SETTING_CPU_AFFINITY) == 0 &&
+ settings->cpuset) {
+
+ if (!arg_settings_trusted)
+ log_warning("Ignoring CPUAffinity= setting, file '%s' is not trusted.", p);
+ else {
+ if (arg_cpuset)
+ CPU_FREE(arg_cpuset);
+ arg_cpuset = TAKE_PTR(settings->cpuset);
+ arg_cpuset_ncpus = settings->cpuset_ncpus;
+ }
+ }
+
return 0;
}
@@ -4375,6 +4414,7 @@ finish:
expose_port_free_all(arg_expose_ports);
free(arg_root_hash);
rlimit_free_all(arg_rlimit);
+ arg_cpuset = cpu_set_mfree(arg_cpuset);
return r < 0 ? EXIT_FAILURE : ret;
}