[PATCH v8 2/6] cgroup,cgroup/dmem: Add (dmem_)cgroup_common_ancestor helper
Natalie Vock <[email protected]> Tue, 04 Aug 2026 10:25:17 +0200
| Newsgroups | org.kernel.vger.cgroups,org.freedesktop.lists.dri-devel |
|---|---|
| Message-ID | <[email protected]> |
This helps to find a common subtree of two resources, which is important when determining whether it's helpful to evict one resource in favor of another. To facilitate this, add a common helper to find the ancestor of two cgroups using each cgroup's ancestor array. Signed-off-by: Natalie Vock <[email protected]> =2D-- include/linux/cgroup.h | 21 +++++++++++++++++++++ include/linux/cgroup_dmem.h | 9 +++++++++ kernel/cgroup/dmem.c | 41 ++++++++++++++++++++++++++++++++++++++++= + 3 files changed, 71 insertions(+) diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index f2aa46a4f871e..83a17ded1c516 100644 =2D-- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -623,6 +623,27 @@ static inline struct cgroup *cgroup_ancestor(struct c= group *cgrp, return cgrp->ancestors[ancestor_level]; } =20 +/** + * cgroup_common_ancestor - find common ancestor of two cgroups + * @a: first cgroup to find common ancestor of + * @b: second cgroup to find common ancestor of + * + * Find the first cgroup that is an ancestor of both @a and @b, if it exi= sts + * and return a pointer to it. If such a cgroup doesn't exist, return NUL= L. + * + * This function is safe to call as long as both @a and @b are accessible= . + */ +static inline struct cgroup *cgroup_common_ancestor(struct cgroup *a, + struct cgroup *b) +{ + int level; + + for (level =3D min(a->level, b->level); level >=3D 0; level--) + if (a->ancestors[level] =3D=3D b->ancestors[level]) + return a->ancestors[level]; + return NULL; +} + /** * task_under_cgroup_hierarchy - test task's membership of cgroup ancestr= y * @task: the task to be tested diff --git a/include/linux/cgroup_dmem.h b/include/linux/cgroup_dmem.h index 1a88cd0c9eb00..9d72457c4cb9d 100644 =2D-- a/include/linux/cgroup_dmem.h +++ b/include/linux/cgroup_dmem.h @@ -28,6 +28,8 @@ bool dmem_cgroup_below_min(struct dmem_cgroup_pool_state= *root, struct dmem_cgroup_pool_state *test); bool dmem_cgroup_below_low(struct dmem_cgroup_pool_state *root, struct dmem_cgroup_pool_state *test); +struct dmem_cgroup_pool_state *dmem_cgroup_get_common_ancestor(struct dme= m_cgroup_pool_state *a, + struct dmem_cgroup_pool_state *b); =20 void dmem_cgroup_pool_state_put(struct dmem_cgroup_pool_state *pool); #else @@ -75,6 +77,13 @@ static inline bool dmem_cgroup_below_low(struct dmem_cg= roup_pool_state *root, return false; } =20 +static inline +struct dmem_cgroup_pool_state *dmem_cgroup_get_common_ancestor(struct dme= m_cgroup_pool_state *a, + struct dmem_cgroup_pool_state *b) +{ + return NULL; +} + static inline void dmem_cgroup_pool_state_put(struct dmem_cgroup_pool_sta= te *pool) { } =20 diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c index 9df3b33c65046..a587611ca2235 100644 =2D-- a/kernel/cgroup/dmem.c +++ b/kernel/cgroup/dmem.c @@ -762,6 +762,47 @@ bool dmem_cgroup_below_low(struct dmem_cgroup_pool_st= ate *root, } EXPORT_SYMBOL_GPL(dmem_cgroup_below_low); =20 +/** + * dmem_cgroup_get_common_ancestor(): Find the first common ancestor of t= wo pools. + * @a: First pool to find the common ancestor of. + * @b: First pool to find the common ancestor of. + * + * Return: The first pool that is a parent of both @a and @b, or NULL if = either @a or @b are NULL, + * or if such a pool does not exist. A reference to the returned pool is = grabbed and must be + * released by the caller when it is done using the pool. + */ +struct dmem_cgroup_pool_state *dmem_cgroup_get_common_ancestor(struct dme= m_cgroup_pool_state *a, + struct dmem_cgroup_pool_state *b) +{ + struct cgroup *ancestor_cgroup; + struct cgroup_subsys_state *ancestor_css; + struct dmemcg_state *ancestor_dmemcs =3D NULL; + struct dmem_cgroup_pool_state *pool =3D NULL; + + if (!a || !b) + return NULL; + + ancestor_cgroup =3D cgroup_common_ancestor(a->cs->css.cgroup, b->cs->css= .cgroup); + if (!ancestor_cgroup) + return NULL; + + rcu_read_lock(); + ancestor_css =3D cgroup_e_css(ancestor_cgroup, &dmem_cgrp_subsys); + if (css_tryget(ancestor_css)) + ancestor_dmemcs =3D css_to_dmemcs(ancestor_css); + rcu_read_unlock(); + + if (ancestor_dmemcs) { + pool =3D get_cg_pool_unlocked(css_to_dmemcs(ancestor_css), + a->region); + if (IS_ERR(pool)) + pool =3D NULL; + css_put(ancestor_css); + } + return pool; +} +EXPORT_SYMBOL_GPL(dmem_cgroup_get_common_ancestor); + static int dmem_cgroup_region_capacity_show(struct seq_file *sf, void *v) { struct dmem_cgroup_region *region; =2D-=20 2.55.0