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)
 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        return 0;
 181}
 182
 183static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task)
 184{
 185        struct freezer *freezer;
 186
 187        /*
 188         * No lock is needed, since the task isn't on tasklist yet,
 189         * so it can't be moved to another cgroup, which means the
 190         * freezer won't be removed and will be valid during this
 191         * function call.
 192         */
 193        freezer = task_freezer(task);
 194
 195        /*
 196         * The root cgroup is non-freezable, so we can skip the
 197         * following check.
 198         */
 199        if (!freezer->css.cgroup->parent)
 200                return;
 201
 202        spin_lock_irq(&freezer->lock);
 203        BUG_ON(freezer->state == CGROUP_FROZEN);
 204
 205        /* Locking avoids race with FREEZING -> THAWED transitions. */
 206        if (freezer->state == CGROUP_FREEZING)
 207                freeze_task(task, true);
 208        spin_unlock_irq(&freezer->lock);
 209}
 210
 211/*
 212 * caller must hold freezer->lock
 213 */
 214static void update_freezer_state(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
 221        cgroup_iter_start(cgroup, &it);
 222        while ((task = cgroup_iter_next(cgroup, &it))) {
 223                ntotal++;
 224                if (is_task_frozen_enough(task))
 225                        nfrozen++;
 226        }
 227
 228        /*
 229         * Transition to FROZEN when no new tasks can be added ensures
 230         * that we never exist in the FROZEN state while there are unfrozen
 231         * tasks.
 232         */
 233        if (nfrozen == ntotal)
 234                freezer->state = CGROUP_FROZEN;
 235        else if (nfrozen > 0)
 236                freezer->state = CGROUP_FREEZING;
 237        else
 238                freezer->state = CGROUP_THAWED;
 239        cgroup_iter_end(cgroup, &it);
 240}
 241
 242static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
 243                        struct seq_file *m)
 244{
 245        struct freezer *freezer;
 246        enum freezer_state state;
 247
 248        if (!cgroup_lock_live_group(cgroup))
 249                return -ENODEV;
 250
 251        freezer = cgroup_freezer(cgroup);
 252        spin_lock_irq(&freezer->lock);
 253        state = freezer->state;
 254        if (state == CGROUP_FREEZING) {
 255                /* We change from FREEZING to FROZEN lazily if the cgroup was
 256                 * only partially frozen when we exitted write. */
 257                update_freezer_state(cgroup, freezer);
 258                state = freezer->state;
 259        }
 260        spin_unlock_irq(&freezer->lock);
 261        cgroup_unlock();
 262
 263        seq_puts(m, freezer_state_strs[state]);
 264        seq_putc(m, '\n');
 265        return 0;
 266}
 267
 268static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
 269{
 270        struct cgroup_iter it;
 271        struct task_struct *task;
 272        unsigned int num_cant_freeze_now = 0;
 273
 274        freezer->state = CGROUP_FREEZING;
 275        cgroup_iter_start(cgroup, &it);
 276        while ((task = cgroup_iter_next(cgroup, &it))) {
 277                if (!freeze_task(task, true))
 278                        continue;
 279                if (is_task_frozen_enough(task))
 280                        continue;
 281                if (!freezing(task) && !freezer_should_skip(task))
 282                        num_cant_freeze_now++;
 283        }
 284        cgroup_iter_end(cgroup, &it);
 285
 286        return num_cant_freeze_now ? -EBUSY : 0;
 287}
 288
 289static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
 290{
 291        struct cgroup_iter it;
 292        struct task_struct *task;
 293
 294        cgroup_iter_start(cgroup, &it);
 295        while ((task = cgroup_iter_next(cgroup, &it))) {
 296                thaw_process(task);
 297        }
 298        cgroup_iter_end(cgroup, &it);
 299
 300        freezer->state = CGROUP_THAWED;
 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_freezer_state(cgroup, freezer);
 314        if (goal_state == freezer->state)
 315                goto out;
 316
 317        switch (goal_state) {
 318        case CGROUP_THAWED:
 319                unfreeze_cgroup(cgroup, freezer);
 320                break;
 321        case CGROUP_FROZEN:
 322                retval = try_to_freeze_cgroup(cgroup, freezer);
 323                break;
 324        default:
 325                BUG();
 326        }
 327out:
 328        spin_unlock_irq(&freezer->lock);
 329
 330        return retval;
 331}
 332
 333static int freezer_write(struct cgroup *cgroup,
 334                         struct cftype *cft,
 335                         const char *buffer)
 336{
 337        int retval;
 338        enum freezer_state goal_state;
 339
 340        if (strcmp(buffer, freezer_state_strs[CGROUP_THAWED]) == 0)
 341                goal_state = CGROUP_THAWED;
 342        else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0)
 343                goal_state = CGROUP_FROZEN;
 344        else
 345                return -EINVAL;
 346
 347        if (!cgroup_lock_live_group(cgroup))
 348                return -ENODEV;
 349        retval = freezer_change_state(cgroup, goal_state);
 350        cgroup_unlock();
 351        return retval;
 352}
 353
 354static struct cftype files[] = {
 355        {
 356                .name = "state",
 357                .read_seq_string = freezer_read,
 358                .write_string = freezer_write,
 359        },
 360};
 361
 362static int freezer_populate(struct cgroup_subsys *ss, struct cgroup *cgroup)
 363{
 364        if (!cgroup->parent)
 365                return 0;
 366        return cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files));
 367}
 368
 369struct cgroup_subsys freezer_subsys = {
 370        .name           = "freezer",
 371        .create         = freezer_create,
 372        .destroy        = freezer_destroy,
 373        .populate       = freezer_populate,
 374        .subsys_id      = freezer_subsys_id,
 375        .can_attach     = freezer_can_attach,
 376        .attach         = NULL,
 377        .fork           = freezer_fork,
 378        .exit           = NULL,
 379};
 380
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.