The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kernel/cgroup_freezer.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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  */
   32 enum 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 
   42 struct freezer {
   43         struct cgroup_subsys_state      css;
   44         unsigned int                    state;
   45         spinlock_t                      lock;
   46 };
   47 
   48 static 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 
   54 static 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 
   60 static 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 
   69 bool 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  */
   84 static 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 
   93 struct cgroup_subsys freezer_subsys;
   94 
   95 static 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  */
  115 static 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  */
  150 static 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 
  164 static 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  */
  178 static 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 
  223 static 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);
  241 out:
  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  */
  261 static 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;
  302 out_iter_end:
  303         cgroup_iter_end(cgroup, &it);
  304 out_unlock:
  305         spin_unlock_irq(&freezer->lock);
  306 }
  307 
  308 static 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 
  327 static 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 
  339 static 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  */
  360 static 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  */
  396 static 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 
  428 static 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 
  444 static 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 
  451 static 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 
  458 static 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 
  478 struct 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 };

Cache object: 11ca9c951e9bbf0cbf1995182f8347c1


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.