cgroup: introduce cancel_attach()
[linux-2.6.git] / kernel / cgroup.c
index 4fd90e1..be45d2f 100644 (file)
@@ -1554,7 +1554,7 @@ int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen)
 int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
 {
        int retval = 0;
-       struct cgroup_subsys *ss;
+       struct cgroup_subsys *ss, *failed_ss = NULL;
        struct cgroup *oldcgrp;
        struct css_set *cg;
        struct css_set *newcg;
@@ -1568,8 +1568,16 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
        for_each_subsys(root, ss) {
                if (ss->can_attach) {
                        retval = ss->can_attach(ss, cgrp, tsk, false);
-                       if (retval)
-                               return retval;
+                       if (retval) {
+                               /*
+                                * Remember on which subsystem the can_attach()
+                                * failed, so that we only call cancel_attach()
+                                * against the subsystems whose can_attach()
+                                * succeeded. (See below)
+                                */
+                               failed_ss = ss;
+                               goto out;
+                       }
                }
        }
 
@@ -1583,14 +1591,17 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
         */
        newcg = find_css_set(cg, cgrp);
        put_css_set(cg);
-       if (!newcg)
-               return -ENOMEM;
+       if (!newcg) {
+               retval = -ENOMEM;
+               goto out;
+       }
 
        task_lock(tsk);
        if (tsk->flags & PF_EXITING) {
                task_unlock(tsk);
                put_css_set(newcg);
-               return -ESRCH;
+               retval = -ESRCH;
+               goto out;
        }
        rcu_assign_pointer(tsk->cgroups, newcg);
        task_unlock(tsk);
@@ -1616,7 +1627,22 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
         * is no longer empty.
         */
        cgroup_wakeup_rmdir_waiter(cgrp);
-       return 0;
+out:
+       if (retval) {
+               for_each_subsys(root, ss) {
+                       if (ss == failed_ss)
+                               /*
+                                * This subsystem was the one that failed the
+                                * can_attach() check earlier, so we don't need
+                                * to call cancel_attach() against it or any
+                                * remaining subsystems.
+                                */
+                               break;
+                       if (ss->cancel_attach)
+                               ss->cancel_attach(ss, cgrp, tsk, false);
+               }
+       }
+       return retval;
 }
 
 /*