aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2013-05-23 21:50:24 -0400
committerTejun Heo <tj@kernel.org>2013-05-23 21:50:24 -0400
commit7805d000db30a3787a4c969bab6ae4d8a5fd8ce6 (patch)
tree5178e1adaa4f398d2c1367fb38633191e81764e5 /kernel
parentd6cbf35dac8a3dadb9103379820c96d7c85df3d9 (diff)
cgroup: fix a subtle bug in descendant pre-order walk
When cgroup_next_descendant_pre() initiates a walk, it checks whether the subtree root doesn't have any children and if not returns NULL. Later code assumes that the subtree isn't empty. This is broken because the subtree may become empty inbetween, which can lead to the traversal escaping the subtree by walking to the sibling of the subtree root. There's no reason to have the early exit path. Remove it along with the later assumption that the subtree isn't empty. This simplifies the code a bit and fixes the subtle bug. While at it, fix the comment of cgroup_for_each_descendant_pre() which was incorrectly referring to ->css_offline() instead of ->css_online(). Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Michal Hocko <mhocko@suse.cz> Cc: stable@vger.kernel.org
Diffstat (limited to 'kernel')
-rw-r--r--kernel/cgroup.c9
1 files changed, 3 insertions, 6 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 38b136553044..31e9ef319070 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -2954,11 +2954,8 @@ struct cgroup *cgroup_next_descendant_pre(struct cgroup *pos,
2954 WARN_ON_ONCE(!rcu_read_lock_held()); 2954 WARN_ON_ONCE(!rcu_read_lock_held());
2955 2955
2956 /* if first iteration, pretend we just visited @cgroup */ 2956 /* if first iteration, pretend we just visited @cgroup */
2957 if (!pos) { 2957 if (!pos)
2958 if (list_empty(&cgroup->children))
2959 return NULL;
2960 pos = cgroup; 2958 pos = cgroup;
2961 }
2962 2959
2963 /* visit the first child if exists */ 2960 /* visit the first child if exists */
2964 next = list_first_or_null_rcu(&pos->children, struct cgroup, sibling); 2961 next = list_first_or_null_rcu(&pos->children, struct cgroup, sibling);
@@ -2966,14 +2963,14 @@ struct cgroup *cgroup_next_descendant_pre(struct cgroup *pos,
2966 return next; 2963 return next;
2967 2964
2968 /* no child, visit my or the closest ancestor's next sibling */ 2965 /* no child, visit my or the closest ancestor's next sibling */
2969 do { 2966 while (pos != cgroup) {
2970 next = list_entry_rcu(pos->sibling.next, struct cgroup, 2967 next = list_entry_rcu(pos->sibling.next, struct cgroup,
2971 sibling); 2968 sibling);
2972 if (&next->sibling != &pos->parent->children) 2969 if (&next->sibling != &pos->parent->children)
2973 return next; 2970 return next;
2974 2971
2975 pos = pos->parent; 2972 pos = pos->parent;
2976 } while (pos != cgroup); 2973 }
2977 2974
2978 return NULL; 2975 return NULL;
2979} 2976}