|
@@ -4379,13 +4379,8 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
|
|
|
|
|
|
/*
|
|
|
* Block new css_tryget() by deactivating refcnt and mark @cgrp
|
|
|
- * removed. This makes future css_tryget() and child creation
|
|
|
- * attempts fail thus maintaining the removal conditions verified
|
|
|
- * above.
|
|
|
- *
|
|
|
- * Note that CGRP_DEAD assertion is depended upon by
|
|
|
- * cgroup_next_sibling() to resume iteration after dropping RCU
|
|
|
- * read lock. See cgroup_next_sibling() for details.
|
|
|
+ * removed. This makes future css_tryget() attempts fail which we
|
|
|
+ * guarantee to ->css_offline() callbacks.
|
|
|
*/
|
|
|
for_each_subsys(cgrp->root, ss) {
|
|
|
struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id];
|
|
@@ -4393,8 +4388,41 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
|
|
|
WARN_ON(atomic_read(&css->refcnt) < 0);
|
|
|
atomic_add(CSS_DEACT_BIAS, &css->refcnt);
|
|
|
}
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Mark @cgrp dead. This prevents further task migration and child
|
|
|
+ * creation by disabling cgroup_lock_live_group(). Note that
|
|
|
+ * CGRP_DEAD assertion is depended upon by cgroup_next_sibling() to
|
|
|
+ * resume iteration after dropping RCU read lock. See
|
|
|
+ * cgroup_next_sibling() for details.
|
|
|
+ */
|
|
|
set_bit(CGRP_DEAD, &cgrp->flags);
|
|
|
|
|
|
+ /* CGRP_DEAD is set, remove from ->release_list for the last time */
|
|
|
+ raw_spin_lock(&release_list_lock);
|
|
|
+ if (!list_empty(&cgrp->release_list))
|
|
|
+ list_del_init(&cgrp->release_list);
|
|
|
+ raw_spin_unlock(&release_list_lock);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Remove @cgrp directory. The removal puts the base ref but we
|
|
|
+ * aren't quite done with @cgrp yet, so hold onto it.
|
|
|
+ */
|
|
|
+ dget(d);
|
|
|
+ cgroup_d_remove_dir(d);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Unregister events and notify userspace.
|
|
|
+ * Notify userspace about cgroup removing only after rmdir of cgroup
|
|
|
+ * directory to avoid race between userspace and kernelspace.
|
|
|
+ */
|
|
|
+ spin_lock(&cgrp->event_list_lock);
|
|
|
+ list_for_each_entry_safe(event, tmp, &cgrp->event_list, list) {
|
|
|
+ list_del_init(&event->list);
|
|
|
+ schedule_work(&event->remove);
|
|
|
+ }
|
|
|
+ spin_unlock(&cgrp->event_list_lock);
|
|
|
+
|
|
|
/* tell subsystems to initate destruction */
|
|
|
for_each_subsys(cgrp->root, ss)
|
|
|
offline_css(ss, cgrp);
|
|
@@ -4409,34 +4437,15 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
|
|
|
for_each_subsys(cgrp->root, ss)
|
|
|
css_put(cgrp->subsys[ss->subsys_id]);
|
|
|
|
|
|
- raw_spin_lock(&release_list_lock);
|
|
|
- if (!list_empty(&cgrp->release_list))
|
|
|
- list_del_init(&cgrp->release_list);
|
|
|
- raw_spin_unlock(&release_list_lock);
|
|
|
-
|
|
|
/* delete this cgroup from parent->children */
|
|
|
list_del_rcu(&cgrp->sibling);
|
|
|
list_del_init(&cgrp->allcg_node);
|
|
|
|
|
|
- dget(d);
|
|
|
- cgroup_d_remove_dir(d);
|
|
|
dput(d);
|
|
|
|
|
|
set_bit(CGRP_RELEASABLE, &parent->flags);
|
|
|
check_for_release(parent);
|
|
|
|
|
|
- /*
|
|
|
- * Unregister events and notify userspace.
|
|
|
- * Notify userspace about cgroup removing only after rmdir of cgroup
|
|
|
- * directory to avoid race between userspace and kernelspace.
|
|
|
- */
|
|
|
- spin_lock(&cgrp->event_list_lock);
|
|
|
- list_for_each_entry_safe(event, tmp, &cgrp->event_list, list) {
|
|
|
- list_del_init(&event->list);
|
|
|
- schedule_work(&event->remove);
|
|
|
- }
|
|
|
- spin_unlock(&cgrp->event_list_lock);
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|