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