cgroup: reorder the operations in cgroup_destroy_locked()
authorTejun Heo <tj@kernel.org>
Fri, 14 Jun 2013 02:27:41 +0000 (19:27 -0700)
committerTejun Heo <tj@kernel.org>
Fri, 14 Jun 2013 02:27:41 +0000 (19:27 -0700)
This patch reorders the operations in cgroup_destroy_locked() such
that the userland visible parts happen before css offlining and
removal from the ->sibling list.  This will be used to make css use
percpu refcnt.

While at it, split out CGRP_DEAD related comment from the refcnt
deactivation one and correct / clarify how different guarantees are
met.

While this patch changes the specific order of operations, it
shouldn't cause any noticeable behavior difference.

Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Li Zefan <lizefan@huawei.com>
kernel/cgroup.c

index 49bfd7b0bbdaa05130235d57537f7f3eeddbd8b9..5a1ddecc3cfa49f4152ba21478bf792f07edc8d3 100644 (file)
@@ -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;
 }