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