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; }