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);
+
 static int dmem_cgroup_region_capacity_show(struct seq_file *sf, void *v)
 {
        struct dmem_cgroup_region *region;

-- 
2.53.0

Reply via email to