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);