From b5f99b537d047072bd50ff5c51000613eb537be8 Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Fri, 22 Feb 2013 16:34:53 -0800 Subject: [PATCH] memcg: fast hierarchy-aware child test Currently, we use cgroups' provided list of children to verify if it is safe to proceed with any value change that is dependent on the cgroup being empty. This is less than ideal, because it enforces a dependency over cgroup core that we would be better off without. The solution proposed here is to iterate over the child cgroups and if any is found that is already online, we bounce and return: we don't really care how many children we have, only if we have any. This is also made to be hierarchy aware. IOW, cgroups with hierarchy disabled, while they still exist, will be considered for the purpose of this interface as having no children. [akpm@linux-foundation.org: tweak comments] Signed-off-by: Glauber Costa Acked-by: Michal Hocko Cc: Tejun Heo Cc: Hiroyuki Kamezawa Cc: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index a9318963d5d5..2bc3fbe93154 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -4762,6 +4762,33 @@ static void mem_cgroup_reparent_charges(struct mem_cgroup *memcg) } while (usage > 0); } +/* + * This mainly exists for tests during the setting of set of use_hierarchy. + * Since this is the very setting we are changing, the current hierarchy value + * is meaningless + */ +static inline bool __memcg_has_children(struct mem_cgroup *memcg) +{ + struct cgroup *pos; + + /* bounce at first found */ + cgroup_for_each_child(pos, memcg->css.cgroup) + return true; + return false; +} + +/* + * Must be called with cgroup_lock held, unless the cgroup is guaranteed to be + * already dead (in mem_cgroup_force_empty(), for instance). This is different + * from mem_cgroup_count_children(), in the sense that we don't really care how + * many children we have; we only need to know if we have any. It also counts + * any memcg without hierarchy as infertile. + */ +static inline bool memcg_has_children(struct mem_cgroup *memcg) +{ + return memcg->use_hierarchy && __memcg_has_children(memcg); +} + /* * Reclaims as many pages from the given memcg as possible and moves * the rest to the parent. @@ -4847,7 +4874,7 @@ static int mem_cgroup_hierarchy_write(struct cgroup *cont, struct cftype *cft, */ if ((!parent_memcg || !parent_memcg->use_hierarchy) && (val == 1 || val == 0)) { - if (list_empty(&cont->children)) + if (!__memcg_has_children(memcg)) memcg->use_hierarchy = val; else retval = -EBUSY; @@ -4964,8 +4991,7 @@ static int memcg_update_kmem_limit(struct cgroup *cont, u64 val) cgroup_lock(); mutex_lock(&set_limit_mutex); if (!memcg->kmem_account_flags && val != RESOURCE_MAX) { - if (cgroup_task_count(cont) || (memcg->use_hierarchy && - !list_empty(&cont->children))) { + if (cgroup_task_count(cont) || memcg_has_children(memcg)) { ret = -EBUSY; goto out; } @@ -5373,8 +5399,7 @@ static int mem_cgroup_swappiness_write(struct cgroup *cgrp, struct cftype *cft, cgroup_lock(); /* If under hierarchy, only empty-root can set this value */ - if ((parent->use_hierarchy) || - (memcg->use_hierarchy && !list_empty(&cgrp->children))) { + if ((parent->use_hierarchy) || memcg_has_children(memcg)) { cgroup_unlock(); return -EINVAL; } @@ -5709,8 +5734,7 @@ static int mem_cgroup_oom_control_write(struct cgroup *cgrp, cgroup_lock(); /* oom-kill-disable is a flag for subhierarchy. */ - if ((parent->use_hierarchy) || - (memcg->use_hierarchy && !list_empty(&cgrp->children))) { + if ((parent->use_hierarchy) || memcg_has_children(memcg)) { cgroup_unlock(); return -EINVAL; } -- 2.20.1