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
  25/*
  26 * A cgroup is freezing if any FREEZING flags are set.  FREEZING_SELF is
  27 * set if "FROZEN" is written to freezer.state cgroupfs file, and cleared
  28 * for "THAWED".  FREEZING_PARENT is set if the parent freezer is FREEZING
  29 * for whatever reason.  IOW, a cgroup has FREEZING_PARENT set if one of
  30 * its ancestors has FREEZING_SELF set.
  31 */
  32enum freezer_state_flags {
  33        CGROUP_FREEZER_ONLINE   = (1 << 0), /* freezer is fully online */
  34        CGROUP_FREEZING_SELF    = (1 << 1), /* this freezer is freezing */
  35        CGROUP_FREEZING_PARENT  = (1 << 2), /* the parent freezer is freezing */
  36        CGROUP_FROZEN           = (1 << 3), /* this and its descendants frozen */
  37
  38        /* mask for all FREEZING flags */
  39        CGROUP_FREEZING         = CGROUP_FREEZING_SELF | CGROUP_FREEZING_PARENT,
  40};
  41
  42struct freezer {
  43        struct cgroup_subsys_state      css;
  44        unsigned int                    state;
  45        spinlock_t                      lock;
  46};
  47
  48static inline struct freezer *css_freezer(struct cgroup_subsys_state *css)
  49{
  50        return css ? container_of(css, struct freezer, css) : NULL;
  51}
  52
  53static inline struct freezer *task_freezer(struct task_struct *task)
  54{
  55        return css_freezer(task_css(task, freezer_subsys_id));
  56}
  57
  58static struct freezer *parent_freezer(struct freezer *freezer)
  59{
  60        return css_freezer(css_parent(&freezer->css));
  61}
  62
  63bool cgroup_freezing(struct task_struct *task)
  64{
  65        bool ret;
  66
  67        rcu_read_lock();
  68        ret = task_freezer(task)->state & CGROUP_FREEZING;
  69        rcu_read_unlock();
  70
  71        return ret;
  72}
  73
  74/*
  75 * cgroups_write_string() limits the size of freezer state strings to
  76 * CGROUP_LOCAL_BUFFER_SIZE
  77 */
  78static const char *freezer_state_strs(unsigned int state)
  79{
  80        if (state & CGROUP_FROZEN)
  81                return "FROZEN";
  82        if (state & CGROUP_FREEZING)
  83                return "FREEZING";
  84        return "THAWED";
  85};
  86
  87struct cgroup_subsys freezer_subsys;
  88
  89static struct cgroup_subsys_state *
  90freezer_css_alloc(struct cgroup_subsys_state *parent_css)
  91{
  92        struct freezer *freezer;
  93
  94        freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
  95        if (!freezer)
  96                return ERR_PTR(-ENOMEM);
  97
  98        spin_lock_init(&freezer->lock);
  99        return &freezer->css;
 100}
 101
 102/**
 103 * freezer_css_online - commit creation of a freezer css
 104 * @css: css being created
 105 *
 106 * We're committing to creation of @css.  Mark it online and inherit
 107 * parent's freezing state while holding both parent's and our
 108 * freezer->lock.
 109 */
 110static int freezer_css_online(struct cgroup_subsys_state *css)
 111{
 112        struct freezer *freezer = css_freezer(css);
 113        struct freezer *parent = parent_freezer(freezer);
 114
 115        /*
 116         * The following double locking and freezing state inheritance
 117         * guarantee that @cgroup can never escape ancestors' freezing
 118         * states.  See css_for_each_descendant_pre() for details.
 119         */
 120        if (parent)
 121                spin_lock_irq(&parent->lock);
 122        spin_lock_nested(&freezer->lock, SINGLE_DEPTH_NESTING);
 123
 124        freezer->state |= CGROUP_FREEZER_ONLINE;
 125
 126        if (parent && (parent->state & CGROUP_FREEZING)) {
 127                freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN;
 128                atomic_inc(&system_freezing_cnt);
 129        }
 130
 131        spin_unlock(&freezer->lock);
 132        if (parent)
 133                spin_unlock_irq(&parent->lock);
 134
 135        return 0;
 136}
 137
 138/**
 139 * freezer_css_offline - initiate destruction of a freezer css
 140 * @css: css being destroyed
 141 *
 142 * @css is going away.  Mark it dead and decrement system_freezing_count if
 143 * it was holding one.
 144 */
 145static void freezer_css_offline(struct cgroup_subsys_state *css)
 146{
 147        struct freezer *freezer = css_freezer(css);
 148
 149        spin_lock_irq(&freezer->lock);
 150
 151        if (freezer->state & CGROUP_FREEZING)
 152                atomic_dec(&system_freezing_cnt);
 153
 154        freezer->state = 0;
 155
 156        spin_unlock_irq(&freezer->lock);
 157}
 158
 159static void freezer_css_free(struct cgroup_subsys_state *css)
 160{
 161        kfree(css_freezer(css));
 162}
 163
 164/*
 165 * Tasks can be migrated into a different freezer anytime regardless of its
 166 * current state.  freezer_attach() is responsible for making new tasks
 167 * conform to the current state.
 168 *
 169 * Freezer state changes and task migration are synchronized via
 170 * @freezer->lock.  freezer_attach() makes the new tasks conform to the
 171 * current state and all following state changes can see the new tasks.
 172 */
 173static void freezer_attach(struct cgroup_subsys_state *new_css,
 174                           struct cgroup_taskset *tset)
 175{
 176        struct freezer *freezer = css_freezer(new_css);
 177        struct task_struct *task;
 178        bool clear_frozen = false;
 179
 180        spin_lock_irq(&freezer->lock);
 181
 182        /*
 183         * Make the new tasks conform to the current state of @new_css.
 184         * For simplicity, when migrating any task to a FROZEN cgroup, we
 185         * revert it to FREEZING and let update_if_frozen() determine the
 186         * correct state later.
 187         *
 188         * Tasks in @tset are on @new_css but may not conform to its
 189         * current state before executing the following - !frozen tasks may
 190         * be visible in a FROZEN cgroup and frozen tasks in a THAWED one.
 191         */
 192        cgroup_taskset_for_each(task, new_css, tset) {
 193                if (!(freezer->state & CGROUP_FREEZING)) {
 194                        __thaw_task(task);
 195                } else {
 196                        freeze_task(task);
 197                        freezer->state &= ~CGROUP_FROZEN;
 198                        clear_frozen = true;
 199                }
 200        }
 201
 202        spin_unlock_irq(&freezer->lock);
 203
 204        /*
 205         * Propagate FROZEN clearing upwards.  We may race with
 206         * update_if_frozen(), but as long as both work bottom-up, either
 207         * update_if_frozen() sees child's FROZEN cleared or we clear the
 208         * parent's FROZEN later.  No parent w/ !FROZEN children can be
 209         * left FROZEN.
 210         */
 211        while (clear_frozen && (freezer = parent_freezer(freezer))) {
 212                spin_lock_irq(&freezer->lock);
 213                freezer->state &= ~CGROUP_FROZEN;
 214                clear_frozen = freezer->state & CGROUP_FREEZING;
 215                spin_unlock_irq(&freezer->lock);
 216        }
 217}
 218
 219static void freezer_fork(struct task_struct *task)
 220{
 221        struct freezer *freezer;
 222
 223        rcu_read_lock();
 224        freezer = task_freezer(task);
 225
 226        /*
 227         * The root cgroup is non-freezable, so we can skip the
 228         * following check.
 229         */
 230        if (!parent_freezer(freezer))
 231                goto out;
 232
 233        spin_lock_irq(&freezer->lock);
 234        if (freezer->state & CGROUP_FREEZING)
 235                freeze_task(task);
 236        spin_unlock_irq(&freezer->lock);
 237out:
 238        rcu_read_unlock();
 239}
 240
 241/**
 242 * update_if_frozen - update whether a cgroup finished freezing
 243 * @css: css of interest
 244 *
 245 * Once FREEZING is initiated, transition to FROZEN is lazily updated by
 246 * calling this function.  If the current state is FREEZING but not FROZEN,
 247 * this function checks whether all tasks of this cgroup and the descendant
 248 * cgroups finished freezing and, if so, sets FROZEN.
 249 *
 250 * The caller is responsible for grabbing RCU read lock and calling
 251 * update_if_frozen() on all descendants prior to invoking this function.
 252 *
 253 * Task states and freezer state might disagree while tasks are being
 254 * migrated into or out of @css, so we can't verify task states against
 255 * @freezer state here.  See freezer_attach() for details.
 256 */
 257static void update_if_frozen(struct cgroup_subsys_state *css)
 258{
 259        struct freezer *freezer = css_freezer(css);
 260        struct cgroup_subsys_state *pos;
 261        struct css_task_iter it;
 262        struct task_struct *task;
 263
 264        WARN_ON_ONCE(!rcu_read_lock_held());
 265
 266        spin_lock_irq(&freezer->lock);
 267
 268        if (!(freezer->state & CGROUP_FREEZING) ||
 269            (freezer->state & CGROUP_FROZEN))
 270                goto out_unlock;
 271
 272        /* are all (live) children frozen? */
 273        css_for_each_child(pos, css) {
 274                struct freezer *child = css_freezer(pos);
 275
 276                if ((child->state & CGROUP_FREEZER_ONLINE) &&
 277                    !(child->state & CGROUP_FROZEN))
 278                        goto out_unlock;
 279        }
 280
 281        /* are all tasks frozen? */
 282        css_task_iter_start(css, &it);
 283
 284        while ((task = css_task_iter_next(&it))) {
 285                if (freezing(task)) {
 286                        /*
 287                         * freezer_should_skip() indicates that the task
 288                         * should be skipped when determining freezing
 289                         * completion.  Consider it frozen in addition to
 290                         * the usual frozen condition.
 291                         */
 292                        if (!frozen(task) && !freezer_should_skip(task))
 293                                goto out_iter_end;
 294                }
 295        }
 296
 297        freezer->state |= CGROUP_FROZEN;
 298out_iter_end:
 299        css_task_iter_end(&it);
 300out_unlock:
 301        spin_unlock_irq(&freezer->lock);
 302}
 303
 304static int freezer_read(struct cgroup_subsys_state *css, struct cftype *cft,
 305                        struct seq_file *m)
 306{
 307        struct cgroup_subsys_state *pos;
 308
 309        rcu_read_lock();
 310
 311        /* update states bottom-up */
 312        css_for_each_descendant_post(pos, css)
 313                update_if_frozen(pos);
 314
 315        rcu_read_unlock();
 316
 317        seq_puts(m, freezer_state_strs(css_freezer(css)->state));
 318        seq_putc(m, '\n');
 319        return 0;
 320}
 321
 322static void freeze_cgroup(struct freezer *freezer)
 323{
 324        struct css_task_iter it;
 325        struct task_struct *task;
 326
 327        css_task_iter_start(&freezer->css, &it);
 328        while ((task = css_task_iter_next(&it)))
 329                freeze_task(task);
 330        css_task_iter_end(&it);
 331}
 332
 333static void unfreeze_cgroup(struct freezer *freezer)
 334{
 335        struct css_task_iter it;
 336        struct task_struct *task;
 337
 338        css_task_iter_start(&freezer->css, &it);
 339        while ((task = css_task_iter_next(&it)))
 340                __thaw_task(task);
 341        css_task_iter_end(&it);
 342}
 343
 344/**
 345 * freezer_apply_state - apply state change to a single cgroup_freezer
 346 * @freezer: freezer to apply state change to
 347 * @freeze: whether to freeze or unfreeze
 348 * @state: CGROUP_FREEZING_* flag to set or clear
 349 *
 350 * Set or clear @state on @cgroup according to @freeze, and perform
 351 * freezing or thawing as necessary.
 352 */
 353static void freezer_apply_state(struct freezer *freezer, bool freeze,
 354                                unsigned int state)
 355{
 356        /* also synchronizes against task migration, see freezer_attach() */
 357        lockdep_assert_held(&freezer->lock);
 358
 359        if (!(freezer->state & CGROUP_FREEZER_ONLINE))
 360                return;
 361
 362        if (freeze) {
 363                if (!(freezer->state & CGROUP_FREEZING))
 364                        atomic_inc(&system_freezing_cnt);
 365                freezer->state |= state;
 366                freeze_cgroup(freezer);
 367        } else {
 368                bool was_freezing = freezer->state & CGROUP_FREEZING;
 369
 370                freezer->state &= ~state;
 371
 372                if (!(freezer->state & CGROUP_FREEZING)) {
 373                        if (was_freezing)
 374                                atomic_dec(&system_freezing_cnt);
 375                        freezer->state &= ~CGROUP_FROZEN;
 376                        unfreeze_cgroup(freezer);
 377                }
 378        }
 379}
 380
 381/**
 382 * freezer_change_state - change the freezing state of a cgroup_freezer
 383 * @freezer: freezer of interest
 384 * @freeze: whether to freeze or thaw
 385 *
 386 * Freeze or thaw @freezer according to @freeze.  The operations are
 387 * recursive - all descendants of @freezer will be affected.
 388 */
 389static void freezer_change_state(struct freezer *freezer, bool freeze)
 390{
 391        struct cgroup_subsys_state *pos;
 392
 393        /*
 394         * Update all its descendants in pre-order traversal.  Each
 395         * descendant will try to inherit its parent's FREEZING state as
 396         * CGROUP_FREEZING_PARENT.
 397         */
 398        rcu_read_lock();
 399        css_for_each_descendant_pre(pos, &freezer->css) {
 400                struct freezer *pos_f = css_freezer(pos);
 401                struct freezer *parent = parent_freezer(pos_f);
 402
 403                spin_lock_irq(&pos_f->lock);
 404
 405                if (pos_f == freezer) {
 406                        freezer_apply_state(pos_f, freeze,
 407                                            CGROUP_FREEZING_SELF);
 408                } else {
 409                        /*
 410                         * Our update to @parent->state is already visible
 411                         * which is all we need.  No need to lock @parent.
 412                         * For more info on synchronization, see
 413                         * freezer_post_create().
 414                         */
 415                        freezer_apply_state(pos_f,
 416                                            parent->state & CGROUP_FREEZING,
 417                                            CGROUP_FREEZING_PARENT);
 418                }
 419
 420                spin_unlock_irq(&pos_f->lock);
 421        }
 422        rcu_read_unlock();
 423}
 424
 425static int freezer_write(struct cgroup_subsys_state *css, struct cftype *cft,
 426                         const char *buffer)
 427{
 428        bool freeze;
 429
 430        if (strcmp(buffer, freezer_state_strs(0)) == 0)
 431                freeze = false;
 432        else if (strcmp(buffer, freezer_state_strs(CGROUP_FROZEN)) == 0)
 433                freeze = true;
 434        else
 435                return -EINVAL;
 436
 437        freezer_change_state(css_freezer(css), freeze);
 438        return 0;
 439}
 440
 441static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css,
 442                                      struct cftype *cft)
 443{
 444        struct freezer *freezer = css_freezer(css);
 445
 446        return (bool)(freezer->state & CGROUP_FREEZING_SELF);
 447}
 448
 449static u64 freezer_parent_freezing_read(struct cgroup_subsys_state *css,
 450                                        struct cftype *cft)
 451{
 452        struct freezer *freezer = css_freezer(css);
 453
 454        return (bool)(freezer->state & CGROUP_FREEZING_PARENT);
 455}
 456
 457static struct cftype files[] = {
 458        {
 459                .name = "state",
 460                .flags = CFTYPE_NOT_ON_ROOT,
 461                .read_seq_string = freezer_read,
 462                .write_string = freezer_write,
 463        },
 464        {
 465                .name = "self_freezing",
 466                .flags = CFTYPE_NOT_ON_ROOT,
 467                .read_u64 = freezer_self_freezing_read,
 468        },
 469        {
 470                .name = "parent_freezing",
 471                .flags = CFTYPE_NOT_ON_ROOT,
 472                .read_u64 = freezer_parent_freezing_read,
 473        },
 474        { }     /* terminate */
 475};
 476
 477struct cgroup_subsys freezer_subsys = {
 478        .name           = "freezer",
 479        .css_alloc      = freezer_css_alloc,
 480        .css_online     = freezer_css_online,
 481        .css_offline    = freezer_css_offline,
 482        .css_free       = freezer_css_free,
 483        .subsys_id      = freezer_subsys_id,
 484        .attach         = freezer_attach,
 485        .fork           = freezer_fork,
 486        .base_cftypes   = files,
 487};
 488
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.