summaryrefslogtreecommitdiff
path: root/core-cgroup-support-freezer.patch
diff options
context:
space:
mode:
Diffstat (limited to 'core-cgroup-support-freezer.patch')
-rw-r--r--core-cgroup-support-freezer.patch534
1 files changed, 534 insertions, 0 deletions
diff --git a/core-cgroup-support-freezer.patch b/core-cgroup-support-freezer.patch
new file mode 100644
index 0000000..093b89f
--- /dev/null
+++ b/core-cgroup-support-freezer.patch
@@ -0,0 +1,534 @@
+From 05a0f33b0d0a650b25ce7955a171d725f9c3f5f6 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 freezer.
+
+This patch add support for freezer subsystem.
+---
+ meson.build | 2 +
+ meson_options.txt | 3 ++
+ src/basic/cgroup-util.c | 1 +
+ src/basic/cgroup-util.h | 5 +++
+ src/core/cgroup.c | 16 +++++++
+ src/core/cgroup.h | 4 ++
+ src/core/dbus-cgroup.c | 29 +++++++++++++
+ src/core/dbus-manager.c | 1 +
+ src/core/load-fragment-gperf.gperf.in | 2 +
+ src/core/load-fragment.c | 33 ++++++++++++++
+ 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 | 11 +++++
+ src/test/meson.build | 3 ++
+ src/test/test-cgroup-freezer.c | 43 +++++++++++++++++++
+ src/test/test-cgroup-mask.c | 3 +-
+ .../fuzz-unit-file/directives-all.service | 2 +
+ 21 files changed, 164 insertions(+), 1 deletion(-)
+ create mode 100644 src/test/test-cgroup-freezer.c
+
+diff --git a/meson.build b/meson.build
+index 614013b..8712bdb 100644
+--- a/meson.build
++++ b/meson.build
+@@ -1582,6 +1582,7 @@ foreach term : ['analyze',
+ 'efi',
+ 'environment-d',
+ 'firstboot',
++ 'freezer-cgv1',
+ 'gshadow',
+ 'hibernate',
+ 'hostnamed',
+@@ -2855,6 +2856,7 @@ foreach tuple : [
+ ['standalone-binaries', get_option('standalone-binaries')],
+ ['coverage', get_option('b_coverage')],
+ ['cpuset-cgv1'],
++ ['freezer-cgv1'],
+ ]
+
+ if tuple.length() >= 2
+diff --git a/meson_options.txt b/meson_options.txt
+index 5fda5d9..b61d99d 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -517,3 +517,6 @@ option('bpf-framework', type : 'feature', deprecated : { 'true' : 'enabled', 'fa
+
+ option('cpuset-cgv1', type : 'boolean', value : 'true',
+ description : 'enable cgroup v1 cpuset support')
++
++option('freezer-cgv1', type : 'boolean', value : 'true',
++ description : 'enable cgroup v1 freezer support')
+diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
+index abd1f91..3e60488 100644
+--- a/src/basic/cgroup-util.c
++++ b/src/basic/cgroup-util.c
+@@ -2359,6 +2359,7 @@ static const char *const cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
+ [CGROUP_CONTROLLER_MEMORY] = "memory",
+ [CGROUP_CONTROLLER_DEVICES] = "devices",
+ [CGROUP_CONTROLLER_PIDS] = "pids",
++ [CGROUP_CONTROLLER_FREEZER] = "freezer",
+ [CGROUP_CONTROLLER_BPF_FIREWALL] = "bpf-firewall",
+ [CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices",
+ [CGROUP_CONTROLLER_BPF_FOREIGN] = "bpf-foreign",
+diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
+index dd3df28..4389cce 100644
+--- a/src/basic/cgroup-util.h
++++ b/src/basic/cgroup-util.h
+@@ -29,6 +29,7 @@ typedef enum CGroupController {
+ CGROUP_CONTROLLER_MEMORY,
+ CGROUP_CONTROLLER_DEVICES, /* v1 only */
+ CGROUP_CONTROLLER_PIDS,
++ CGROUP_CONTROLLER_FREEZER, /* v1 only */
+
+ /* BPF-based pseudo-controllers, v2 only */
+ CGROUP_CONTROLLER_BPF_FIREWALL,
+@@ -57,6 +58,7 @@ typedef enum CGroupMask {
+ CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY),
+ CGROUP_MASK_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_DEVICES),
+ CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS),
++ CGROUP_MASK_FREEZER = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_FREEZER),
+ CGROUP_MASK_BPF_FIREWALL = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FIREWALL),
+ CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES),
+ CGROUP_MASK_BPF_FOREIGN = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FOREIGN),
+@@ -67,6 +69,9 @@ typedef enum CGroupMask {
+ 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
++#if ENABLE_FREEZER_CGV1
++ | CGROUP_MASK_FREEZER
+ #endif
+ ,
+
+diff --git a/src/core/cgroup.c b/src/core/cgroup.c
+index cd1e97d..3e47f76 100644
+--- a/src/core/cgroup.c
++++ b/src/core/cgroup.c
+@@ -179,6 +179,7 @@ void cgroup_context_init(CGroupContext *c) {
+ .startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID,
+
+ .tasks_max = CGROUP_TASKS_MAX_UNSET,
++ .freezer_state_v1 = NULL,
+
+ .moom_swap = MANAGED_OOM_AUTO,
+ .moom_mem_pressure = MANAGED_OOM_AUTO,
+@@ -304,6 +305,9 @@ void cgroup_context_done(CGroupContext *c) {
+ cpu_set_reset(&c->cpuset_mems);
+ cpu_set_reset(&c->startup_cpuset_mems);
+
++ if (c->freezer_state_v1)
++ c->freezer_state_v1 = mfree(c->freezer_state_v1);
++
+ c->delegate_subgroup = mfree(c->delegate_subgroup);
+
+ nft_set_context_clear(&c->nft_set_context);
+@@ -542,6 +546,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
+ "%sBlockIOAccounting: %s\n"
+ "%sMemoryAccounting: %s\n"
+ "%sCPUSetAccounting: %s\n"
++ "%sFreezerAccounting=%s\n"
+ "%sTasksAccounting: %s\n"
+ "%sIPAccounting: %s\n"
+ "%sCPUWeight: %" PRIu64 "\n"
+@@ -577,6 +582,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
+ "%sCPUSetCloneChildren=%s\n"
+ "%sCPUSetMemMigrate=%s\n"
+ "%sTasksMax: %" PRIu64 "\n"
++ "%sFreezerState=%s\n"
+ "%sDevicePolicy: %s\n"
+ "%sDisableControllers: %s\n"
+ "%sDelegate: %s\n"
+@@ -591,6 +597,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
+ prefix, yes_no(c->blockio_accounting),
+ prefix, yes_no(c->memory_accounting),
+ prefix, yes_no(c->cpuset_accounting),
++ prefix, yes_no(c->freezer_accounting),
+ prefix, yes_no(c->tasks_accounting),
+ prefix, yes_no(c->ip_accounting),
+ prefix, c->cpu_weight,
+@@ -626,6 +633,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
+ prefix, yes_no(c->cpuset_clone_children),
+ prefix, yes_no(c->cpuset_memory_migrate),
+ prefix, cgroup_tasks_max_resolve(&c->tasks_max),
++ prefix, c->freezer_state_v1,
+ prefix, cgroup_device_policy_to_string(c->device_policy),
+ prefix, strempty(disable_controllers_str),
+ prefix, delegate_str,
+@@ -1957,6 +1965,11 @@ static void cgroup_context_apply(
+ }
+ }
+
++ if ((apply_mask & CGROUP_MASK_FREEZER) && !is_local_root && cg_all_unified() == 0) {
++ if (c->freezer_state_v1)
++ (void) set_attribute_and_warn(u, "freezer", "freezer.state", c->freezer_state_v1);
++ }
++
+ /* On cgroup v2 we can apply BPF everywhere. On cgroup v1 we apply it everywhere except for the root of
+ * containers, where we leave this to the manager */
+ if ((apply_mask & (CGROUP_MASK_DEVICES | CGROUP_MASK_BPF_DEVICES)) &&
+@@ -2115,6 +2128,9 @@ static CGroupMask unit_get_cgroup_mask(Unit *u) {
+ unit_has_unified_memory_config(u))
+ mask |= CGROUP_MASK_MEMORY;
+
++ if (c->freezer_accounting || c->freezer_state_v1)
++ mask |= CGROUP_MASK_FREEZER;
++
+ if (c->device_allow ||
+ c->device_policy != CGROUP_DEVICE_POLICY_AUTO)
+ mask |= CGROUP_MASK_DEVICES | CGROUP_MASK_BPF_DEVICES;
+diff --git a/src/core/cgroup.h b/src/core/cgroup.h
+index 04a7f25..7fb792a 100644
+--- a/src/core/cgroup.h
++++ b/src/core/cgroup.h
+@@ -135,6 +135,7 @@ struct CGroupContext {
+ bool blockio_accounting;
+ bool memory_accounting;
+ bool cpuset_accounting;
++ bool freezer_accounting;
+ bool tasks_accounting;
+ bool ip_accounting;
+
+@@ -228,6 +229,9 @@ struct CGroupContext {
+ /* Common */
+ CGroupTasksMax tasks_max;
+
++ /* Freezer */
++ char *freezer_state_v1;
++
+ /* Settings for systemd-oomd */
+ ManagedOOMMode moom_swap;
+ ManagedOOMMode moom_mem_pressure;
+diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
+index 05fd445..052049c 100644
+--- a/src/core/dbus-cgroup.c
++++ b/src/core/dbus-cgroup.c
+@@ -493,6 +493,8 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
+ 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("FreezerAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, freezer_accounting), 0),
++ SD_BUS_PROPERTY("FreezerState", "s", NULL, offsetof(CGroupContext, freezer_state_v1), 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),
+@@ -1287,6 +1289,9 @@ int bus_cgroup_set_property(
+ if (streq(name, "CPUSetAccounting"))
+ return bus_cgroup_set_boolean(u, name, &c->cpuset_accounting, CGROUP_MASK_CPUSET, message, flags, error);
+
++ if (streq(name, "FreezerAccounting"))
++ return bus_cgroup_set_boolean(u, name, &c->freezer_accounting, CGROUP_MASK_FREEZER, message, flags, error);
++
+ if (STR_IN_SET(name, "CPUSetCpus", "CPUSetMems")) {
+ const char *cpuset_str = NULL;
+
+@@ -1321,6 +1326,30 @@ int bus_cgroup_set_property(
+ if (streq(name, "CPUSetMemMigrate"))
+ return bus_cgroup_set_boolean(u, name, &c->cpuset_memory_migrate, CGROUP_MASK_CPUSET, message, flags, error);
+
++ if (streq(name, "FreezerState")) {
++ const char *state = NULL;
++
++ r = sd_bus_message_read(message, "s", &state);
++ if (r < 0)
++ return r;
++
++ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
++ unit_invalidate_cgroup(u, CGROUP_MASK_FREEZER);
++
++ if (c->freezer_state_v1) {
++ free(c->freezer_state_v1);
++ c->freezer_state_v1 = NULL;
++ }
++
++ c->freezer_state_v1 = strdup(state);
++ if (!c->freezer_state_v1)
++ return -ENOMEM;
++
++ unit_write_settingf(u, flags, name, "FreezerState=%s", state);
++ }
++ return 1;
++ }
++
+ 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 fc49e7d..0f9d4e8 100644
+--- a/src/core/dbus-manager.c
++++ b/src/core/dbus-manager.c
+@@ -3006,6 +3006,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
+ 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("DefaultFreezerAccounting", "b", bus_property_get_bool, offsetof(Manager, defaults.freezer_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 1e46af4..1e5b7ab 100644
+--- a/src/core/load-fragment-gperf.gperf.in
++++ b/src/core/load-fragment-gperf.gperf.in
+@@ -226,6 +226,8 @@
+ {{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}}.FreezerAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.freezer_accounting)
++{{type}}.FreezerState, config_parse_freezer_state, 0, offsetof({{type}}, cgroup_context.freezer_state_v1)
+ {{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 cbc75e1..8648fb1 100644
+--- a/src/core/load-fragment.c
++++ b/src/core/load-fragment.c
+@@ -3973,6 +3973,39 @@ int config_parse_cpuset_cpumems(
+ return 0;
+ }
+
++int config_parse_freezer_state(
++ 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 **freezer_state = data;
++ char *pinstr = NULL;
++
++ assert(filename);
++ assert(lvalue);
++ assert(rvalue);
++
++ if (!STR_IN_SET(rvalue, "FROZEN", "THAWED")) {
++ log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Freezer state '%s' is invalid, Ignoring.", rvalue);
++ return 0;
++ }
++
++ pinstr = strdup(rvalue);
++ if (!pinstr)
++ return log_oom();
++
++ free(*freezer_state);
++ *freezer_state = 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 0b77c8b..f9ffbf4 100644
+--- a/src/core/load-fragment.h
++++ b/src/core/load-fragment.h
+@@ -85,6 +85,7 @@ 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_freezer_state);
+ 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 de3f536..96b0a11 100644
+--- a/src/core/main.c
++++ b/src/core/main.c
+@@ -679,6 +679,7 @@ static int parse_config_file(void) {
+ { "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", "DefaultFreezerAccounting", config_parse_bool, 0, &arg_defaults.freezer_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 ef22fed..b29d4e1 100644
+--- a/src/core/manager.c
++++ b/src/core/manager.c
+@@ -4193,6 +4193,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.freezer_accounting = defaults->freezer_accounting;
+ m->defaults.io_accounting = defaults->io_accounting;
+ m->defaults.blockio_accounting = defaults->blockio_accounting;
+ m->defaults.tasks_accounting = defaults->tasks_accounting;
+@@ -4963,6 +4964,7 @@ void unit_defaults_init(UnitDefaults *defaults, RuntimeScope scope) {
+ .cpu_accounting = cpu_accounting_is_cheap(),
+ .memory_accounting = MEMORY_ACCOUNTING_DEFAULT,
+ .cpuset_accounting = false,
++ .freezer_accounting = false,
+ .io_accounting = false,
+ .blockio_accounting = false,
+ .tasks_accounting = true,
+diff --git a/src/core/manager.h b/src/core/manager.h
+index e560811..93e9d2a 100644
+--- a/src/core/manager.h
++++ b/src/core/manager.h
+@@ -166,6 +166,7 @@ typedef struct UnitDefaults {
+ bool io_accounting;
+ bool blockio_accounting;
+ bool cpuset_accounting;
++ bool freezer_accounting;
+ bool tasks_accounting;
+ bool ip_accounting;
+
+diff --git a/src/core/system.conf.in b/src/core/system.conf.in
+index 69ea5d6..dbdc47c 100644
+--- a/src/core/system.conf.in
++++ b/src/core/system.conf.in
+@@ -58,6 +58,7 @@
+ #DefaultIPAccounting=no
+ #DefaultMemoryAccounting={{ 'yes' if MEMORY_ACCOUNTING_DEFAULT else 'no' }}
+ #DefaultCpusetAccounting=
++#DefaultFreezerAccounting=no
+ #DefaultTasksAccounting=yes
+ #DefaultTasksMax=80%
+ #DefaultLimitCPU=
+diff --git a/src/core/unit.c b/src/core/unit.c
+index 38017d0..c069018 100644
+--- a/src/core/unit.c
++++ b/src/core/unit.c
+@@ -189,6 +189,7 @@ static void unit_init(Unit *u) {
+ 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->freezer_accounting = u->manager->defaults.freezer_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 a8f493e..6390986 100644
+--- a/src/shared/bus-unit-util.c
++++ b/src/shared/bus-unit-util.c
+@@ -568,6 +568,7 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
+ "IPAccounting",
+ "CoredumpReceive",
+ "CPUSetAccounting",
++ "FreezerAccounting",
+ "CPUSetCloneChildren",
+ "CPUSetMemMigrate"))
+ return bus_append_parse_boolean(m, field, eq);
+@@ -685,6 +686,16 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
+ return 1;
+ }
+
++ if (streq(field, "FreezerState")) {
++ if (STR_IN_SET(eq, "FROZEN", "THAWED"))
++ 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/test/meson.build b/src/test/meson.build
+index a59461a..a7ca76e 100644
+--- a/src/test/meson.build
++++ b/src/test/meson.build
+@@ -484,6 +484,9 @@ executables += [
+ 'sources' : files('test-cgroup-mask.c'),
+ 'dependencies' : common_test_dependencies,
+ },
++ core_test_template + {
++ 'sources' : files('test-cgroup-freezer.c'),
++ },
+ core_test_template + {
+ 'sources' : files('test-cgroup-unit-default.c'),
+ },
+diff --git a/src/test/test-cgroup-freezer.c b/src/test/test-cgroup-freezer.c
+new file mode 100644
+index 0000000..a533d16
+--- /dev/null
++++ b/src/test/test-cgroup-freezer.c
+@@ -0,0 +1,43 @@
++/* SPDX-License-Identifier: LGPL-2.1+ */
++
++#include "load-fragment.h"
++#include "string-util.h"
++
++static void test_config_parse_freezer_state(void) {
++ /* int config_parse_freezer_state(
++ 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 r;
++ _cleanup_free_ char *pstate = NULL;
++
++ r = config_parse_freezer_state(NULL, "fake", 1, "section", 1, "FreezerState", 0, "FROZEN", &pstate, NULL);
++ assert_se(r >= 0);
++ assert_se(streq(pstate, "FROZEN"));
++
++ pstate = mfree(pstate);
++ r = config_parse_freezer_state(NULL, "fake", 1, "section", 1, "FreezerState", 0, "THAWED", &pstate, NULL);
++ assert_se(r >= 0);
++ assert_se(streq(pstate, "THAWED"));
++
++ pstate = mfree(pstate);
++ r = config_parse_freezer_state(NULL, "fake", 1, "section", 1, "FreezerState", 0, "test", &pstate, NULL);
++ assert_se(r >= 0);
++ assert_se(!pstate);
++
++ r = config_parse_freezer_state(NULL, "fake", 1, "section", 1, "FreezerState", 0, "", &pstate, NULL);
++ assert_se(r >= 0);
++ assert_se(!pstate);
++}
++
++int main(int argc, char *argv[]){
++ test_config_parse_freezer_state();
++ return 0;
++}
+diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c
+index 37ec6d6..e0574d9 100644
+--- a/src/test/test-cgroup-mask.c
++++ b/src/test/test-cgroup-mask.c
+@@ -56,6 +56,7 @@ TEST_RET(cgroup_mask, .sd_booted = true) {
+ m->defaults.cpu_accounting =
+ m->defaults.memory_accounting =
+ m->defaults.cpuset_accounting =
++ m->defaults.freezer_accounting =
+ m->defaults.blockio_accounting =
+ m->defaults.io_accounting =
+ m->defaults.tasks_accounting = false;
+@@ -141,7 +142,7 @@ static void test_cg_mask_to_string_one(CGroupMask mask, const char *t) {
+
+ TEST(cg_mask_to_string) {
+ test_cg_mask_to_string_one(0, NULL);
+- test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids bpf-firewall bpf-devices bpf-foreign bpf-socket-bind bpf-restrict-network-interfaces");
++ test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids freezer bpf-firewall bpf-devices bpf-foreign bpf-socket-bind bpf-restrict-network-interfaces");
+ test_cg_mask_to_string_one(CGROUP_MASK_CPU, "cpu");
+ test_cg_mask_to_string_one(CGROUP_MASK_CPUACCT, "cpuacct");
+ test_cg_mask_to_string_one(CGROUP_MASK_CPUSET, "cpuset");
+diff --git a/test/fuzz/fuzz-unit-file/directives-all.service b/test/fuzz/fuzz-unit-file/directives-all.service
+index 0e953f2..123c98e 100644
+--- a/test/fuzz/fuzz-unit-file/directives-all.service
++++ b/test/fuzz/fuzz-unit-file/directives-all.service
+@@ -115,6 +115,8 @@ FileDescriptorName=
+ FileDescriptorStoreMax=
+ ForceUnmount=
+ FreeBind=
++FreezerAccounting=
++FreezerState=
+ Group=
+ GuessMainPID=
+ IOAccounting=
+--
+2.41.0
+