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