On 3/2/26 15:38, Maarten Lankhorst wrote:
Hey,

This should probably have a Co-developed-by: Tejun Heo <[email protected]>

Oh, that's a good point, sorry!

Although, I think I also need to add a S-o-b tag, then, don't I?

Tejun, just to confirm, would you be fine with that? Wouldn't want to claim people certify something without talking to them first :P


I need to take a closer look at patch 4 and 6, to add my r-b over the rest.

Den 2026-03-02 kl. 13:37, skrev Natalie Vock:
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]>
---
  include/linux/cgroup.h      | 21 +++++++++++++++++++++
  include/linux/cgroup_dmem.h |  9 +++++++++
  kernel/cgroup/dmem.c        | 43 ++++++++++++++++++++++++++++++++++++++++---
  3 files changed, 70 insertions(+), 3 deletions(-)

diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index bc892e3b37eea..560ae995e3a54 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -561,6 +561,27 @@ static inline struct cgroup *cgroup_ancestor(struct cgroup 
*cgrp,
        return cgrp->ancestors[ancestor_level];
  }
+/**
+ * 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 exists
+ * and return a pointer to it. If such a cgroup doesn't exist, return NULL.
+ *
+ * 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 = min(a->level, b->level); level >= 0; level--)
+               if (a->ancestors[level] == b->ancestors[level])
+                       return a->ancestors[level];
+       return NULL;
+}
+
  /**
   * task_under_cgroup_hierarchy - test task's membership of cgroup ancestry
   * @task: the task to be tested
diff --git a/include/linux/cgroup_dmem.h b/include/linux/cgroup_dmem.h
index 1a88cd0c9eb00..444b84f4c253a 100644
--- 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_common_ancestor(struct 
dmem_cgroup_pool_state *a,
+                                                          struct 
dmem_cgroup_pool_state *b);
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_cgroup_pool_state *root,
        return false;
  }
+static inline
+struct dmem_cgroup_pool_state *dmem_cgroup_common_ancestor(struct 
dmem_cgroup_pool_state *a,
+                                                          struct 
dmem_cgroup_pool_state *b)
+{
+       return NULL;
+}
+
  static inline void dmem_cgroup_pool_state_put(struct dmem_cgroup_pool_state 
*pool)
  { }
diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c
index 28227405f7cfe..a3ba865f4c68f 100644
--- a/kernel/cgroup/dmem.c
+++ b/kernel/cgroup/dmem.c
@@ -569,11 +569,10 @@ void dmem_cgroup_pool_state_put(struct 
dmem_cgroup_pool_state *pool)
  EXPORT_SYMBOL_GPL(dmem_cgroup_pool_state_put);
static struct dmem_cgroup_pool_state *
-get_cg_pool_unlocked(struct dmemcg_state *cg, struct dmem_cgroup_region 
*region)
+find_cg_pool_unlocked(struct dmemcg_state *cg, struct dmem_cgroup_region 
*region)
  {
-       struct dmem_cgroup_pool_state *pool, *allocpool = NULL;
+       struct dmem_cgroup_pool_state *pool;
- /* fastpath lookup? */
        rcu_read_lock();
        pool = find_cg_pool_locked(cg, region);
        if (pool && !READ_ONCE(pool->inited))
@@ -582,6 +581,17 @@ get_cg_pool_unlocked(struct dmemcg_state *cg, struct 
dmem_cgroup_region *region)
                pool = NULL;
        rcu_read_unlock();
+ return pool;
+}
+
+static struct dmem_cgroup_pool_state *
+get_cg_pool_unlocked(struct dmemcg_state *cg, struct dmem_cgroup_region 
*region)
+{
+       struct dmem_cgroup_pool_state *pool, *allocpool = NULL;
+
+       /* fastpath lookup? */
+       pool = find_cg_pool_unlocked(cg, region);
+
        while (!pool) {
                spin_lock(&dmemcg_lock);
                if (!region->unregistered)
@@ -756,6 +766,33 @@ bool dmem_cgroup_below_low(struct dmem_cgroup_pool_state 
*root,
  }
  EXPORT_SYMBOL_GPL(dmem_cgroup_below_low);
+/**
+ * dmem_cgroup_common_ancestor(): Find the first common ancestor of two 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.
+ */
+struct dmem_cgroup_pool_state *dmem_cgroup_common_ancestor(struct 
dmem_cgroup_pool_state *a,
+                                                          struct 
dmem_cgroup_pool_state *b)
+{
+       struct cgroup *ancestor_cgroup;
+       struct cgroup_subsys_state *ancestor_css;
+
+       if (!a || !b)
+               return NULL;
+
+       ancestor_cgroup = cgroup_common_ancestor(a->cs->css.cgroup, 
b->cs->css.cgroup);
+       if (!ancestor_cgroup)
+               return NULL;
+
+       ancestor_css = cgroup_e_css(ancestor_cgroup, &dmem_cgrp_subsys);
+
+       return find_cg_pool_unlocked(css_to_dmemcs(ancestor_css), a->region);
+}
+EXPORT_SYMBOL_GPL(dmem_cgroup_common_ancestor);
 From the naming, I would not expect a reference to be taken to the common 
ancestor, especially because the reference through a and b would both be able 
keep the ancestor alive. Otherwise it would not be an ancestor. Rename to 
dmem_cgroup_get_common_ancestor perhaps? Same for the find_, perhaps rename to 
lookup_ or use the unmodified get_cg_pool_unlocked version, because the common 
ancestor's pool_state definitely exists if either a or b do.

Right. Will rename to dmem_cgroup_get_common_ancestor, and also point out in the documentation that a reference is taken.

Thanks,
Natalie


Kind regards,
~Maarten Lankhorst

Reply via email to