summaryrefslogtreecommitdiff
path: root/core-cgroup-support-cpuset.patch
diff options
context:
space:
mode:
Diffstat (limited to 'core-cgroup-support-cpuset.patch')
-rw-r--r--core-cgroup-support-cpuset.patch615
1 files changed, 615 insertions, 0 deletions
diff --git a/core-cgroup-support-cpuset.patch b/core-cgroup-support-cpuset.patch
new file mode 100644
index 0000000..84560b6
--- /dev/null
+++ b/core-cgroup-support-cpuset.patch
@@ -0,0 +1,615 @@
+From 8fc496f1e5b6d71d29eb446e02f6317bdc45c7c2 Mon Sep 17 00:00:00 2001
+From: licunlong <licunlong1@huawei.com>
+Date: Thu, 6 May 2021 09:38:54 +0800
+Subject: [PATCH] core-cgroup: support cpuset
+
+This patch add support for cpuset subsystem.
+---
+ meson.build | 2 +
+ meson_options.txt | 3 +
+ src/basic/cgroup-util.h | 8 ++-
+ src/basic/string-util.c | 42 +++++++++++
+ src/basic/string-util.h | 1 +
+ src/core/cgroup.c | 62 ++++++++++++++++-
+ src/core/cgroup.h | 6 ++
+ src/core/dbus-cgroup.c | 42 +++++++++++
+ src/core/dbus-manager.c | 1 +
+ src/core/load-fragment-gperf.gperf.in | 5 ++
+ src/core/load-fragment.c | 69 +++++++++++++++++++
+ src/core/load-fragment.h | 1 +
+ src/core/main.c | 1 +
+ src/core/manager.c | 2 +
+ src/core/manager.h | 1 +
+ src/core/system.conf.in | 1 +
+ src/core/unit.c | 1 +
+ src/shared/bus-unit-util.c | 15 +++-
+ src/shared/cpu-set-util.c | 1 +
+ src/test/test-cgroup-mask.c | 1 +
+ .../fuzz-unit-file/directives-all.service | 5 ++
+ 21 files changed, 264 insertions(+), 6 deletions(-)
+
+diff --git a/meson.build b/meson.build
+index 7419e2b..614013b 100644
+--- a/meson.build
++++ b/meson.build
+@@ -1578,6 +1578,7 @@ foreach term : ['analyze',
+ 'binfmt',
+ 'compat-mutable-uid-boundaries',
+ 'coredump',
++ 'cpuset-cgv1',
+ 'efi',
+ 'environment-d',
+ 'firstboot',
+@@ -2853,6 +2854,7 @@ foreach tuple : [
+ ['fexecve'],
+ ['standalone-binaries', get_option('standalone-binaries')],
+ ['coverage', get_option('b_coverage')],
++ ['cpuset-cgv1'],
+ ]
+
+ if tuple.length() >= 2
+diff --git a/meson_options.txt b/meson_options.txt
+index e708745..5fda5d9 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -514,3 +514,6 @@ option('bpf-compiler', type : 'combo', choices : ['clang', 'gcc'],
+ description: 'compiler used to build BPF programs')
+ option('bpf-framework', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
+ description: 'build BPF programs from source code in restricted C')
++
++option('cpuset-cgv1', type : 'boolean', value : 'true',
++ description : 'enable cgroup v1 cpuset support')
+diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
+index 6ab14c7..831f8ce 100644
+--- a/src/basic/cgroup-util.h
++++ b/src/basic/cgroup-util.h
+@@ -22,7 +22,7 @@ typedef enum CGroupController {
+ /* Original cgroup controllers */
+ CGROUP_CONTROLLER_CPU,
+ CGROUP_CONTROLLER_CPUACCT, /* v1 only */
+- CGROUP_CONTROLLER_CPUSET, /* v2 only */
++ CGROUP_CONTROLLER_CPUSET,
+ CGROUP_CONTROLLER_IO, /* v2 only */
+ CGROUP_CONTROLLER_BLKIO, /* v1 only */
+ CGROUP_CONTROLLER_MEMORY,
+@@ -62,7 +62,11 @@ typedef enum CGroupMask {
+ CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES),
+
+ /* All real cgroup v1 controllers */
+- CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS,
++ CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS
++#if ENABLE_CPUSET_CGV1
++ | CGROUP_MASK_CPUSET
++#endif
++ ,
+
+ /* All real cgroup v2 controllers */
+ CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
+diff --git a/src/basic/string-util.c b/src/basic/string-util.c
+index 7329bfa..0fecb40 100644
+--- a/src/basic/string-util.c
++++ b/src/basic/string-util.c
+@@ -1295,6 +1295,48 @@ int string_contains_word_strv(const char *string, const char *separators, char *
+ return !!found;
+ }
+
++int string_isvalid_interval(const char *instr)
++{
++ const char *pstr = instr; /* tmp */
++ const char *pstr_front = instr; /* front char */
++ const char *pstr_behind = instr; /* behind char */
++
++ if (isempty(instr))
++ {
++ return 1;
++ }
++
++ while (*pstr != '\0')
++ {
++ /* behind */
++ pstr_behind = pstr + 1;
++
++ /* 0-3,4,6,7-10 */
++ if (((*pstr < '0') || (*pstr > '9')) &&
++ (*pstr != '-') &&
++ (*pstr != ','))
++ {
++ return 2;
++ }
++
++ /* - , must is a num */
++ if (('-' == *pstr) || (',' == *pstr))
++ {
++ if ((*pstr_front < '0') || (*pstr_front > '9') ||
++ (*pstr_behind < '0') || (*pstr_behind > '9'))
++ {
++ return 3;
++ }
++ }
++
++ /* front */
++ pstr_front = pstr;
++ pstr++;
++ }
++
++ return 0;
++}
++
+ bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
+ if (!s1 && !s2)
+ return true;
+diff --git a/src/basic/string-util.h b/src/basic/string-util.h
+index b6d8be3..c6773d3 100644
+--- a/src/basic/string-util.h
++++ b/src/basic/string-util.h
+@@ -270,6 +270,7 @@ static inline int string_contains_word(const char *string, const char *separator
+ return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL);
+ }
+
++int string_isvalid_interval(const char *instr);
+ bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok);
+
+ char *string_replace_char(char *str, char old_char, char new_char);
+diff --git a/src/core/cgroup.c b/src/core/cgroup.c
+index 78bc551..3154fd3 100644
+--- a/src/core/cgroup.c
++++ b/src/core/cgroup.c
+@@ -293,6 +293,12 @@ void cgroup_context_done(CGroupContext *c) {
+
+ c->restrict_network_interfaces = set_free_free(c->restrict_network_interfaces);
+
++ if (c->cpuset_cpus_v1)
++ c->cpuset_cpus_v1 = mfree(c->cpuset_cpus_v1);
++
++ if (c->cpuset_mems_v1)
++ c->cpuset_mems_v1 = mfree(c->cpuset_mems_v1);
++
+ cpu_set_reset(&c->cpuset_cpus);
+ cpu_set_reset(&c->startup_cpuset_cpus);
+ cpu_set_reset(&c->cpuset_mems);
+@@ -535,6 +541,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
+ "%sIOAccounting: %s\n"
+ "%sBlockIOAccounting: %s\n"
+ "%sMemoryAccounting: %s\n"
++ "%sCPUSetAccounting: %s\n"
+ "%sTasksAccounting: %s\n"
+ "%sIPAccounting: %s\n"
+ "%sCPUWeight: %" PRIu64 "\n"
+@@ -565,6 +572,10 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
+ "%sMemoryZSwapMax: %" PRIu64 "%s\n"
+ "%sStartupMemoryZSwapMax: %" PRIu64 "%s\n"
+ "%sMemoryLimit: %" PRIu64 "\n"
++ "%sCPUSetCpus=%s\n"
++ "%sCPUSetMems=%s\n"
++ "%sCPUSetCloneChildren=%s\n"
++ "%sCPUSetMemMigrate=%s\n"
+ "%sTasksMax: %" PRIu64 "\n"
+ "%sDevicePolicy: %s\n"
+ "%sDisableControllers: %s\n"
+@@ -579,6 +590,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
+ prefix, yes_no(c->io_accounting),
+ prefix, yes_no(c->blockio_accounting),
+ prefix, yes_no(c->memory_accounting),
++ prefix, yes_no(c->cpuset_accounting),
+ prefix, yes_no(c->tasks_accounting),
+ prefix, yes_no(c->ip_accounting),
+ prefix, c->cpu_weight,
+@@ -609,6 +621,10 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
+ prefix, c->memory_zswap_max, format_cgroup_memory_limit_comparison(cdj, sizeof(cdj), u, "MemoryZSwapMax"),
+ prefix, c->startup_memory_zswap_max, format_cgroup_memory_limit_comparison(cdk, sizeof(cdk), u, "StartupMemoryZSwapMax"),
+ prefix, c->memory_limit,
++ prefix, c->cpuset_cpus_v1,
++ prefix, c->cpuset_mems_v1,
++ prefix, yes_no(c->cpuset_clone_children),
++ prefix, yes_no(c->cpuset_memory_migrate),
+ prefix, cgroup_tasks_max_resolve(&c->tasks_max),
+ prefix, cgroup_device_policy_to_string(c->device_policy),
+ prefix, strempty(disable_controllers_str),
+@@ -1728,8 +1744,47 @@ static void cgroup_context_apply(
+ }
+
+ if ((apply_mask & CGROUP_MASK_CPUSET) && !is_local_root) {
+- cgroup_apply_unified_cpuset(u, cgroup_context_allowed_cpus(c, state), "cpuset.cpus");
+- cgroup_apply_unified_cpuset(u, cgroup_context_allowed_mems(c, state), "cpuset.mems");
++ if (cg_all_unified() == 0) {
++ (void) set_attribute_and_warn(u, "cpuset", "cgroup.clone_children", one_zero(c->cpuset_clone_children));
++ (void) set_attribute_and_warn(u, "cpuset", "cpuset.memory_migrate", one_zero(c->cpuset_memory_migrate));
++ if (c->cpuset_cpus_v1) {
++ if (streq(c->cpuset_cpus_v1, "all")) {
++ _cleanup_free_ char *str_cpuset_cpus = NULL;
++ _cleanup_free_ char *cg_root_path_cpus = NULL;
++ r = cg_get_root_path(&cg_root_path_cpus);
++ if (r < 0)
++ log_info_errno(r, "Failed to determine root cgroup, ignoring cgroup cpuset cpus: %m");
++ if (cg_root_path_cpus) {
++ r = cg_get_attribute("cpuset", cg_root_path_cpus, "cpuset.cpus", &str_cpuset_cpus);
++ if (r < 0)
++ log_error("cgroup context apply: cg get attribute is error(%d), path=%s.", r, cg_root_path_cpus);
++ if (str_cpuset_cpus)
++ (void) set_attribute_and_warn(u, "cpuset", "cpuset.cpus", str_cpuset_cpus);
++ }
++ } else
++ (void) set_attribute_and_warn(u, "cpuset", "cpuset.cpus", c->cpuset_cpus_v1);
++ }
++ if (c->cpuset_mems_v1) {
++ if (streq(c->cpuset_mems_v1, "all")) {
++ _cleanup_free_ char *str_cpuset_mems = NULL;
++ _cleanup_free_ char *cg_root_path_mems = NULL;
++ r = cg_get_root_path(&cg_root_path_mems);
++ if (r < 0)
++ log_info_errno(r, "Failed to determine root cgroup, ignoring cgroup cpuset mems: %m");
++ if (cg_root_path_mems) {
++ r = cg_get_attribute("cpuset", cg_root_path_mems, "cpuset.mems", &str_cpuset_mems);
++ if (r < 0)
++ log_error("cgroup context apply: cg get attribute is error(%d), path=%s.", r, cg_root_path_mems);
++ if (str_cpuset_mems)
++ (void) set_attribute_and_warn(u, "cpuset", "cpuset.mems", str_cpuset_mems);
++ }
++ } else
++ (void) set_attribute_and_warn(u, "cpuset", "cpuset.mems", c->cpuset_mems_v1);
++ }
++ } else {
++ cgroup_apply_unified_cpuset(u, cgroup_context_allowed_cpus(c, state), "cpuset.cpus");
++ cgroup_apply_unified_cpuset(u, cgroup_context_allowed_mems(c, state), "cpuset.mems");
++ }
+ }
+
+ /* The 'io' controller attributes are not exported on the host's root cgroup (being a pure cgroup v2
+@@ -2044,7 +2099,8 @@ static CGroupMask unit_get_cgroup_mask(Unit *u) {
+ c->cpu_quota_per_sec_usec != USEC_INFINITY)
+ mask |= CGROUP_MASK_CPU;
+
+- if (cgroup_context_has_allowed_cpus(c) || cgroup_context_has_allowed_mems(c))
++ if (cgroup_context_has_allowed_cpus(c) || cgroup_context_has_allowed_mems(c) ||
++ c->cpuset_accounting || c->cpuset_cpus_v1 || c->cpuset_mems_v1)
+ mask |= CGROUP_MASK_CPUSET;
+
+ if (cgroup_context_has_io_config(c) || cgroup_context_has_blockio_config(c))
+diff --git a/src/core/cgroup.h b/src/core/cgroup.h
+index f1b674b..a4bd959 100644
+--- a/src/core/cgroup.h
++++ b/src/core/cgroup.h
+@@ -134,6 +134,7 @@ struct CGroupContext {
+ bool io_accounting;
+ bool blockio_accounting;
+ bool memory_accounting;
++ bool cpuset_accounting;
+ bool tasks_accounting;
+ bool ip_accounting;
+
+@@ -177,6 +178,11 @@ struct CGroupContext {
+ uint64_t memory_zswap_max;
+ uint64_t startup_memory_zswap_max;
+
++ char *cpuset_cpus_v1;
++ char *cpuset_mems_v1;
++ bool cpuset_clone_children;
++ bool cpuset_memory_migrate;
++
+ bool default_memory_min_set:1;
+ bool default_memory_low_set:1;
+ bool default_startup_memory_low_set:1;
+diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
+index 4237e69..e82d56e 100644
+--- a/src/core/dbus-cgroup.c
++++ b/src/core/dbus-cgroup.c
+@@ -488,6 +488,11 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
+ SD_BUS_PROPERTY("MemoryZSwapMax", "t", NULL, offsetof(CGroupContext, memory_zswap_max), 0),
+ SD_BUS_PROPERTY("StartupMemoryZSwapMax", "t", NULL, offsetof(CGroupContext, startup_memory_zswap_max), 0),
+ SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
++ SD_BUS_PROPERTY("CPUSetAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpuset_accounting), 0),
++ SD_BUS_PROPERTY("CPUSetCpus", "s", NULL, offsetof(CGroupContext, cpuset_cpus_v1), 0),
++ SD_BUS_PROPERTY("CPUSetMems", "s", NULL, offsetof(CGroupContext, cpuset_mems_v1), 0),
++ SD_BUS_PROPERTY("CPUSetCloneChildren", "b", bus_property_get_bool, offsetof(CGroupContext, cpuset_clone_children), 0),
++ SD_BUS_PROPERTY("CPUSetMemMigrate", "b", bus_property_get_bool, offsetof(CGroupContext, cpuset_memory_migrate), 0),
+ SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
+ SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
+ SD_BUS_PROPERTY("TasksAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, tasks_accounting), 0),
+@@ -1279,6 +1284,43 @@ int bus_cgroup_set_property(
+ if (streq(name, "MemoryLimitScale"))
+ return bus_cgroup_set_memory_scale(u, name, &c->memory_limit, message, flags, error);
+
++ if (streq(name, "CPUSetAccounting"))
++ return bus_cgroup_set_boolean(u, name, &c->cpuset_accounting, CGROUP_MASK_CPUSET, message, flags, error);
++
++ if (STR_IN_SET(name, "CPUSetCpus", "CPUSetMems")) {
++ const char *cpuset_str = NULL;
++
++ r = sd_bus_message_read(message, "s", &cpuset_str);
++ if (r < 0)
++ return r;
++
++ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
++ unit_invalidate_cgroup(u, CGROUP_MASK_CPUSET);
++ if (streq(name, "CPUSetCpus")) {
++ if (c->cpuset_cpus_v1)
++ c->cpuset_cpus_v1 = mfree(c->cpuset_cpus_v1);
++ c->cpuset_cpus_v1 = strdup(cpuset_str);
++ if (!c->cpuset_cpus_v1)
++ return -ENOMEM;
++ unit_write_settingf(u, flags, name, "CPUSetCpus=%s", cpuset_str);
++ } else {
++ if (c->cpuset_mems_v1)
++ c->cpuset_mems_v1 = mfree(c->cpuset_mems_v1);
++ c->cpuset_mems_v1 = strdup(cpuset_str);
++ if (!c->cpuset_mems_v1)
++ return -ENOMEM;
++ unit_write_settingf(u, flags, name, "CPUSetMems=%s", cpuset_str);
++ }
++ }
++ return 1;
++ }
++
++ if (streq(name, "CPUSetCloneChildren"))
++ return bus_cgroup_set_boolean(u, name, &c->cpuset_clone_children, CGROUP_MASK_CPUSET, message, flags, error);
++
++ if (streq(name, "CPUSetMemMigrate"))
++ return bus_cgroup_set_boolean(u, name, &c->cpuset_memory_migrate, CGROUP_MASK_CPUSET, message, flags, error);
++
+ if (streq(name, "TasksAccounting"))
+ return bus_cgroup_set_boolean(u, name, &c->tasks_accounting, CGROUP_MASK_PIDS, message, flags, error);
+
+diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
+index 745f5cc..fc49e7d 100644
+--- a/src/core/dbus-manager.c
++++ b/src/core/dbus-manager.c
+@@ -3005,6 +3005,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
+ SD_BUS_PROPERTY("DefaultIOAccounting", "b", bus_property_get_bool, offsetof(Manager, defaults.io_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultIPAccounting", "b", bus_property_get_bool, offsetof(Manager, defaults.ip_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultMemoryAccounting", "b", bus_property_get_bool, offsetof(Manager, defaults.memory_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
++ SD_BUS_PROPERTY("DefaultCpusetAccounting", "b", bus_property_get_bool, offsetof(Manager, defaults.cpuset_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultTasksAccounting", "b", bus_property_get_bool, offsetof(Manager, defaults.tasks_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitCPU", "t", bus_property_get_rlimit, offsetof(Manager, defaults.rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitCPUSoft", "t", bus_property_get_rlimit, offsetof(Manager, defaults.rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
+diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
+index 45f9ab0..62c4027 100644
+--- a/src/core/load-fragment-gperf.gperf.in
++++ b/src/core/load-fragment-gperf.gperf.in
+@@ -221,6 +221,11 @@
+ {{type}}.MemoryZSwapMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+ {{type}}.StartupMemoryZSwapMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+ {{type}}.MemoryLimit, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
++{{type}}.CPUSetAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.cpuset_accounting)
++{{type}}.CPUSetCpus, config_parse_cpuset_cpumems, 0, offsetof({{type}}, cgroup_context.cpuset_cpus_v1)
++{{type}}.CPUSetMems, config_parse_cpuset_cpumems, 0, offsetof({{type}}, cgroup_context.cpuset_mems_v1)
++{{type}}.CPUSetCloneChildren, config_parse_bool, 0, offsetof({{type}}, cgroup_context.cpuset_clone_children)
++{{type}}.CPUSetMemMigrate, config_parse_bool, 0, offsetof({{type}}, cgroup_context.cpuset_memory_migrate)
+ {{type}}.DeviceAllow, config_parse_device_allow, 0, offsetof({{type}}, cgroup_context)
+ {{type}}.DevicePolicy, config_parse_device_policy, 0, offsetof({{type}}, cgroup_context.device_policy)
+ {{type}}.IOAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.io_accounting)
+diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
+index 6e3a22b..cbc75e1 100644
+--- a/src/core/load-fragment.c
++++ b/src/core/load-fragment.c
+@@ -3904,6 +3904,75 @@ int config_parse_memory_limit(
+ return 0;
+ }
+
++int config_parse_cpuset_cpumems(
++ 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)
++{
++ char **pcpumems = data;
++ char *pinstr = NULL;
++ int iret = 0;
++
++ assert(filename);
++ assert(lvalue);
++ assert(rvalue);
++ assert(data);
++ (void)section;
++ (void)section_line;
++ (void)ltype;
++ (void)userdata;
++
++ if (!utf8_is_valid(rvalue))
++ {
++ log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
++ return 0;
++ }
++
++ if (0 == strcmp(rvalue, "all"))
++ {
++ pinstr = strdup(rvalue);
++ if (!pinstr)
++ {
++ return log_oom();
++ }
++
++ free(*pcpumems);
++ *pcpumems = pinstr;
++
++ return 0;
++ }
++
++ /* 0-2,4 */
++ iret = string_isvalid_interval(rvalue);
++ if (0 != iret)
++ {
++ pinstr = NULL;
++ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
++ "cpuset cpumems '%s' is invalid, Ignoring(%d).",
++ rvalue, iret);
++ }
++ else
++ {
++ pinstr = strdup(rvalue);
++ if (!pinstr)
++ {
++ return log_oom();
++ }
++ }
++
++ free(*pcpumems);
++ *pcpumems = pinstr;
++
++ return 0;
++}
++
+ int config_parse_tasks_max(
+ const char *unit,
+ const char *filename,
+diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
+index 6919805..0b77c8b 100644
+--- a/src/core/load-fragment.h
++++ b/src/core/load-fragment.h
+@@ -84,6 +84,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_cg_weight);
+ CONFIG_PARSER_PROTOTYPE(config_parse_cg_cpu_weight);
+ CONFIG_PARSER_PROTOTYPE(config_parse_cpu_shares);
+ CONFIG_PARSER_PROTOTYPE(config_parse_memory_limit);
++CONFIG_PARSER_PROTOTYPE(config_parse_cpuset_cpumems);
+ CONFIG_PARSER_PROTOTYPE(config_parse_tasks_max);
+ CONFIG_PARSER_PROTOTYPE(config_parse_delegate);
+ CONFIG_PARSER_PROTOTYPE(config_parse_delegate_subgroup);
+diff --git a/src/core/main.c b/src/core/main.c
+index bfdcc13..724593a 100644
+--- a/src/core/main.c
++++ b/src/core/main.c
+@@ -678,6 +678,7 @@ static int parse_config_file(void) {
+ { "Manager", "DefaultIPAccounting", config_parse_bool, 0, &arg_defaults.ip_accounting },
+ { "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_defaults.blockio_accounting },
+ { "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_defaults.memory_accounting },
++ { "Manager", "DefaultCpusetAccounting", config_parse_bool, 0, &arg_defaults.cpuset_accounting },
+ { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_defaults.tasks_accounting },
+ { "Manager", "DefaultTasksMax", config_parse_tasks_max, 0, &arg_defaults.tasks_max },
+ { "Manager", "DefaultMemoryPressureThresholdSec", config_parse_sec, 0, &arg_defaults.memory_pressure_threshold_usec },
+diff --git a/src/core/manager.c b/src/core/manager.c
+index ce20d6b..ef22fed 100644
+--- a/src/core/manager.c
++++ b/src/core/manager.c
+@@ -4192,6 +4192,7 @@ int manager_set_unit_defaults(Manager *m, const UnitDefaults *defaults) {
+
+ m->defaults.cpu_accounting = defaults->cpu_accounting;
+ m->defaults.memory_accounting = defaults->memory_accounting;
++ m->defaults.cpuset_accounting = defaults->cpuset_accounting;
+ m->defaults.io_accounting = defaults->io_accounting;
+ m->defaults.blockio_accounting = defaults->blockio_accounting;
+ m->defaults.tasks_accounting = defaults->tasks_accounting;
+@@ -4961,6 +4962,7 @@ void unit_defaults_init(UnitDefaults *defaults, RuntimeScope scope) {
+ * controller to be enabled, so the default is to enable it unless we got told otherwise. */
+ .cpu_accounting = cpu_accounting_is_cheap(),
+ .memory_accounting = MEMORY_ACCOUNTING_DEFAULT,
++ .cpuset_accounting = false,
+ .io_accounting = false,
+ .blockio_accounting = false,
+ .tasks_accounting = true,
+diff --git a/src/core/manager.h b/src/core/manager.h
+index d96eb7b..e560811 100644
+--- a/src/core/manager.h
++++ b/src/core/manager.h
+@@ -165,6 +165,7 @@ typedef struct UnitDefaults {
+ bool memory_accounting;
+ bool io_accounting;
+ bool blockio_accounting;
++ bool cpuset_accounting;
+ bool tasks_accounting;
+ bool ip_accounting;
+
+diff --git a/src/core/system.conf.in b/src/core/system.conf.in
+index 90109ad..69ea5d6 100644
+--- a/src/core/system.conf.in
++++ b/src/core/system.conf.in
+@@ -57,6 +57,7 @@
+ #DefaultIOAccounting=no
+ #DefaultIPAccounting=no
+ #DefaultMemoryAccounting={{ 'yes' if MEMORY_ACCOUNTING_DEFAULT else 'no' }}
++#DefaultCpusetAccounting=
+ #DefaultTasksAccounting=yes
+ #DefaultTasksMax=80%
+ #DefaultLimitCPU=
+diff --git a/src/core/unit.c b/src/core/unit.c
+index 3d60904..e38a535 100644
+--- a/src/core/unit.c
++++ b/src/core/unit.c
+@@ -188,6 +188,7 @@ static void unit_init(Unit *u) {
+ cc->io_accounting = u->manager->defaults.io_accounting;
+ cc->blockio_accounting = u->manager->defaults.blockio_accounting;
+ cc->memory_accounting = u->manager->defaults.memory_accounting;
++ cc->cpuset_accounting = u->manager->defaults.cpuset_accounting;
+ cc->tasks_accounting = u->manager->defaults.tasks_accounting;
+ cc->ip_accounting = u->manager->defaults.ip_accounting;
+
+diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
+index 4ee9706..a8f493e 100644
+--- a/src/shared/bus-unit-util.c
++++ b/src/shared/bus-unit-util.c
+@@ -566,7 +566,10 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
+ "BlockIOAccounting",
+ "TasksAccounting",
+ "IPAccounting",
+- "CoredumpReceive"))
++ "CoredumpReceive",
++ "CPUSetAccounting",
++ "CPUSetCloneChildren",
++ "CPUSetMemMigrate"))
+ return bus_append_parse_boolean(m, field, eq);
+
+ if (STR_IN_SET(field, "CPUWeight",
+@@ -672,6 +675,16 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
+ return bus_append_parse_size(m, field, eq, 1024);
+ }
+
++ if (STR_IN_SET(field, "CPUSetCpus", "CPUSetMems")) {
++ if (string_isvalid_interval(eq) == 0 || streq(eq, "all"))
++ r = sd_bus_message_append(m, "(sv)", field, "s", eq);
++ else
++ r = -EINVAL;
++ if (r < 0)
++ return bus_log_create_error(r);
++ return 1;
++ }
++
+ if (streq(field, "CPUQuota")) {
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
+diff --git a/src/shared/cpu-set-util.c b/src/shared/cpu-set-util.c
+index d096576..356a46a 100644
+--- a/src/shared/cpu-set-util.c
++++ b/src/shared/cpu-set-util.c
+@@ -7,6 +7,7 @@
+
+ #include "alloc-util.h"
+ #include "cpu-set-util.h"
++#include "cgroup-util.h"
+ #include "dirent-util.h"
+ #include "errno-util.h"
+ #include "extract-word.h"
+diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c
+index bfc8fac..5dd569f 100644
+--- a/src/test/test-cgroup-mask.c
++++ b/src/test/test-cgroup-mask.c
+@@ -55,6 +55,7 @@ TEST_RET(cgroup_mask, .sd_booted = true) {
+ * else. */
+ m->defaults.cpu_accounting =
+ m->defaults.memory_accounting =
++ m->defaults.cpuset_accounting =
+ m->defaults.blockio_accounting =
+ m->defaults.io_accounting =
+ m->defaults.tasks_accounting = false;
+diff --git a/test/fuzz/fuzz-unit-file/directives-all.service b/test/fuzz/fuzz-unit-file/directives-all.service
+index 4bdc48a..0e953f2 100644
+--- a/test/fuzz/fuzz-unit-file/directives-all.service
++++ b/test/fuzz/fuzz-unit-file/directives-all.service
+@@ -52,6 +52,11 @@ BusName=
+ CoredumpFilter=
+ CPUAccounting=
+ CPUQuota=
++CPUSetAccounting=
++CPUSetCloneChildren=
++CPUSetCpus=
++CPUSetMemMigrate=
++CPUSetMems=
+ CPUShares=
+ CPUWeight=
+ CapabilityBoundingSet=
+--
+2.23.0
+