[PATCH RFC v3 20/90] cgroup: root a cgroup namespace at a given cgroup

From: Josef Bacik

Date: Thu Nov 05 2026 - 11:09:08 EST


copy_cgroup_ns() roots the new cgroup namespace at the cgroup of the
caller. The builder of a cell is not in the budget of the cell and never
will be.

Add cgroup_cell_ns(), which creates a cgroup namespace that is rooted at a
given cgroup, and cgroup_cell_cset(), which finds the css_set that it
needs.

Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
---
include/linux/cgroup.h | 3 +++
kernel/cgroup/cgroup.c | 17 +++++++++++++++++
kernel/cgroup/namespace.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 64 insertions(+)

diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index 57a4037671c9..af467b402448 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -154,6 +154,9 @@ struct cgroup *cgroup_get_from_path(const char *path);
struct cgroup *cgroup_get_from_fd(int fd);

struct cgroup *cgroup_cell_budget_get(struct file *file, kuid_t *uid, kgid_t *gid);
+struct css_set *cgroup_cell_cset(struct cgroup *cgrp);
+struct cgroup_namespace *cgroup_cell_ns(struct user_namespace *user_ns,
+ struct cgroup *cgrp);
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);
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 75f8367271aa..051b220e2850 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -7163,6 +7163,23 @@ int cgroup_cell_budget_put(struct cgroup *cgrp, bool handed, kuid_t uid, kgid_t
return ret;
}

+/**
+ * cgroup_cell_cset - the css_set a cgroup namespace rooted at a cgroup needs
+ * @cgrp: a cgroup on the default hierarchy
+ *
+ * Return: a css_set that has @cgrp on the default hierarchy and the root on
+ * every other, with a reference, or NULL if @cgrp is dead or memory is short.
+ */
+struct css_set *cgroup_cell_cset(struct cgroup *cgrp)
+{
+ struct css_set *cset;
+
+ cgroup_lock();
+ cset = cgroup_is_dead(cgrp) ? NULL : find_css_set(&init_css_set, cgrp);
+ cgroup_unlock();
+ return cset;
+}
+
/**
* cgroup_cell_chown - hand a cgroup to somebody
* @cgrp: the cgroup
diff --git a/kernel/cgroup/namespace.c b/kernel/cgroup/namespace.c
index 76660332ec64..987ec8cd5db6 100644
--- a/kernel/cgroup/namespace.c
+++ b/kernel/cgroup/namespace.c
@@ -87,6 +87,50 @@ struct cgroup_namespace *copy_cgroup_ns(u64 flags,
return new_ns;
}

+#ifdef CONFIG_CELLS
+/**
+ * cgroup_cell_ns - make a cgroup namespace rooted at a given cgroup
+ * @user_ns: the user namespace that will own it
+ * @cgrp: the cgroup, on the default hierarchy
+ *
+ * copy_cgroup_ns() roots the namespace wherever the caller happens to be. This
+ * is for a cell, whose builder is not in the budget.
+ *
+ * Return: the namespace with a reference, or an ERR_PTR().
+ */
+struct cgroup_namespace *cgroup_cell_ns(struct user_namespace *user_ns,
+ struct cgroup *cgrp)
+{
+ struct cgroup_namespace *new_ns;
+ struct ucounts *ucounts;
+ struct css_set *cset;
+
+ ucounts = inc_cgroup_namespaces(user_ns);
+ if (!ucounts)
+ return ERR_PTR(-ENOSPC);
+
+ cset = cgroup_cell_cset(cgrp);
+ if (!cset) {
+ dec_cgroup_namespaces(ucounts);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ new_ns = alloc_cgroup_ns();
+ if (IS_ERR(new_ns)) {
+ put_css_set(cset);
+ dec_cgroup_namespaces(ucounts);
+ return new_ns;
+ }
+
+ new_ns->user_ns = get_user_ns(user_ns);
+ new_ns->ucounts = ucounts;
+ new_ns->root_cset = cset;
+
+ ns_tree_add(new_ns);
+ return new_ns;
+}
+#endif
+
static int cgroupns_install(struct nsset *nsset, struct ns_common *ns)
{
struct nsproxy *nsproxy = nsset->nsproxy;

--
2.55.0