memcg: update memcg_has_children() to use css_next_child()

Currently, memcg_has_children() and mem_cgroup_hierarchy_write()
directly test cgroup->children for list emptiness.  It's semantically
correct in traditional hierarchies as it actually wants to test for
any children dead or alive; however, cgroup->children is not a
published field and scheduled to go away.

This patch moves out .use_hierarchy test out of memcg_has_children()
and updates it to use css_next_child() to test whether there exists
any children.  With .use_hierarchy test moved out, it can also be used
by mem_cgroup_hierarchy_write().

A side note: As .use_hierarchy is going away, it doesn't really matter
but I'm not sure about how it's used in __memcg_activate_kmem().  The
condition tested by memcg_has_children() is mushy when seen from
userland as its result is affected by dead csses which aren't visible
from userland.  I think the rule would be a lot clearer if we have a
dedicated "freshly minted" flag which gets cleared when the first task
is migrated into it or the first child is created and then gate
activation with that.

v2: Added comment noting that testing use_hierarchy is the
    responsibility of the callers of memcg_has_children() as suggested
    by Michal Hocko.

Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Michal Hocko <mhocko@suse.cz>
Acked-by: Li Zefan <lizefan@huawei.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
This commit is contained in:
Tejun Heo 2014-05-16 13:22:48 -04:00
parent f61c42a7d9
commit ea280e7b40
1 changed files with 21 additions and 10 deletions

View File

@ -4834,18 +4834,28 @@ static void mem_cgroup_reparent_charges(struct mem_cgroup *memcg)
} while (usage > 0); } while (usage > 0);
} }
/*
* Test whether @memcg has children, dead or alive. Note that this
* function doesn't care whether @memcg has use_hierarchy enabled and
* returns %true if there are child csses according to the cgroup
* hierarchy. Testing use_hierarchy is the caller's responsiblity.
*/
static inline bool memcg_has_children(struct mem_cgroup *memcg) static inline bool memcg_has_children(struct mem_cgroup *memcg)
{ {
lockdep_assert_held(&memcg_create_mutex); bool ret;
/* /*
* The lock does not prevent addition or deletion to the list * The lock does not prevent addition or deletion of children, but
* of children, but it prevents a new child from being * it prevents a new child from being initialized based on this
* initialized based on this parent in css_online(), so it's * parent in css_online(), so it's enough to decide whether
* enough to decide whether hierarchically inherited * hierarchically inherited attributes can still be changed or not.
* attributes can still be changed or not.
*/ */
return memcg->use_hierarchy && lockdep_assert_held(&memcg_create_mutex);
!list_empty(&memcg->css.cgroup->children);
rcu_read_lock();
ret = css_next_child(NULL, &memcg->css);
rcu_read_unlock();
return ret;
} }
/* /*
@ -4919,7 +4929,7 @@ static int mem_cgroup_hierarchy_write(struct cgroup_subsys_state *css,
*/ */
if ((!parent_memcg || !parent_memcg->use_hierarchy) && if ((!parent_memcg || !parent_memcg->use_hierarchy) &&
(val == 1 || val == 0)) { (val == 1 || val == 0)) {
if (list_empty(&memcg->css.cgroup->children)) if (!memcg_has_children(memcg))
memcg->use_hierarchy = val; memcg->use_hierarchy = val;
else else
retval = -EBUSY; retval = -EBUSY;
@ -5036,7 +5046,8 @@ static int __memcg_activate_kmem(struct mem_cgroup *memcg,
* of course permitted. * of course permitted.
*/ */
mutex_lock(&memcg_create_mutex); mutex_lock(&memcg_create_mutex);
if (cgroup_has_tasks(memcg->css.cgroup) || memcg_has_children(memcg)) if (cgroup_has_tasks(memcg->css.cgroup) ||
(memcg->use_hierarchy && memcg_has_children(memcg)))
err = -EBUSY; err = -EBUSY;
mutex_unlock(&memcg_create_mutex); mutex_unlock(&memcg_create_mutex);
if (err) if (err)