On Fri, Apr 24, 2026 at 12:36:44PM +0200, Michal Privoznik via Devel wrote:
From: Michal Privoznik <mprivozn@redhat.com>
With CGroupsV2 the freezer controller is split into two files:
1) cgroup.freeze where an integer is written to thaw(0)/freeze(1) processes within the cgroup, and 2) cgroup.events where the frozen status can be read.
Now, freezing/thawing a cgroup is as simple as writing corresponding integer into cgroup.freeze. But similarly to CGroupsV1, it may take some time to actually freeze all processes inside the cgroup. So read both file and map combination of their values according to this table:
| frozen from cgroup.events cgroup.freeze | 0 | 1 --------------+------------+------------- 0 | THAWED | N/A --+------------+------------- 1 | FREEZING | FROZEN
Resolves: https://gitlab.com/libvirt/libvirt/-/work_items/870 Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/util/vircgroupv2.c | 87 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+)
diff --git a/src/util/vircgroupv2.c b/src/util/vircgroupv2.c index 3fbd67fbda..1f2ba72c4d 100644 --- a/src/util/vircgroupv2.c +++ b/src/util/vircgroupv2.c @@ -1907,6 +1907,90 @@ virCgroupV2DenyAllDevices(virCgroup *group) }
+static int +virCgroupV2SetFreezerState(virCgroup *group, + virCgroupFreezerState state) +{ + unsigned long long val; + + switch (state) { + case VIR_CGROUP_FREEZER_STATE_THAWED: + val = 0; break; + case VIR_CGROUP_FREEZER_STATE_FROZEN: + val = 1; break;
Currently not in our coding style docs but I would prefer having break on separate line.
+ case VIR_CGROUP_FREEZER_STATE_FREEZING: + case VIR_CGROUP_FREEZER_STATE_LAST: + default: + virReportEnumRangeError(virCgroupFreezerState, state); + return -1; + } + + return virCgroupSetValueU64(group, + VIR_CGROUP_CONTROLLER_FREEZER, + "cgroup.freeze", + val); +} + + +static int +virCgroupV2GetFreezerState(virCgroup *group, + virCgroupFreezerState *state) +{ + unsigned long long freezeReqested; + g_autofree char *eventsStr = NULL; + const char *frozenStr = "frozen "; + const char *tmp; + + if (virCgroupGetValueU64(group, + VIR_CGROUP_CONTROLLER_FREEZER, + "cgroup.freeze", &freezeReqested) < 0) { + + return -1; + } + + if (freezeReqested == 0) { + /* No freeze is requested. */ + *state = VIR_CGROUP_FREEZER_STATE_THAWED; + return 0; + } else if (freezeReqested != 1) {
No need ot use `else if` as the previous condition returns if true.
+ virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown value of freezer controller: %1$llu"), + freezeReqested); + return -1; + } + + /* Now look at cgroup.events at 'frozen' state. If it's 0, then some + * processes inside the CGroup are still being frozen. If it's 1, + * then all processes are frozen. */ + if (virCgroupGetValueStr(group, + VIR_CGROUP_CONTROLLER_FREEZER, + "cgroup.events", &eventsStr) < 0) { + return -1; + } + + if (!eventsStr || + !(tmp = strstr(eventsStr, frozenStr))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Invalid format of cgroup.events file")); + return -1; + } + + tmp += strlen(frozenStr); + if (*tmp == '0') { + *state = VIR_CGROUP_FREEZER_STATE_FREEZING; + } else if (*tmp == '1') { + *state = VIR_CGROUP_FREEZER_STATE_FROZEN; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown state of freezer controller: %1$s"), + tmp); + return -1; + } + + return 0; +} + + virCgroupBackend virCgroupV2Backend = { .type = VIR_CGROUP_BACKEND_TYPE_V2,
@@ -1980,6 +2064,9 @@ virCgroupBackend virCgroupV2Backend = { .getCpusetMemoryMigrate = virCgroupV2GetCpusetMemoryMigrate, .setCpusetCpus = virCgroupV2SetCpusetCpus, .getCpusetCpus = virCgroupV2GetCpusetCpus, + + .setFreezerState = virCgroupV2SetFreezerState, + .getFreezerState = virCgroupV2GetFreezerState, };
-- 2.53.0