cgroup_freezer: introduce CGROUP_FREEZING_[SELF|PARENT]
[deliverable/linux.git] / kernel / cgroup_freezer.c
index 8a92b0e52099185136ace406dcefa7129b1ba5e1..b8ad93c6f5f6d7d48f2ec9e18ac383a0821450b5 100644 (file)
 #include <linux/freezer.h>
 #include <linux/seq_file.h>
 
-enum freezer_state {
-       CGROUP_THAWED = 0,
-       CGROUP_FREEZING,
-       CGROUP_FROZEN,
+enum freezer_state_flags {
+       CGROUP_FREEZING_SELF    = (1 << 1), /* this freezer is freezing */
+       CGROUP_FREEZING_PARENT  = (1 << 2), /* the parent freezer is freezing */
+       CGROUP_FROZEN           = (1 << 3), /* this and its descendants frozen */
+
+       /* mask for all FREEZING flags */
+       CGROUP_FREEZING         = CGROUP_FREEZING_SELF | CGROUP_FREEZING_PARENT,
 };
 
 struct freezer {
-       struct cgroup_subsys_state css;
-       enum freezer_state state;
-       spinlock_t lock; /* protects _writes_ to state */
+       struct cgroup_subsys_state      css;
+       unsigned int                    state;
+       spinlock_t                      lock;
 };
 
-static inline struct freezer *cgroup_freezer(
-               struct cgroup *cgroup)
+static inline struct freezer *cgroup_freezer(struct cgroup *cgroup)
 {
-       return container_of(
-               cgroup_subsys_state(cgroup, freezer_subsys_id),
-               struct freezer, css);
+       return container_of(cgroup_subsys_state(cgroup, freezer_subsys_id),
+                           struct freezer, css);
 }
 
 static inline struct freezer *task_freezer(struct task_struct *task)
@@ -50,12 +51,10 @@ static inline struct freezer *task_freezer(struct task_struct *task)
 
 bool cgroup_freezing(struct task_struct *task)
 {
-       enum freezer_state state;
        bool ret;
 
        rcu_read_lock();
-       state = task_freezer(task)->state;
-       ret = state == CGROUP_FREEZING || state == CGROUP_FROZEN;
+       ret = task_freezer(task)->state & CGROUP_FREEZING;
        rcu_read_unlock();
 
        return ret;
@@ -65,10 +64,13 @@ bool cgroup_freezing(struct task_struct *task)
  * cgroups_write_string() limits the size of freezer state strings to
  * CGROUP_LOCAL_BUFFER_SIZE
  */
-static const char *freezer_state_strs[] = {
-       "THAWED",
-       "FREEZING",
-       "FROZEN",
+static const char *freezer_state_strs(unsigned int state)
+{
+       if (state & CGROUP_FROZEN)
+               return "FROZEN";
+       if (state & CGROUP_FREEZING)
+               return "FREEZING";
+       return "THAWED";
 };
 
 /*
@@ -93,7 +95,6 @@ static struct cgroup_subsys_state *freezer_create(struct cgroup *cgroup)
                return ERR_PTR(-ENOMEM);
 
        spin_lock_init(&freezer->lock);
-       freezer->state = CGROUP_THAWED;
        return &freezer->css;
 }
 
@@ -101,7 +102,7 @@ static void freezer_destroy(struct cgroup *cgroup)
 {
        struct freezer *freezer = cgroup_freezer(cgroup);
 
-       if (freezer->state != CGROUP_THAWED)
+       if (freezer->state & CGROUP_FREEZING)
                atomic_dec(&system_freezing_cnt);
        kfree(freezer);
 }
@@ -131,15 +132,13 @@ static void freezer_attach(struct cgroup *new_cgrp, struct cgroup_taskset *tset)
         * Tasks in @tset are on @new_cgrp but may not conform to its
         * current state before executing the following - !frozen tasks may
         * be visible in a FROZEN cgroup and frozen tasks in a THAWED one.
-        * This means that, to determine whether to freeze, one should test
-        * whether the state equals THAWED.
         */
        cgroup_taskset_for_each(task, new_cgrp, tset) {
-               if (freezer->state == CGROUP_THAWED) {
+               if (!(freezer->state & CGROUP_FREEZING)) {
                        __thaw_task(task);
                } else {
                        freeze_task(task);
-                       freezer->state = CGROUP_FREEZING;
+                       freezer->state &= ~CGROUP_FROZEN;
                }
        }
 
@@ -161,11 +160,7 @@ static void freezer_fork(struct task_struct *task)
                goto out;
 
        spin_lock_irq(&freezer->lock);
-       /*
-        * @task might have been just migrated into a FROZEN cgroup.  Test
-        * equality with THAWED.  Read the comment in freezer_attach().
-        */
-       if (freezer->state != CGROUP_THAWED)
+       if (freezer->state & CGROUP_FREEZING)
                freeze_task(task);
        spin_unlock_irq(&freezer->lock);
 out:
@@ -180,12 +175,14 @@ out:
  * migrated into or out of @cgroup, so we can't verify task states against
  * @freezer state here.  See freezer_attach() for details.
  */
-static void update_if_frozen(struct cgroup *cgroup, struct freezer *freezer)
+static void update_if_frozen(struct freezer *freezer)
 {
+       struct cgroup *cgroup = freezer->css.cgroup;
        struct cgroup_iter it;
        struct task_struct *task;
 
-       if (freezer->state != CGROUP_FREEZING)
+       if (!(freezer->state & CGROUP_FREEZING) ||
+           (freezer->state & CGROUP_FROZEN))
                return;
 
        cgroup_iter_start(cgroup, &it);
@@ -198,13 +195,12 @@ static void update_if_frozen(struct cgroup *cgroup, struct freezer *freezer)
                         * completion.  Consider it frozen in addition to
                         * the usual frozen condition.
                         */
-                       if (!frozen(task) && !task_is_stopped_or_traced(task) &&
-                           !freezer_should_skip(task))
+                       if (!frozen(task) && !freezer_should_skip(task))
                                goto notyet;
                }
        }
 
-       freezer->state = CGROUP_FROZEN;
+       freezer->state |= CGROUP_FROZEN;
 notyet:
        cgroup_iter_end(cgroup, &it);
 }
@@ -212,22 +208,22 @@ notyet:
 static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
                        struct seq_file *m)
 {
-       struct freezer *freezer;
-       enum freezer_state state;
+       struct freezer *freezer = cgroup_freezer(cgroup);
+       unsigned int state;
 
-       freezer = cgroup_freezer(cgroup);
        spin_lock_irq(&freezer->lock);
-       update_if_frozen(cgroup, freezer);
+       update_if_frozen(freezer);
        state = freezer->state;
        spin_unlock_irq(&freezer->lock);
 
-       seq_puts(m, freezer_state_strs[state]);
+       seq_puts(m, freezer_state_strs(state));
        seq_putc(m, '\n');
        return 0;
 }
 
-static void freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
+static void freeze_cgroup(struct freezer *freezer)
 {
+       struct cgroup *cgroup = freezer->css.cgroup;
        struct cgroup_iter it;
        struct task_struct *task;
 
@@ -237,8 +233,9 @@ static void freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
        cgroup_iter_end(cgroup, &it);
 }
 
-static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
+static void unfreeze_cgroup(struct freezer *freezer)
 {
+       struct cgroup *cgroup = freezer->css.cgroup;
        struct cgroup_iter it;
        struct task_struct *task;
 
@@ -248,51 +245,85 @@ static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
        cgroup_iter_end(cgroup, &it);
 }
 
-static void freezer_change_state(struct cgroup *cgroup,
-                                enum freezer_state goal_state)
+/**
+ * freezer_apply_state - apply state change to a single cgroup_freezer
+ * @freezer: freezer to apply state change to
+ * @freeze: whether to freeze or unfreeze
+ * @state: CGROUP_FREEZING_* flag to set or clear
+ *
+ * Set or clear @state on @cgroup according to @freeze, and perform
+ * freezing or thawing as necessary.
+ */
+static void freezer_apply_state(struct freezer *freezer, bool freeze,
+                               unsigned int state)
 {
-       struct freezer *freezer = cgroup_freezer(cgroup);
-
        /* also synchronizes against task migration, see freezer_attach() */
-       spin_lock_irq(&freezer->lock);
+       lockdep_assert_held(&freezer->lock);
 
-       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)
+       if (freeze) {
+               if (!(freezer->state & CGROUP_FREEZING))
                        atomic_inc(&system_freezing_cnt);
-               freezer->state = CGROUP_FREEZING;
-               freeze_cgroup(cgroup, freezer);
-               break;
-       default:
-               BUG();
+               freezer->state |= state;
+               freeze_cgroup(freezer);
+       } else {
+               bool was_freezing = freezer->state & CGROUP_FREEZING;
+
+               freezer->state &= ~state;
+
+               if (!(freezer->state & CGROUP_FREEZING)) {
+                       if (was_freezing)
+                               atomic_dec(&system_freezing_cnt);
+                       freezer->state &= ~CGROUP_FROZEN;
+                       unfreeze_cgroup(freezer);
+               }
        }
+}
 
+/**
+ * freezer_change_state - change the freezing state of a cgroup_freezer
+ * @freezer: freezer of interest
+ * @freeze: whether to freeze or thaw
+ *
+ * Freeze or thaw @cgroup according to @freeze.
+ */
+static void freezer_change_state(struct freezer *freezer, bool freeze)
+{
+       /* update @freezer */
+       spin_lock_irq(&freezer->lock);
+       freezer_apply_state(freezer, freeze, CGROUP_FREEZING_SELF);
        spin_unlock_irq(&freezer->lock);
 }
 
-static int freezer_write(struct cgroup *cgroup,
-                        struct cftype *cft,
+static int freezer_write(struct cgroup *cgroup, struct cftype *cft,
                         const char *buffer)
 {
-       enum freezer_state goal_state;
+       bool freeze;
 
-       if (strcmp(buffer, freezer_state_strs[CGROUP_THAWED]) == 0)
-               goal_state = CGROUP_THAWED;
-       else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0)
-               goal_state = CGROUP_FROZEN;
+       if (strcmp(buffer, freezer_state_strs(0)) == 0)
+               freeze = false;
+       else if (strcmp(buffer, freezer_state_strs(CGROUP_FROZEN)) == 0)
+               freeze = true;
        else
                return -EINVAL;
 
-       freezer_change_state(cgroup, goal_state);
+       freezer_change_state(cgroup_freezer(cgroup), freeze);
        return 0;
 }
 
+static u64 freezer_self_freezing_read(struct cgroup *cgroup, struct cftype *cft)
+{
+       struct freezer *freezer = cgroup_freezer(cgroup);
+
+       return (bool)(freezer->state & CGROUP_FREEZING_SELF);
+}
+
+static u64 freezer_parent_freezing_read(struct cgroup *cgroup, struct cftype *cft)
+{
+       struct freezer *freezer = cgroup_freezer(cgroup);
+
+       return (bool)(freezer->state & CGROUP_FREEZING_PARENT);
+}
+
 static struct cftype files[] = {
        {
                .name = "state",
@@ -300,6 +331,16 @@ static struct cftype files[] = {
                .read_seq_string = freezer_read,
                .write_string = freezer_write,
        },
+       {
+               .name = "self_freezing",
+               .flags = CFTYPE_NOT_ON_ROOT,
+               .read_u64 = freezer_self_freezing_read,
+       },
+       {
+               .name = "parent_freezing",
+               .flags = CFTYPE_NOT_ON_ROOT,
+               .read_u64 = freezer_parent_freezing_read,
+       },
        { }     /* terminate */
 };
 
This page took 0.040921 seconds and 5 git commands to generate.