cgroup,cgroup/dmem: Add (dmem_)cgroup_common_ancestor helper

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.

Tested-by: Thadeu Lima de Souza Cascardo <cascardo@igalia.com>
Reviewed-by: Maarten Lankhorst <dev@lankhorst.se>
Reviewed-by: Timur Kristóf <timur.kristof@gmail.com>
Signed-off-by: Natalie Vock <natalie.vock@gmx.de>
Link: https://patch.msgid.link/20260804-dmemcg-aggressive-protect-v8-2-07af96681bf8@gmx.de
This commit is contained in:
Natalie Vock 2026-08-04 10:25:17 +02:00 committed by Natalie Vock
parent dd517e49a3
commit 34f3e25992
3 changed files with 72 additions and 0 deletions

View File

@ -623,6 +623,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

View File

@ -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 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_get_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)
{ }

View File

@ -762,6 +762,48 @@ bool dmem_cgroup_below_low(struct dmem_cgroup_pool_state *root,
}
EXPORT_SYMBOL_GPL(dmem_cgroup_below_low);
/**
* dmem_cgroup_get_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. 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 dmem_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 = NULL;
struct dmem_cgroup_pool_state *pool = NULL;
if (!a || !b)
return NULL;
ancestor_cgroup = cgroup_common_ancestor(a->cs->css.cgroup, b->cs->css.cgroup);
if (!ancestor_cgroup)
return NULL;
rcu_read_lock();
ancestor_css = cgroup_e_css(ancestor_cgroup, &dmem_cgrp_subsys);
if (css_tryget(ancestor_css))
ancestor_dmemcs = css_to_dmemcs(ancestor_css);
rcu_read_unlock();
if (ancestor_dmemcs) {
pool = get_cg_pool_unlocked(css_to_dmemcs(ancestor_css),
a->region);
if (WARN_ON(IS_ERR(pool))) {
pool = 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;