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        rcu_read_lock();
 190        freezer = task_freezer(task);
 191
 192        /*
 193         * The root cgroup is non-freezable, so we can skip the
 194         * following check.
 195         */
 196        if (!freezer->css.cgroup->parent)
 197                goto out;
 198
 199        spin_lock_irq(&freezer->lock);
 200        BUG_ON(freezer->state == CGROUP_FROZEN);
 201
 202        /* Locking avoids race with FREEZING -> THAWED transitions. */
 203        if (freezer->state == CGROUP_FREEZING)
 204                freeze_task(task);
 205
 206        spin_unlock_irq(&freezer->lock);
 207out:
 208        rcu_read_unlock();
 209}
 210
 211/*
 212 * caller must hold freezer->lock
 213 */
 214static void update_if_frozen(struct cgroup *cgroup,
 215                                 struct freezer *freezer)
 216{
 217        struct cgroup_iter it;
 218        struct task_struct *task;
 219        unsigned int nfrozen = 0, ntotal = 0;
 220        enum freezer_state old_state = freezer->state;
 221
 222        cgroup_iter_start(cgroup, &it);
 223        while ((task = cgroup_iter_next(cgroup, &it))) {
 224                ntotal++;
 225                if (freezing(task) && is_task_frozen_enough(task))
 226                        nfrozen++;
 227        }
 228
 229        if (old_state == CGROUP_THAWED) {
 230                BUG_ON(nfrozen > 0);
 231        } else if (old_state == CGROUP_FREEZING) {
 232                if (nfrozen == ntotal)
 233                        freezer->state = CGROUP_FROZEN;
 234        } else { /* old_state == CGROUP_FROZEN */
 235                BUG_ON(nfrozen != ntotal);
 236        }
 237
 238        cgroup_iter_end(cgroup, &it);
 239}
 240
 241static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
 242                        struct seq_file *m)
 243{
 244        struct freezer *freezer;
 245        enum freezer_state state;
 246
 247        if (!cgroup_lock_live_group(cgroup))
 248                return -ENODEV;
 249
 250        freezer = cgroup_freezer(cgroup);
 251        spin_lock_irq(&freezer->lock);
 252        state = freezer->state;
 253        if (state == CGROUP_FREEZING) {
 254                /* We change from FREEZING to FROZEN lazily if the cgroup was
 255                 * only partially frozen when we exitted write. */
 256                update_if_frozen(cgroup, freezer);
 257                state = freezer->state;
 258        }
 259        spin_unlock_irq(&freezer->lock);
 260        cgroup_unlock();
 261
 262        seq_puts(m, freezer_state_strs[state]);
 263        seq_putc(m, '\n');
 264        return 0;
 265}
 266
 267static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
 268{
 269        struct cgroup_iter it;
 270        struct task_struct *task;
 271        unsigned int num_cant_freeze_now = 0;
 272
 273        cgroup_iter_start(cgroup, &it);
 274        while ((task = cgroup_iter_next(cgroup, &it))) {
 275                if (!freeze_task(task))
 276                        continue;
 277                if (is_task_frozen_enough(task))
 278                        continue;
 279                if (!freezing(task) && !freezer_should_skip(task))
 280                        num_cant_freeze_now++;
 281        }
 282        cgroup_iter_end(cgroup, &it);
 283
 284        return num_cant_freeze_now ? -EBUSY : 0;
 285}
 286
 287static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
 288{
 289        struct cgroup_iter it;
 290        struct task_struct *task;
 291
 292        cgroup_iter_start(cgroup, &it);
 293        while ((task = cgroup_iter_next(cgroup, &it)))
 294                __thaw_task(task);
 295        cgroup_iter_end(cgroup, &it);
 296}
 297
 298static int freezer_change_state(struct cgroup *cgroup,
 299                                enum freezer_state goal_state)
 300{
 301        struct freezer *freezer;
 302        int retval = 0;
 303
 304        freezer = cgroup_freezer(cgroup);
 305
 306        spin_lock_irq(&freezer->lock);
 307
 308        update_if_frozen(cgroup, freezer);
 309
 310        switch (goal_state) {
 311        case CGROUP_THAWED:
 312                if (freezer->state != CGROUP_THAWED)
 313                        atomic_dec(&system_freezing_cnt);
 314                freezer->state = CGROUP_THAWED;
 315                unfreeze_cgroup(cgroup, freezer);
 316                break;
 317        case CGROUP_FROZEN:
 318                if (freezer->state == CGROUP_THAWED)
 319                        atomic_inc(&system_freezing_cnt);
 320                freezer->state = CGROUP_FREEZING;
 321                retval = try_to_freeze_cgroup(cgroup, freezer);
 322                break;
 323        default:
 324                BUG();
 325        }
 326
 327        spin_unlock_irq(&freezer->lock);
 328
 329        return retval;
 330}
 331
 332static int freezer_write(struct cgroup *cgroup,
 333                         struct cftype *cft,
 334                         const char *buffer)
 335{
 336        int retval;
 337        enum freezer_state goal_state;
 338
 339        if (strcmp(buffer, freezer_state_strs[CGROUP_THAWED]) == 0)
 340                goal_state = CGROUP_THAWED;
 341        else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0)
 342                goal_state = CGROUP_FROZEN;
 343        else
 344                return -EINVAL;
 345
 346        if (!cgroup_lock_live_group(cgroup))
 347                return -ENODEV;
 348        retval = freezer_change_state(cgroup, goal_state);
 349        cgroup_unlock();
 350        return retval;
 351}
 352
 353static struct cftype files[] = {
 354        {
 355                .name = "state",
 356                .flags = CFTYPE_NOT_ON_ROOT,
 357                .read_seq_string = freezer_read,
 358                .write_string = freezer_write,
 359        },
 360        { }     /* terminate */
 361};
 362
 363struct cgroup_subsys freezer_subsys = {
 364        .name           = "freezer",
 365        .create         = freezer_create,
 366        .destroy        = freezer_destroy,
 367        .subsys_id      = freezer_subsys_id,
 368        .can_attach     = freezer_can_attach,
 369        .fork           = freezer_fork,
 370        .base_cftypes   = files,
 371
 372        /*
 373         * freezer subsys doesn't handle hierarchy at all.  Frozen state
 374         * should be inherited through the hierarchy - if a parent is
 375         * frozen, all its children should be frozen.  Fix it and remove
 376         * the following.
 377         */
 378        .broken_hierarchy = true,
 379};
 380
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.