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 *cgroup)
 132{
 133        struct freezer *freezer;
 134
 135        freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
 136        if (!freezer)
 137                return ERR_PTR(-ENOMEM);
 138
 139        spin_lock_init(&freezer->lock);
 140        freezer->state = CGROUP_THAWED;
 141        return &freezer->css;
 142}
 143
 144static void freezer_destroy(struct cgroup *cgroup)
 145{
 146        struct freezer *freezer = cgroup_freezer(cgroup);
 147
 148        if (freezer->state != CGROUP_THAWED)
 149                atomic_dec(&system_freezing_cnt);
 150        kfree(freezer);
 151}
 152
 153/* task is frozen or will freeze immediately when next it gets woken */
 154static bool is_task_frozen_enough(struct task_struct *task)
 155{
 156        return frozen(task) ||
 157                (task_is_stopped_or_traced(task) && freezing(task));
 158}
 159
 160/*
 161 * The call to cgroup_lock() in the freezer.state write method prevents
 162 * a write to that file racing against an attach, and hence the
 163 * can_attach() result will remain valid until the attach completes.
 164 */
 165static int freezer_can_attach(struct cgroup *new_cgroup,
 166                              struct cgroup_taskset *tset)
 167{
 168        struct freezer *freezer;
 169        struct task_struct *task;
 170
 171        /*
 172         * Anything frozen can't move or be moved to/from.
 173         */
 174        cgroup_taskset_for_each(task, new_cgroup, tset)
 175                if (cgroup_freezing(task))
 176                        return -EBUSY;
 177
 178        freezer = cgroup_freezer(new_cgroup);
 179        if (freezer->state != CGROUP_THAWED)
 180                return -EBUSY;
 181
 182        return 0;
 183}
 184
 185static void freezer_fork(struct task_struct *task)
 186{
 187        struct freezer *freezer;
 188
 189        /*
 190         * No lock is needed, since the task isn't on tasklist yet,
 191         * so it can't be moved to another cgroup, which means the
 192         * freezer won't be removed and will be valid during this
 193         * function call.  Nevertheless, apply RCU read-side critical
 194         * section to suppress RCU lockdep false positives.
 195         */
 196        rcu_read_lock();
 197        freezer = task_freezer(task);
 198        rcu_read_unlock();
 199
 200        /*
 201         * The root cgroup is non-freezable, so we can skip the
 202         * following check.
 203         */
 204        if (!freezer->css.cgroup->parent)
 205                return;
 206
 207        spin_lock_irq(&freezer->lock);
 208        BUG_ON(freezer->state == CGROUP_FROZEN);
 209
 210        /* Locking avoids race with FREEZING -> THAWED transitions. */
 211        if (freezer->state == CGROUP_FREEZING)
 212                freeze_task(task);
 213        spin_unlock_irq(&freezer->lock);
 214}
 215
 216/*
 217 * caller must hold freezer->lock
 218 */
 219static void update_if_frozen(struct cgroup *cgroup,
 220                                 struct freezer *freezer)
 221{
 222        struct cgroup_iter it;
 223        struct task_struct *task;
 224        unsigned int nfrozen = 0, ntotal = 0;
 225        enum freezer_state old_state = freezer->state;
 226
 227        cgroup_iter_start(cgroup, &it);
 228        while ((task = cgroup_iter_next(cgroup, &it))) {
 229                ntotal++;
 230                if (freezing(task) && is_task_frozen_enough(task))
 231                        nfrozen++;
 232        }
 233
 234        if (old_state == CGROUP_THAWED) {
 235                BUG_ON(nfrozen > 0);
 236        } else if (old_state == CGROUP_FREEZING) {
 237                if (nfrozen == ntotal)
 238                        freezer->state = CGROUP_FROZEN;
 239        } else { /* old_state == CGROUP_FROZEN */
 240                BUG_ON(nfrozen != ntotal);
 241        }
 242
 243        cgroup_iter_end(cgroup, &it);
 244}
 245
 246static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
 247                        struct seq_file *m)
 248{
 249        struct freezer *freezer;
 250        enum freezer_state state;
 251
 252        if (!cgroup_lock_live_group(cgroup))
 253                return -ENODEV;
 254
 255        freezer = cgroup_freezer(cgroup);
 256        spin_lock_irq(&freezer->lock);
 257        state = freezer->state;
 258        if (state == CGROUP_FREEZING) {
 259                /* We change from FREEZING to FROZEN lazily if the cgroup was
 260                 * only partially frozen when we exitted write. */
 261                update_if_frozen(cgroup, freezer);
 262                state = freezer->state;
 263        }
 264        spin_unlock_irq(&freezer->lock);
 265        cgroup_unlock();
 266
 267        seq_puts(m, freezer_state_strs[state]);
 268        seq_putc(m, '\n');
 269        return 0;
 270}
 271
 272static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
 273{
 274        struct cgroup_iter it;
 275        struct task_struct *task;
 276        unsigned int num_cant_freeze_now = 0;
 277
 278        cgroup_iter_start(cgroup, &it);
 279        while ((task = cgroup_iter_next(cgroup, &it))) {
 280                if (!freeze_task(task))
 281                        continue;
 282                if (is_task_frozen_enough(task))
 283                        continue;
 284                if (!freezing(task) && !freezer_should_skip(task))
 285                        num_cant_freeze_now++;
 286        }
 287        cgroup_iter_end(cgroup, &it);
 288
 289        return num_cant_freeze_now ? -EBUSY : 0;
 290}
 291
 292static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
 293{
 294        struct cgroup_iter it;
 295        struct task_struct *task;
 296
 297        cgroup_iter_start(cgroup, &it);
 298        while ((task = cgroup_iter_next(cgroup, &it)))
 299                __thaw_task(task);
 300        cgroup_iter_end(cgroup, &it);
 301}
 302
 303static int freezer_change_state(struct cgroup *cgroup,
 304                                enum freezer_state goal_state)
 305{
 306        struct freezer *freezer;
 307        int retval = 0;
 308
 309        freezer = cgroup_freezer(cgroup);
 310
 311        spin_lock_irq(&freezer->lock);
 312
 313        update_if_frozen(cgroup, freezer);
 314
 315        switch (goal_state) {
 316        case CGROUP_THAWED:
 317                if (freezer->state != CGROUP_THAWED)
 318                        atomic_dec(&system_freezing_cnt);
 319                freezer->state = CGROUP_THAWED;
 320                unfreeze_cgroup(cgroup, freezer);
 321                break;
 322        case CGROUP_FROZEN:
 323                if (freezer->state == CGROUP_THAWED)
 324                        atomic_inc(&system_freezing_cnt);
 325                freezer->state = CGROUP_FREEZING;
 326                retval = try_to_freeze_cgroup(cgroup, freezer);
 327                break;
 328        default:
 329                BUG();
 330        }
 331
 332        spin_unlock_irq(&freezer->lock);
 333
 334        return retval;
 335}
 336
 337static int freezer_write(struct cgroup *cgroup,
 338                         struct cftype *cft,
 339                         const char *buffer)
 340{
 341        int retval;
 342        enum freezer_state goal_state;
 343
 344        if (strcmp(buffer, freezer_state_strs[CGROUP_THAWED]) == 0)
 345                goal_state = CGROUP_THAWED;
 346        else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0)
 347                goal_state = CGROUP_FROZEN;
 348        else
 349                return -EINVAL;
 350
 351        if (!cgroup_lock_live_group(cgroup))
 352                return -ENODEV;
 353        retval = freezer_change_state(cgroup, goal_state);
 354        cgroup_unlock();
 355        return retval;
 356}
 357
 358static struct cftype files[] = {
 359        {
 360                .name = "state",
 361                .flags = CFTYPE_NOT_ON_ROOT,
 362                .read_seq_string = freezer_read,
 363                .write_string = freezer_write,
 364        },
 365        { }     /* terminate */
 366};
 367
 368struct cgroup_subsys freezer_subsys = {
 369        .name           = "freezer",
 370        .create         = freezer_create,
 371        .destroy        = freezer_destroy,
 372        .subsys_id      = freezer_subsys_id,
 373        .can_attach     = freezer_can_attach,
 374        .fork           = freezer_fork,
 375        .base_cftypes   = files,
 376};
 377
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.