Merge tag 'please-pull-naveen' of git://git.kernel.org/pub/scm/linux/kernel/git/ras...
[linux-3.10.git] / kernel / cgroup_freezer.c
index 321e2a0..3649fc6 100644 (file)
@@ -14,7 +14,7 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/cgroup.h>
 #include <linux/fs.h>
@@ -48,19 +48,17 @@ static inline struct freezer *task_freezer(struct task_struct *task)
                            struct freezer, css);
 }
 
-static inline int __cgroup_freezing_or_frozen(struct task_struct *task)
+bool cgroup_freezing(struct task_struct *task)
 {
-       enum freezer_state state = task_freezer(task)->state;
-       return (state == CGROUP_FREEZING) || (state == CGROUP_FROZEN);
-}
+       enum freezer_state state;
+       bool ret;
 
-int cgroup_freezing_or_frozen(struct task_struct *task)
-{
-       int result;
-       task_lock(task);
-       result = __cgroup_freezing_or_frozen(task);
-       task_unlock(task);
-       return result;
+       rcu_read_lock();
+       state = task_freezer(task)->state;
+       ret = state == CGROUP_FREEZING || state == CGROUP_FROZEN;
+       rcu_read_unlock();
+
+       return ret;
 }
 
 /*
@@ -102,9 +100,6 @@ struct cgroup_subsys freezer_subsys;
  * freezer_can_attach():
  * cgroup_mutex (held by caller of can_attach)
  *
- * cgroup_freezing_or_frozen():
- * task->alloc_lock (to get task's cgroup)
- *
  * freezer_fork() (preserving fork() performance means can't take cgroup_mutex):
  * freezer->lock
  *  sighand->siglock (if the cgroup is freezing)
@@ -130,11 +125,10 @@ struct cgroup_subsys freezer_subsys;
  *   write_lock css_set_lock (cgroup iterator start)
  *    task->alloc_lock
  *   read_lock css_set_lock (cgroup iterator start)
- *    task->alloc_lock (inside thaw_process(), prevents race with refrigerator())
+ *    task->alloc_lock (inside __thaw_task(), prevents race with refrigerator())
  *     sighand->siglock
  */
-static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss,
-                                                 struct cgroup *cgroup)
+static struct cgroup_subsys_state *freezer_create(struct cgroup *cgroup)
 {
        struct freezer *freezer;
 
@@ -147,13 +141,16 @@ static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss,
        return &freezer->css;
 }
 
-static void freezer_destroy(struct cgroup_subsys *ss,
-                           struct cgroup *cgroup)
+static void freezer_destroy(struct cgroup *cgroup)
 {
-       kfree(cgroup_freezer(cgroup));
+       struct freezer *freezer = cgroup_freezer(cgroup);
+
+       if (freezer->state != CGROUP_THAWED)
+               atomic_dec(&system_freezing_cnt);
+       kfree(freezer);
 }
 
-/* Task is frozen or will freeze immediately when next it gets woken */
+/* task is frozen or will freeze immediately when next it gets woken */
 static bool is_task_frozen_enough(struct task_struct *task)
 {
        return frozen(task) ||
@@ -165,43 +162,27 @@ static bool is_task_frozen_enough(struct task_struct *task)
  * a write to that file racing against an attach, and hence the
  * can_attach() result will remain valid until the attach completes.
  */
-static int freezer_can_attach(struct cgroup_subsys *ss,
-                             struct cgroup *new_cgroup,
-                             struct task_struct *task, bool threadgroup)
+static int freezer_can_attach(struct cgroup *new_cgroup,
+                             struct cgroup_taskset *tset)
 {
        struct freezer *freezer;
+       struct task_struct *task;
 
        /*
         * Anything frozen can't move or be moved to/from.
-        *
-        * Since orig_freezer->state == FROZEN means that @task has been
-        * frozen, so it's sufficient to check the latter condition.
         */
-
-       if (is_task_frozen_enough(task))
-               return -EBUSY;
+       cgroup_taskset_for_each(task, new_cgroup, tset)
+               if (cgroup_freezing(task))
+                       return -EBUSY;
 
        freezer = cgroup_freezer(new_cgroup);
-       if (freezer->state == CGROUP_FROZEN)
+       if (freezer->state != CGROUP_THAWED)
                return -EBUSY;
 
-       if (threadgroup) {
-               struct task_struct *c;
-
-               rcu_read_lock();
-               list_for_each_entry_rcu(c, &task->thread_group, thread_group) {
-                       if (is_task_frozen_enough(c)) {
-                               rcu_read_unlock();
-                               return -EBUSY;
-                       }
-               }
-               rcu_read_unlock();
-       }
-
        return 0;
 }
 
-static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task)
+static void freezer_fork(struct task_struct *task)
 {
        struct freezer *freezer;
 
@@ -228,38 +209,37 @@ static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task)
 
        /* Locking avoids race with FREEZING -> THAWED transitions. */
        if (freezer->state == CGROUP_FREEZING)
-               freeze_task(task, true);
+               freeze_task(task);
        spin_unlock_irq(&freezer->lock);
 }
 
 /*
  * caller must hold freezer->lock
  */
-static void update_freezer_state(struct cgroup *cgroup,
+static void update_if_frozen(struct cgroup *cgroup,
                                 struct freezer *freezer)
 {
        struct cgroup_iter it;
        struct task_struct *task;
        unsigned int nfrozen = 0, ntotal = 0;
+       enum freezer_state old_state = freezer->state;
 
        cgroup_iter_start(cgroup, &it);
        while ((task = cgroup_iter_next(cgroup, &it))) {
                ntotal++;
-               if (is_task_frozen_enough(task))
+               if (freezing(task) && is_task_frozen_enough(task))
                        nfrozen++;
        }
 
-       /*
-        * Transition to FROZEN when no new tasks can be added ensures
-        * that we never exist in the FROZEN state while there are unfrozen
-        * tasks.
-        */
-       if (nfrozen == ntotal)
-               freezer->state = CGROUP_FROZEN;
-       else if (nfrozen > 0)
-               freezer->state = CGROUP_FREEZING;
-       else
-               freezer->state = CGROUP_THAWED;
+       if (old_state == CGROUP_THAWED) {
+               BUG_ON(nfrozen > 0);
+       } else if (old_state == CGROUP_FREEZING) {
+               if (nfrozen == ntotal)
+                       freezer->state = CGROUP_FROZEN;
+       } else { /* old_state == CGROUP_FROZEN */
+               BUG_ON(nfrozen != ntotal);
+       }
+
        cgroup_iter_end(cgroup, &it);
 }
 
@@ -278,7 +258,7 @@ static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
        if (state == CGROUP_FREEZING) {
                /* We change from FREEZING to FROZEN lazily if the cgroup was
                 * only partially frozen when we exitted write. */
-               update_freezer_state(cgroup, freezer);
+               update_if_frozen(cgroup, freezer);
                state = freezer->state;
        }
        spin_unlock_irq(&freezer->lock);
@@ -295,10 +275,9 @@ static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
        struct task_struct *task;
        unsigned int num_cant_freeze_now = 0;
 
-       freezer->state = CGROUP_FREEZING;
        cgroup_iter_start(cgroup, &it);
        while ((task = cgroup_iter_next(cgroup, &it))) {
-               if (!freeze_task(task, true))
+               if (!freeze_task(task))
                        continue;
                if (is_task_frozen_enough(task))
                        continue;
@@ -316,12 +295,9 @@ static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
        struct task_struct *task;
 
        cgroup_iter_start(cgroup, &it);
-       while ((task = cgroup_iter_next(cgroup, &it))) {
-               thaw_process(task);
-       }
+       while ((task = cgroup_iter_next(cgroup, &it)))
+               __thaw_task(task);
        cgroup_iter_end(cgroup, &it);
-
-       freezer->state = CGROUP_THAWED;
 }
 
 static int freezer_change_state(struct cgroup *cgroup,
@@ -334,21 +310,25 @@ static int freezer_change_state(struct cgroup *cgroup,
 
        spin_lock_irq(&freezer->lock);
 
-       update_freezer_state(cgroup, freezer);
-       if (goal_state == freezer->state)
-               goto out;
+       update_if_frozen(cgroup, freezer);
 
        switch (goal_state) {
        case CGROUP_THAWED:
+               if (freezer->state != CGROUP_THAWED)
+                       atomic_dec(&system_freezing_cnt);
+               freezer->state = CGROUP_THAWED;
                unfreeze_cgroup(cgroup, freezer);
                break;
        case CGROUP_FROZEN:
+               if (freezer->state == CGROUP_THAWED)
+                       atomic_inc(&system_freezing_cnt);
+               freezer->state = CGROUP_FREEZING;
                retval = try_to_freeze_cgroup(cgroup, freezer);
                break;
        default:
                BUG();
        }
-out:
+
        spin_unlock_irq(&freezer->lock);
 
        return retval;
@@ -378,26 +358,19 @@ static int freezer_write(struct cgroup *cgroup,
 static struct cftype files[] = {
        {
                .name = "state",
+               .flags = CFTYPE_NOT_ON_ROOT,
                .read_seq_string = freezer_read,
                .write_string = freezer_write,
        },
+       { }     /* terminate */
 };
 
-static int freezer_populate(struct cgroup_subsys *ss, struct cgroup *cgroup)
-{
-       if (!cgroup->parent)
-               return 0;
-       return cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files));
-}
-
 struct cgroup_subsys freezer_subsys = {
        .name           = "freezer",
        .create         = freezer_create,
        .destroy        = freezer_destroy,
-       .populate       = freezer_populate,
        .subsys_id      = freezer_subsys_id,
        .can_attach     = freezer_can_attach,
-       .attach         = NULL,
        .fork           = freezer_fork,
-       .exit           = NULL,
+       .base_cftypes   = files,
 };