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
