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