root/kernel/cgroup/legacy_freezer.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. css_freezer
  2. task_freezer
  3. parent_freezer
  4. cgroup_freezing
  5. freezer_state_strs
  6. freezer_css_alloc
  7. freezer_css_online
  8. freezer_css_offline
  9. freezer_css_free
  10. freezer_attach
  11. freezer_fork
  12. update_if_frozen
  13. freezer_read
  14. freeze_cgroup
  15. unfreeze_cgroup
  16. freezer_apply_state
  17. freezer_change_state
  18. freezer_write
  19. freezer_self_freezing_read
  20. freezer_parent_freezing_read

   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  */
  33 enum 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 
  43 struct freezer {
  44         struct cgroup_subsys_state      css;
  45         unsigned int                    state;
  46 };
  47 
  48 static DEFINE_MUTEX(freezer_mutex);
  49 
  50 static inline struct freezer *css_freezer(struct cgroup_subsys_state *css)
  51 {
  52         return css ? container_of(css, struct freezer, css) : NULL;
  53 }
  54 
  55 static inline struct freezer *task_freezer(struct task_struct *task)
  56 {
  57         return css_freezer(task_css(task, freezer_cgrp_id));
  58 }
  59 
  60 static struct freezer *parent_freezer(struct freezer *freezer)
  61 {
  62         return css_freezer(freezer->css.parent);
  63 }
  64 
  65 bool 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 
  76 static 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 
  85 static struct cgroup_subsys_state *
  86 freezer_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  */
 105 static 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  */
 130 static 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 
 144 static 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  */
 158 static void freezer_attach(struct cgroup_taskset *tset)
 159 {
 160         struct task_struct *task;
 161         struct cgroup_subsys_state *new_css;
 162 
 163         mutex_lock(&freezer_mutex);
 164 
 165         /*
 166          * Make the new tasks conform to the current state of @new_css.
 167          * For simplicity, when migrating any task to a FROZEN cgroup, we
 168          * revert it to FREEZING and let update_if_frozen() determine the
 169          * correct state later.
 170          *
 171          * Tasks in @tset are on @new_css but may not conform to its
 172          * current state before executing the following - !frozen tasks may
 173          * be visible in a FROZEN cgroup and frozen tasks in a THAWED one.
 174          */
 175         cgroup_taskset_for_each(task, new_css, tset) {
 176                 struct freezer *freezer = css_freezer(new_css);
 177 
 178                 if (!(freezer->state & CGROUP_FREEZING)) {
 179                         __thaw_task(task);
 180                 } else {
 181                         freeze_task(task);
 182                         /* clear FROZEN and propagate upwards */
 183                         while (freezer && (freezer->state & CGROUP_FROZEN)) {
 184                                 freezer->state &= ~CGROUP_FROZEN;
 185                                 freezer = parent_freezer(freezer);
 186                         }
 187                 }
 188         }
 189 
 190         mutex_unlock(&freezer_mutex);
 191 }
 192 
 193 /**
 194  * freezer_fork - cgroup post fork callback
 195  * @task: a task which has just been forked
 196  *
 197  * @task has just been created and should conform to the current state of
 198  * the cgroup_freezer it belongs to.  This function may race against
 199  * freezer_attach().  Losing to freezer_attach() means that we don't have
 200  * to do anything as freezer_attach() will put @task into the appropriate
 201  * state.
 202  */
 203 static void freezer_fork(struct task_struct *task)
 204 {
 205         struct freezer *freezer;
 206 
 207         /*
 208          * The root cgroup is non-freezable, so we can skip locking the
 209          * freezer.  This is safe regardless of race with task migration.
 210          * If we didn't race or won, skipping is obviously the right thing
 211          * to do.  If we lost and root is the new cgroup, noop is still the
 212          * right thing to do.
 213          */
 214         if (task_css_is_root(task, freezer_cgrp_id))
 215                 return;
 216 
 217         mutex_lock(&freezer_mutex);
 218         rcu_read_lock();
 219 
 220         freezer = task_freezer(task);
 221         if (freezer->state & CGROUP_FREEZING)
 222                 freeze_task(task);
 223 
 224         rcu_read_unlock();
 225         mutex_unlock(&freezer_mutex);
 226 }
 227 
 228 /**
 229  * update_if_frozen - update whether a cgroup finished freezing
 230  * @css: css of interest
 231  *
 232  * Once FREEZING is initiated, transition to FROZEN is lazily updated by
 233  * calling this function.  If the current state is FREEZING but not FROZEN,
 234  * this function checks whether all tasks of this cgroup and the descendant
 235  * cgroups finished freezing and, if so, sets FROZEN.
 236  *
 237  * The caller is responsible for grabbing RCU read lock and calling
 238  * update_if_frozen() on all descendants prior to invoking this function.
 239  *
 240  * Task states and freezer state might disagree while tasks are being
 241  * migrated into or out of @css, so we can't verify task states against
 242  * @freezer state here.  See freezer_attach() for details.
 243  */
 244 static void update_if_frozen(struct cgroup_subsys_state *css)
 245 {
 246         struct freezer *freezer = css_freezer(css);
 247         struct cgroup_subsys_state *pos;
 248         struct css_task_iter it;
 249         struct task_struct *task;
 250 
 251         lockdep_assert_held(&freezer_mutex);
 252 
 253         if (!(freezer->state & CGROUP_FREEZING) ||
 254             (freezer->state & CGROUP_FROZEN))
 255                 return;
 256 
 257         /* are all (live) children frozen? */
 258         rcu_read_lock();
 259         css_for_each_child(pos, css) {
 260                 struct freezer *child = css_freezer(pos);
 261 
 262                 if ((child->state & CGROUP_FREEZER_ONLINE) &&
 263                     !(child->state & CGROUP_FROZEN)) {
 264                         rcu_read_unlock();
 265                         return;
 266                 }
 267         }
 268         rcu_read_unlock();
 269 
 270         /* are all tasks frozen? */
 271         css_task_iter_start(css, 0, &it);
 272 
 273         while ((task = css_task_iter_next(&it))) {
 274                 if (freezing(task)) {
 275                         /*
 276                          * freezer_should_skip() indicates that the task
 277                          * should be skipped when determining freezing
 278                          * completion.  Consider it frozen in addition to
 279                          * the usual frozen condition.
 280                          */
 281                         if (!frozen(task) && !freezer_should_skip(task))
 282                                 goto out_iter_end;
 283                 }
 284         }
 285 
 286         freezer->state |= CGROUP_FROZEN;
 287 out_iter_end:
 288         css_task_iter_end(&it);
 289 }
 290 
 291 static int freezer_read(struct seq_file *m, void *v)
 292 {
 293         struct cgroup_subsys_state *css = seq_css(m), *pos;
 294 
 295         mutex_lock(&freezer_mutex);
 296         rcu_read_lock();
 297 
 298         /* update states bottom-up */
 299         css_for_each_descendant_post(pos, css) {
 300                 if (!css_tryget_online(pos))
 301                         continue;
 302                 rcu_read_unlock();
 303 
 304                 update_if_frozen(pos);
 305 
 306                 rcu_read_lock();
 307                 css_put(pos);
 308         }
 309 
 310         rcu_read_unlock();
 311         mutex_unlock(&freezer_mutex);
 312 
 313         seq_puts(m, freezer_state_strs(css_freezer(css)->state));
 314         seq_putc(m, '\n');
 315         return 0;
 316 }
 317 
 318 static void freeze_cgroup(struct freezer *freezer)
 319 {
 320         struct css_task_iter it;
 321         struct task_struct *task;
 322 
 323         css_task_iter_start(&freezer->css, 0, &it);
 324         while ((task = css_task_iter_next(&it)))
 325                 freeze_task(task);
 326         css_task_iter_end(&it);
 327 }
 328 
 329 static void unfreeze_cgroup(struct freezer *freezer)
 330 {
 331         struct css_task_iter it;
 332         struct task_struct *task;
 333 
 334         css_task_iter_start(&freezer->css, 0, &it);
 335         while ((task = css_task_iter_next(&it)))
 336                 __thaw_task(task);
 337         css_task_iter_end(&it);
 338 }
 339 
 340 /**
 341  * freezer_apply_state - apply state change to a single cgroup_freezer
 342  * @freezer: freezer to apply state change to
 343  * @freeze: whether to freeze or unfreeze
 344  * @state: CGROUP_FREEZING_* flag to set or clear
 345  *
 346  * Set or clear @state on @cgroup according to @freeze, and perform
 347  * freezing or thawing as necessary.
 348  */
 349 static void freezer_apply_state(struct freezer *freezer, bool freeze,
 350                                 unsigned int state)
 351 {
 352         /* also synchronizes against task migration, see freezer_attach() */
 353         lockdep_assert_held(&freezer_mutex);
 354 
 355         if (!(freezer->state & CGROUP_FREEZER_ONLINE))
 356                 return;
 357 
 358         if (freeze) {
 359                 if (!(freezer->state & CGROUP_FREEZING))
 360                         atomic_inc(&system_freezing_cnt);
 361                 freezer->state |= state;
 362                 freeze_cgroup(freezer);
 363         } else {
 364                 bool was_freezing = freezer->state & CGROUP_FREEZING;
 365 
 366                 freezer->state &= ~state;
 367 
 368                 if (!(freezer->state & CGROUP_FREEZING)) {
 369                         if (was_freezing)
 370                                 atomic_dec(&system_freezing_cnt);
 371                         freezer->state &= ~CGROUP_FROZEN;
 372                         unfreeze_cgroup(freezer);
 373                 }
 374         }
 375 }
 376 
 377 /**
 378  * freezer_change_state - change the freezing state of a cgroup_freezer
 379  * @freezer: freezer of interest
 380  * @freeze: whether to freeze or thaw
 381  *
 382  * Freeze or thaw @freezer according to @freeze.  The operations are
 383  * recursive - all descendants of @freezer will be affected.
 384  */
 385 static void freezer_change_state(struct freezer *freezer, bool freeze)
 386 {
 387         struct cgroup_subsys_state *pos;
 388 
 389         /*
 390          * Update all its descendants in pre-order traversal.  Each
 391          * descendant will try to inherit its parent's FREEZING state as
 392          * CGROUP_FREEZING_PARENT.
 393          */
 394         mutex_lock(&freezer_mutex);
 395         rcu_read_lock();
 396         css_for_each_descendant_pre(pos, &freezer->css) {
 397                 struct freezer *pos_f = css_freezer(pos);
 398                 struct freezer *parent = parent_freezer(pos_f);
 399 
 400                 if (!css_tryget_online(pos))
 401                         continue;
 402                 rcu_read_unlock();
 403 
 404                 if (pos_f == freezer)
 405                         freezer_apply_state(pos_f, freeze,
 406                                             CGROUP_FREEZING_SELF);
 407                 else
 408                         freezer_apply_state(pos_f,
 409                                             parent->state & CGROUP_FREEZING,
 410                                             CGROUP_FREEZING_PARENT);
 411 
 412                 rcu_read_lock();
 413                 css_put(pos);
 414         }
 415         rcu_read_unlock();
 416         mutex_unlock(&freezer_mutex);
 417 }
 418 
 419 static ssize_t freezer_write(struct kernfs_open_file *of,
 420                              char *buf, size_t nbytes, loff_t off)
 421 {
 422         bool freeze;
 423 
 424         buf = strstrip(buf);
 425 
 426         if (strcmp(buf, freezer_state_strs(0)) == 0)
 427                 freeze = false;
 428         else if (strcmp(buf, freezer_state_strs(CGROUP_FROZEN)) == 0)
 429                 freeze = true;
 430         else
 431                 return -EINVAL;
 432 
 433         freezer_change_state(css_freezer(of_css(of)), freeze);
 434         return nbytes;
 435 }
 436 
 437 static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css,
 438                                       struct cftype *cft)
 439 {
 440         struct freezer *freezer = css_freezer(css);
 441 
 442         return (bool)(freezer->state & CGROUP_FREEZING_SELF);
 443 }
 444 
 445 static u64 freezer_parent_freezing_read(struct cgroup_subsys_state *css,
 446                                         struct cftype *cft)
 447 {
 448         struct freezer *freezer = css_freezer(css);
 449 
 450         return (bool)(freezer->state & CGROUP_FREEZING_PARENT);
 451 }
 452 
 453 static struct cftype files[] = {
 454         {
 455                 .name = "state",
 456                 .flags = CFTYPE_NOT_ON_ROOT,
 457                 .seq_show = freezer_read,
 458                 .write = freezer_write,
 459         },
 460         {
 461                 .name = "self_freezing",
 462                 .flags = CFTYPE_NOT_ON_ROOT,
 463                 .read_u64 = freezer_self_freezing_read,
 464         },
 465         {
 466                 .name = "parent_freezing",
 467                 .flags = CFTYPE_NOT_ON_ROOT,
 468                 .read_u64 = freezer_parent_freezing_read,
 469         },
 470         { }     /* terminate */
 471 };
 472 
 473 struct cgroup_subsys freezer_cgrp_subsys = {
 474         .css_alloc      = freezer_css_alloc,
 475         .css_online     = freezer_css_online,
 476         .css_offline    = freezer_css_offline,
 477         .css_free       = freezer_css_free,
 478         .attach         = freezer_attach,
 479         .fork           = freezer_fork,
 480         .legacy_cftypes = files,
 481 };

/* [<][>][^][v][top][bottom][index][help] */