|
@@ -4480,6 +4480,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
|
|
|
struct dentry *d = cgrp->dentry;
|
|
|
struct cgroup_event *event, *tmp;
|
|
|
struct cgroup_subsys *ss;
|
|
|
+ struct cgroup *child;
|
|
|
bool empty;
|
|
|
|
|
|
lockdep_assert_held(&d->d_inode->i_mutex);
|
|
@@ -4490,11 +4491,27 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
|
|
|
* @cgrp from being removed while __put_css_set() is in progress.
|
|
|
*/
|
|
|
read_lock(&css_set_lock);
|
|
|
- empty = list_empty(&cgrp->cset_links) && list_empty(&cgrp->children);
|
|
|
+ empty = list_empty(&cgrp->cset_links);
|
|
|
read_unlock(&css_set_lock);
|
|
|
if (!empty)
|
|
|
return -EBUSY;
|
|
|
|
|
|
+ /*
|
|
|
+ * Make sure there's no live children. We can't test ->children
|
|
|
+ * emptiness as dead children linger on it while being destroyed;
|
|
|
+ * otherwise, "rmdir parent/child parent" may fail with -EBUSY.
|
|
|
+ */
|
|
|
+ empty = true;
|
|
|
+ rcu_read_lock();
|
|
|
+ list_for_each_entry_rcu(child, &cgrp->children, sibling) {
|
|
|
+ empty = cgroup_is_dead(child);
|
|
|
+ if (!empty)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
+ if (!empty)
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
/*
|
|
|
* Block new css_tryget() by killing css refcnts. cgroup core
|
|
|
* guarantees that, by the time ->css_offline() is invoked, no new
|