[PATCH RFC v3 19/90] cgroup: add cgroup_cell_kill()

From: Josef Bacik

Date: Thu Nov 05 2026 - 10:06:43 EST


Killing a cell means killing everything in its budget. Add
cgroup_cell_kill(). It does what a write to cgroup.kill does, which
includes forks that are in flight. It also reports whether the cgroup is
still populated, so that a cell is only reported dead once nothing is left
in it.

It uses the populated state of the cgroup rather than a task iterator. An
iterator skips a task that is exiting, but such a task keeps its cgroup
populated until it has been scheduled out for the last time.
cgroup_destroy_locked() uses the populated state too, and it is called
next, for the cgroups that the cell created below its budget.

Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
---
include/linux/cgroup.h | 1 +
kernel/cgroup/cgroup.c | 24 ++++++++++++++++++++++++
2 files changed, 25 insertions(+)

diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index 4d9baf195f1e..57a4037671c9 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -155,6 +155,7 @@ struct cgroup *cgroup_get_from_fd(int fd);

struct cgroup *cgroup_cell_budget_get(struct file *file, kuid_t *uid, kgid_t *gid);
int cgroup_cell_chown(struct cgroup *cgrp, kuid_t uid, kgid_t gid);
+bool cgroup_cell_kill(struct cgroup *cgrp);
int cgroup_cell_budget_put(struct cgroup *cgrp, bool handed, kuid_t uid, kgid_t gid);
struct cgroup *cgroup_v1v2_get_from_fd(int fd);

diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index e5e6761522eb..75f8367271aa 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -7199,6 +7199,30 @@ int cgroup_cell_chown(struct cgroup *cgrp, kuid_t uid, kgid_t gid)
}
return ret;
}
+
+/**
+ * cgroup_cell_kill - kill everything in a cgroup and below
+ * @cgrp: the cgroup
+ *
+ * As a write to cgroup.kill. It closes against forks in flight by kill_seq.
+ *
+ * Return: whether the cgroup is still populated, as cgroup.events has it. A
+ * task counts until it has been switched out for the last time, see
+ * cgroup_task_dead(). That is longer than a task iterator shows it, and it is
+ * what cgroup_destroy_locked() goes by.
+ */
+bool cgroup_cell_kill(struct cgroup *cgrp)
+{
+ bool busy = false;
+
+ cgroup_lock();
+ if (!cgroup_is_dead(cgrp)) {
+ cgroup_kill(cgrp);
+ busy = cgroup_is_populated(cgrp);
+ }
+ cgroup_unlock();
+ return busy;
+}
#endif /* CONFIG_CELLS */

/**

--
2.55.0