linux/kernel/cgroup_freezer.c
<<
>>
Prefs
   1/*
   2 * cgroup_freezer.c -  control group freezer subsystem
   3 *
   4 * Copyright IBM Corporation, 2007
   5 *
   6 * Author : Cedric Le Goater <clg@fr.ibm.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms of version 2.1 of the GNU Lesser General Public License
  10 * as published by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it would be useful, but
  13 * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15 */
  16
  17#include <linux/export.h>
  18#include <linux/slab.h>
  19#include <linux/cgroup.h>
  20#include <linux/fs.h>
  21#include <linux/uaccess.h>
  22#include <linux/freezer.h>
  23#include <linux/seq_file.h>
  24
  25enum freezer_state {
  26        CGROUP_THAWED = 0,
  27        CGROUP_FREEZING,
  28        CGROUP_FROZEN,
  29};
  30
  31struct freezer {
  32        struct cgroup_subsys_state css;
  33        enum freezer_state state;
  34        spinlock_t lock; /* protects _writes_ to state */
  35};
  36
  37static inline struct freezer *cgroup_freezer(
  38                struct cgroup *cgroup)
  39{
  40        return container_of(
  41                cgroup_subsys_state(cgroup, freezer_subsys_id),
  42                struct freezer, css);
  43}
  44
  45static inline struct freezer *task_freezer(struct task_struct *task)
  46{
  47        return container_of(task_subsys_state(task, freezer_subsys_id),
  48                            struct freezer, css);
  49}
  50
  51bool cgroup_freezing(struct task_struct *task)
  52{
  53        enum freezer_state state;
  54        bool ret;
  55
  56        rcu_read_lock();
  57        state = task_freezer(task)->state;
  58        ret = state == CGROUP_FREEZING || state == CGROUP_FROZEN;
  59        rcu_read_unlock();
  60
  61        return ret;
  62}
  63
  64/*
  65 * cgroups_write_string() limits the size of freezer state strings to
  66 * CGROUP_LOCAL_BUFFER_SIZE
  67 */
  68static const char *freezer_state_strs[] = {
  69        "THAWED",
  70        "FREEZING",
  71        "FROZEN",
  72};
  73
  74/*
  75 * State diagram
  76 * Transitions are caused by userspace writes to the freezer.state file.
  77 * The values in parenthesis are state labels. The rest are edge labels.
  78 *
  79 * (THAWED) --FROZEN--> (FREEZING) --FROZEN--> (FROZEN)
  80 *    ^ ^                    |                     |
  81 *    | \_______THAWED_______/                     |
  82 *    \__________________________THAWED____________/
  83 */
  84
  85struct cgroup_subsys freezer_subsys;
  86
  87/* Locks taken and their ordering
  88 * ------------------------------
  89 * cgroup_mutex (AKA cgroup_lock)
  90 * freezer->lock
  91 * css_set_lock
  92 * task->alloc_lock (AKA task_lock)
  93 * task->sighand->siglock
  94 *
  95 * cgroup code forces css_set_lock to be taken before task->alloc_lock
  96 *
  97 * freezer_create(), freezer_destroy():
  98 * cgroup_mutex [ by cgroup core ]
  99 *
 100 * freezer_can_attach():
 101 * cgroup_mutex (held by caller of can_attach)
 102 *
 103 * freezer_fork() (preserving fork() performance means can't take cgroup_mutex):
 104 * freezer->lock
 105 *  sighand->siglock (if the cgroup is freezing)
 106 *
 107 * freezer_read():
 108 * cgroup_mutex
 109 *  freezer->lock
 110 *   write_lock css_set_lock (cgroup iterator start)
 111 *    task->alloc_lock
 112 *   read_lock css_set_lock (cgroup iterator start)
 113 *
 114 * freezer_write() (freeze):
 115 * cgroup_mutex
 116 *  freezer->lock
 117 *   write_lock css_set_lock (cgroup iterator start)
 118 *    task->alloc_lock
 119 *   read_lock css_set_lock (cgroup iterator start)
 120 *    sighand->siglock (fake signal delivery inside freeze_task())
 121 *
 122 * freezer_write() (unfreeze):
 123 * cgroup_mutex
 124 *  freezer->lock
 125 *   write_lock css_set_lock (cgroup iterator start)
 126 *    task->alloc_lock
 127 *   read_lock css_set_lock (cgroup iterator start)
 128 *    task->alloc_lock (inside __thaw_task(), prevents race with refrigerator())
 129 *     sighand->siglock
 130 */
 131static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss,
 132                                                  struct cgroup *cgroup)
 133{
 134        struct freezer *freezer;
 135
 136        freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
 137        if (!freezer)
 138                return ERR_PTR(-ENOMEM);
 139
 140        spin_lock_init(&freezer->lock);
 141        freezer->state = CGROUP_THAWED;
 142        return &freezer->css;
 143}
 144
 145static void freezer_destroy(struct cgroup_subsys *ss,
 146                            struct cgroup *cgroup)
 147{
 148        struct freezer *freezer = cgroup_freezer(cgroup);
 149
 150        if (freezer->state != CGROUP_THAWED)
 151                atomic_dec(&system_freezing_cnt);
 152        kfree(freezer);
 153}
 154
 155/* task is frozen or will freeze immediately when next it gets woken */
 156static bool is_task_frozen_enough(struct task_struct *task)
 157{
 158        return frozen(task) ||
 159                (task_is_stopped_or_traced(task) && freezing(task));
 160}
 161
 162/*
 163 * The call to cgroup_lock() in the freezer.state write method prevents
 164 * a write to that file racing against an attach, and hence the
 165 * can_attach() result will remain valid until the attach completes.
 166 */
 167static int freezer_can_attach(struct cgroup_subsys *ss,
 168                              struct cgroup *new_cgroup,
 169                              struct cgroup_taskset *tset)
 170{
 171        struct freezer *freezer;
 172        struct task_struct *task;
 173
 174        /*
 175         * Anything frozen can't move or be moved to/from.
 176         */
 177        cgroup_taskset_for_each(task, new_cgroup, tset)
 178                if (cgroup_freezing(task))
 179                        return -EBUSY;
 180
 181        freezer = cgroup_freezer(new_cgroup);
 182        if (freezer->state != CGROUP_THAWED)
 183                return -EBUSY;
 184
 185        return 0;
 186}
 187
 188static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task)
 189{
 190        struct freezer *freezer;
 191
 192        /*
 193         * No lock is needed, since the task isn't on tasklist yet,
 194         * so it can't be moved to another cgroup, which means the
 195         * freezer won't be removed and will be valid during this
 196         * function call.  Nevertheless, apply RCU read-side critical
 197         * section to suppress RCU lockdep false positives.
 198         */
 199        rcu_read_lock();
 200        freezer = task_freezer(task);
 201        rcu_read_unlock();
 202
 203        /*
 204         * The root cgroup is non-freezable, so we can skip the
 205         * following check.
 206         */
 207        if (!freezer->css.cgroup->parent)
 208                return;
 209
 210        spin_lock_irq(&freezer->lock);
 211        BUG_ON(freezer->state == CGROUP_FROZEN);
 212
 213        /* Locking avoids race with FREEZING -> THAWED transitions. */
 214        if (freezer->state == CGROUP_FREEZING)
 215                freeze_task(task);
 216        spin_unlock_irq(&freezer->lock);
 217}
 218
 219/*
 220 * caller must hold freezer->lock
 221 */
 222static void update_if_frozen(struct cgroup *cgroup,
 223                                 struct freezer *freezer)
 224{
 225        struct cgroup_iter it;
 226        struct task_struct *task;
 227        unsigned int nfrozen = 0, ntotal = 0;
 228        enum freezer_state old_state = freezer->state;
 229
 230        cgroup_iter_start(cgroup, &it);
 231        while ((task = cgroup_iter_next(cgroup, &it))) {
 232                ntotal++;
 233                if (freezing(task) && is_task_frozen_enough(task))
 234                        nfrozen++;
 235        }
 236
 237        if (old_state == CGROUP_THAWED) {
 238                BUG_ON(nfrozen > 0);
 239        } else if (old_state == CGROUP_FREEZING) {
 240                if (nfrozen == ntotal)
 241                        freezer->state = CGROUP_FROZEN;
 242        } else { /* old_state == CGROUP_FROZEN */
 243                BUG_ON(nfrozen != ntotal);
 244        }
 245
 246        cgroup_iter_end(cgroup, &it);
 247}
 248
 249static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
 250                        struct seq_file *m)
 251{
 252        struct freezer *freezer;
 253        enum freezer_state state;
 254
 255        if (!cgroup_lock_live_group(cgroup))
 256                return -ENODEV;
 257
 258        freezer = cgroup_freezer(cgroup);
 259        spin_lock_irq(&freezer->lock);
 260        state = freezer->state;
 261        if (state == CGROUP_FREEZING) {
 262                /* We change from FREEZING to FROZEN lazily if the cgroup was
 263                 * only partially frozen when we exitted write. */
 264                update_if_frozen(cgroup, freezer);
 265                state = freezer->state;
 266        }
 267        spin_unlock_irq(&freezer->lock);
 268        cgroup_unlock();
 269
 270        seq_puts(m, freezer_state_strs[state]);
 271        seq_putc(m, '\n');
 272        return 0;
 273}
 274
 275static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
 276{
 277        struct cgroup_iter it;
 278        struct task_struct *task;
 279        unsigned int num_cant_freeze_now = 0;
 280
 281        cgroup_iter_start(cgroup, &it);
 282        while ((task = cgroup_iter_next(cgroup, &it))) {
 283                if (!freeze_task(task))
 284                        continue;
 285                if (is_task_frozen_enough(task))
 286                        continue;
 287                if (!freezing(task) && !freezer_should_skip(task))
 288                        num_cant_freeze_now++;
 289        }
 290        cgroup_iter_end(cgroup, &it);
 291
 292        return num_cant_freeze_now ? -EBUSY : 0;
 293}
 294
 295static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
 296{
 297        struct cgroup_iter it;
 298        struct task_struct *task;
 299
 300        cgroup_iter_start(cgroup, &it);
 301        while ((task = cgroup_iter_next(cgroup, &it)))
 302                __thaw_task(task);
 303        cgroup_iter_end(cgroup, &it);
 304}
 305
 306static int freezer_change_state(struct cgroup *cgroup,
 307                                enum freezer_state goal_state)
 308{
 309        struct freezer *freezer;
 310        int retval = 0;
 311
 312        freezer = cgroup_freezer(cgroup);
 313
 314        spin_lock_irq(&freezer->lock);
 315
 316        update_if_frozen(cgroup, freezer);
 317
 318        switch (goal_state) {
 319        case CGROUP_THAWED:
 320                if (freezer->state != CGROUP_THAWED)
 321                        atomic_dec(&system_freezing_cnt);
 322                freezer->state = CGROUP_THAWED;
 323                unfreeze_cgroup(cgroup, freezer);
 324                break;
 325        case CGROUP_FROZEN:
 326                if (freezer->state == CGROUP_THAWED)
 327                        atomic_inc(&system_freezing_cnt);
 328                freezer->state = CGROUP_FREEZING;
 329                retval = try_to_freeze_cgroup(cgroup, freezer);
 330                break;
 331        default:
 332                BUG();
 333        }
 334
 335        spin_unlock_irq(&freezer->lock);
 336
 337        return retval;
 338}
 339
 340static int freezer_write(struct cgroup *cgroup,
 341                         struct cftype *cft,
 342                         const char *buffer)
 343{
 344        int retval;
 345        enum freezer_state goal_state;
 346
 347        if (strcmp(buffer, freezer_state_strs[CGROUP_THAWED]) == 0)
 348                goal_state = CGROUP_THAWED;
 349        else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0)
 350                goal_state = CGROUP_FROZEN;
 351        else
 352                return -EINVAL;
 353
 354        if (!cgroup_lock_live_group(cgroup))
 355                return -ENODEV;
 356        retval = freezer_change_state(cgroup, goal_state);
 357        cgroup_unlock();
 358        return retval;
 359}
 360
 361static struct cftype files[] = {
 362        {
 363                .name = "state",
 364                .read_seq_string = freezer_read,
 365                .write_string = freezer_write,
 366        },
 367};
 368
 369static int freezer_populate(struct cgroup_subsys *ss, struct cgroup *cgroup)
 370{
 371        if (!cgroup->parent)
 372                return 0;
 373        return cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files));
 374}
 375
 376struct cgroup_subsys freezer_subsys = {
 377        .name           = "freezer",
 378        .create         = freezer_create,
 379        .destroy        = freezer_destroy,
 380        .populate       = freezer_populate,
 381        .subsys_id      = freezer_subsys_id,
 382        .can_attach     = freezer_can_attach,
 383        .fork           = freezer_fork,
 384};
 385