diff options
-rw-r--r-- | include/linux/cgroup.h | 2 | ||||
-rw-r--r-- | kernel/cgroup.c | 32 |
2 files changed, 22 insertions, 12 deletions
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 51a339c99eb6..b76999954beb 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h | |||
@@ -873,6 +873,8 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, | |||
873 | for ((pos) = css_next_descendant_post(NULL, (css)); (pos); \ | 873 | for ((pos) = css_next_descendant_post(NULL, (css)); (pos); \ |
874 | (pos) = css_next_descendant_post((pos), (css))) | 874 | (pos) = css_next_descendant_post((pos), (css))) |
875 | 875 | ||
876 | bool css_has_online_children(struct cgroup_subsys_state *css); | ||
877 | |||
876 | /* A css_task_iter should be treated as an opaque object */ | 878 | /* A css_task_iter should be treated as an opaque object */ |
877 | struct css_task_iter { | 879 | struct css_task_iter { |
878 | struct cgroup_subsys *ss; | 880 | struct cgroup_subsys *ss; |
diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 004004fd0ded..082bb842b11a 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c | |||
@@ -175,7 +175,6 @@ static int need_forkexit_callback __read_mostly; | |||
175 | static struct cftype cgroup_base_files[]; | 175 | static struct cftype cgroup_base_files[]; |
176 | 176 | ||
177 | static void cgroup_put(struct cgroup *cgrp); | 177 | static void cgroup_put(struct cgroup *cgrp); |
178 | static bool cgroup_has_live_children(struct cgroup *cgrp); | ||
179 | static int rebind_subsystems(struct cgroup_root *dst_root, | 178 | static int rebind_subsystems(struct cgroup_root *dst_root, |
180 | unsigned int ss_mask); | 179 | unsigned int ss_mask); |
181 | static int cgroup_destroy_locked(struct cgroup *cgrp); | 180 | static int cgroup_destroy_locked(struct cgroup *cgrp); |
@@ -1769,7 +1768,7 @@ static void cgroup_kill_sb(struct super_block *sb) | |||
1769 | * This prevents new mounts by disabling percpu_ref_tryget_live(). | 1768 | * This prevents new mounts by disabling percpu_ref_tryget_live(). |
1770 | * cgroup_mount() may wait for @root's release. | 1769 | * cgroup_mount() may wait for @root's release. |
1771 | */ | 1770 | */ |
1772 | if (cgroup_has_live_children(&root->cgrp)) | 1771 | if (css_has_online_children(&root->cgrp.self)) |
1773 | cgroup_put(&root->cgrp); | 1772 | cgroup_put(&root->cgrp); |
1774 | else | 1773 | else |
1775 | percpu_ref_kill(&root->cgrp.self.refcnt); | 1774 | percpu_ref_kill(&root->cgrp.self.refcnt); |
@@ -3291,19 +3290,28 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, | |||
3291 | return pos->parent; | 3290 | return pos->parent; |
3292 | } | 3291 | } |
3293 | 3292 | ||
3294 | static bool cgroup_has_live_children(struct cgroup *cgrp) | 3293 | /** |
3294 | * css_has_online_children - does a css have online children | ||
3295 | * @css: the target css | ||
3296 | * | ||
3297 | * Returns %true if @css has any online children; otherwise, %false. This | ||
3298 | * function can be called from any context but the caller is responsible | ||
3299 | * for synchronizing against on/offlining as necessary. | ||
3300 | */ | ||
3301 | bool css_has_online_children(struct cgroup_subsys_state *css) | ||
3295 | { | 3302 | { |
3296 | struct cgroup *child; | 3303 | struct cgroup_subsys_state *child; |
3304 | bool ret = false; | ||
3297 | 3305 | ||
3298 | rcu_read_lock(); | 3306 | rcu_read_lock(); |
3299 | list_for_each_entry_rcu(child, &cgrp->self.children, self.sibling) { | 3307 | css_for_each_child(child, css) { |
3300 | if (!cgroup_is_dead(child)) { | 3308 | if (css->flags & CSS_ONLINE) { |
3301 | rcu_read_unlock(); | 3309 | ret = true; |
3302 | return true; | 3310 | break; |
3303 | } | 3311 | } |
3304 | } | 3312 | } |
3305 | rcu_read_unlock(); | 3313 | rcu_read_unlock(); |
3306 | return false; | 3314 | return ret; |
3307 | } | 3315 | } |
3308 | 3316 | ||
3309 | /** | 3317 | /** |
@@ -4535,7 +4543,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) | |||
4535 | * ->self.children as dead children linger on it while being | 4543 | * ->self.children as dead children linger on it while being |
4536 | * drained; otherwise, "rmdir parent/child parent" may fail. | 4544 | * drained; otherwise, "rmdir parent/child parent" may fail. |
4537 | */ | 4545 | */ |
4538 | if (cgroup_has_live_children(cgrp)) | 4546 | if (css_has_online_children(&cgrp->self)) |
4539 | return -EBUSY; | 4547 | return -EBUSY; |
4540 | 4548 | ||
4541 | /* | 4549 | /* |
@@ -5014,8 +5022,8 @@ void cgroup_exit(struct task_struct *tsk) | |||
5014 | 5022 | ||
5015 | static void check_for_release(struct cgroup *cgrp) | 5023 | static void check_for_release(struct cgroup *cgrp) |
5016 | { | 5024 | { |
5017 | if (cgroup_is_releasable(cgrp) && | 5025 | if (cgroup_is_releasable(cgrp) && list_empty(&cgrp->cset_links) && |
5018 | list_empty(&cgrp->cset_links) && !cgroup_has_live_children(cgrp)) { | 5026 | !css_has_online_children(&cgrp->self)) { |
5019 | /* | 5027 | /* |
5020 | * Control Group is currently removeable. If it's not | 5028 | * Control Group is currently removeable. If it's not |
5021 | * already queued for a userspace notification, queue | 5029 | * already queued for a userspace notification, queue |