LCOV - code coverage report
Current view: top level - kernel - cgroup_freezer.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 14 114 12.3 %
Date: 2015-04-12 14:34:49 Functions: 3 16 18.8 %

          Line data    Source code
       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           2 :         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           0 : bool cgroup_freezing(struct task_struct *task)
      66             : {
      67             :         bool ret;
      68             : 
      69             :         rcu_read_lock();
      70           0 :         ret = task_freezer(task)->state & CGROUP_FREEZING;
      71             :         rcu_read_unlock();
      72             : 
      73           0 :         return ret;
      74             : }
      75             : 
      76             : static const char *freezer_state_strs(unsigned int state)
      77             : {
      78           0 :         if (state & CGROUP_FROZEN)
      79             :                 return "FROZEN";
      80           0 :         if (state & CGROUP_FREEZING)
      81             :                 return "FREEZING";
      82             :         return "THAWED";
      83             : };
      84             : 
      85             : static struct cgroup_subsys_state *
      86           1 : freezer_css_alloc(struct cgroup_subsys_state *parent_css)
      87             : {
      88             :         struct freezer *freezer;
      89             : 
      90             :         freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
      91           1 :         if (!freezer)
      92             :                 return ERR_PTR(-ENOMEM);
      93             : 
      94           1 :         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           1 : static int freezer_css_online(struct cgroup_subsys_state *css)
     106             : {
     107           1 :         struct freezer *freezer = css_freezer(css);
     108             :         struct freezer *parent = parent_freezer(freezer);
     109             : 
     110           1 :         mutex_lock(&freezer_mutex);
     111             : 
     112           1 :         freezer->state |= CGROUP_FREEZER_ONLINE;
     113             : 
     114           1 :         if (parent && (parent->state & CGROUP_FREEZING)) {
     115           0 :                 freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN;
     116             :                 atomic_inc(&system_freezing_cnt);
     117             :         }
     118             : 
     119           1 :         mutex_unlock(&freezer_mutex);
     120           1 :         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           0 : static void freezer_css_offline(struct cgroup_subsys_state *css)
     131             : {
     132             :         struct freezer *freezer = css_freezer(css);
     133             : 
     134           0 :         mutex_lock(&freezer_mutex);
     135             : 
     136           0 :         if (freezer->state & CGROUP_FREEZING)
     137             :                 atomic_dec(&system_freezing_cnt);
     138             : 
     139           0 :         freezer->state = 0;
     140             : 
     141           0 :         mutex_unlock(&freezer_mutex);
     142           0 : }
     143             : 
     144           0 : static void freezer_css_free(struct cgroup_subsys_state *css)
     145             : {
     146           0 :         kfree(css_freezer(css));
     147           0 : }
     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           0 : static void freezer_attach(struct cgroup_subsys_state *new_css,
     159             :                            struct cgroup_taskset *tset)
     160             : {
     161           0 :         struct freezer *freezer = css_freezer(new_css);
     162             :         struct task_struct *task;
     163             :         bool clear_frozen = false;
     164             : 
     165           0 :         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           0 :         cgroup_taskset_for_each(task, tset) {
     178           0 :                 if (!(freezer->state & CGROUP_FREEZING)) {
     179           0 :                         __thaw_task(task);
     180             :                 } else {
     181           0 :                         freeze_task(task);
     182           0 :                         freezer->state &= ~CGROUP_FROZEN;
     183             :                         clear_frozen = true;
     184             :                 }
     185             :         }
     186             : 
     187             :         /* propagate FROZEN clearing upwards */
     188           0 :         while (clear_frozen && (freezer = parent_freezer(freezer))) {
     189           0 :                 freezer->state &= ~CGROUP_FROZEN;
     190           0 :                 clear_frozen = freezer->state & CGROUP_FREEZING;
     191             :         }
     192             : 
     193           0 :         mutex_unlock(&freezer_mutex);
     194           0 : }
     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             :  */
     206        2992 : static 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        2992 :         if (task_css_is_root(task, freezer_cgrp_id))
     218        2992 :                 return;
     219             : 
     220           0 :         mutex_lock(&freezer_mutex);
     221             :         rcu_read_lock();
     222             : 
     223             :         freezer = task_freezer(task);
     224           0 :         if (freezer->state & CGROUP_FREEZING)
     225           0 :                 freeze_task(task);
     226             : 
     227             :         rcu_read_unlock();
     228           0 :         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             :  */
     247           0 : static 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           0 :         if (!(freezer->state & CGROUP_FREEZING) ||
     257           0 :             (freezer->state & CGROUP_FROZEN))
     258           0 :                 return;
     259             : 
     260             :         /* are all (live) children frozen? */
     261             :         rcu_read_lock();
     262           0 :         css_for_each_child(pos, css) {
     263             :                 struct freezer *child = css_freezer(pos);
     264             : 
     265           0 :                 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           0 :         css_task_iter_start(css, &it);
     275             : 
     276           0 :         while ((task = css_task_iter_next(&it))) {
     277           0 :                 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           0 :                         if (!frozen(task) && !freezer_should_skip(task))
     285             :                                 goto out_iter_end;
     286             :                 }
     287             :         }
     288             : 
     289           0 :         freezer->state |= CGROUP_FROZEN;
     290             : out_iter_end:
     291           0 :         css_task_iter_end(&it);
     292             : }
     293             : 
     294           0 : static int freezer_read(struct seq_file *m, void *v)
     295             : {
     296             :         struct cgroup_subsys_state *css = seq_css(m), *pos;
     297             : 
     298           0 :         mutex_lock(&freezer_mutex);
     299             :         rcu_read_lock();
     300             : 
     301             :         /* update states bottom-up */
     302           0 :         css_for_each_descendant_post(pos, css) {
     303           0 :                 if (!css_tryget_online(pos))
     304           0 :                         continue;
     305             :                 rcu_read_unlock();
     306             : 
     307           0 :                 update_if_frozen(pos);
     308             : 
     309             :                 rcu_read_lock();
     310             :                 css_put(pos);
     311             :         }
     312             : 
     313             :         rcu_read_unlock();
     314           0 :         mutex_unlock(&freezer_mutex);
     315             : 
     316           0 :         seq_puts(m, freezer_state_strs(css_freezer(css)->state));
     317           0 :         seq_putc(m, '\n');
     318           0 :         return 0;
     319             : }
     320             : 
     321           0 : static void freeze_cgroup(struct freezer *freezer)
     322             : {
     323             :         struct css_task_iter it;
     324             :         struct task_struct *task;
     325             : 
     326           0 :         css_task_iter_start(&freezer->css, &it);
     327           0 :         while ((task = css_task_iter_next(&it)))
     328           0 :                 freeze_task(task);
     329           0 :         css_task_iter_end(&it);
     330           0 : }
     331             : 
     332           0 : static void unfreeze_cgroup(struct freezer *freezer)
     333             : {
     334             :         struct css_task_iter it;
     335             :         struct task_struct *task;
     336             : 
     337           0 :         css_task_iter_start(&freezer->css, &it);
     338           0 :         while ((task = css_task_iter_next(&it)))
     339           0 :                 __thaw_task(task);
     340           0 :         css_task_iter_end(&it);
     341           0 : }
     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             :  */
     352           0 : static 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           0 :         if (!(freezer->state & CGROUP_FREEZER_ONLINE))
     359           0 :                 return;
     360             : 
     361           0 :         if (freeze) {
     362           0 :                 if (!(freezer->state & CGROUP_FREEZING))
     363             :                         atomic_inc(&system_freezing_cnt);
     364           0 :                 freezer->state |= state;
     365           0 :                 freeze_cgroup(freezer);
     366             :         } else {
     367           0 :                 bool was_freezing = freezer->state & CGROUP_FREEZING;
     368             : 
     369           0 :                 freezer->state &= ~state;
     370             : 
     371           0 :                 if (!(freezer->state & CGROUP_FREEZING)) {
     372           0 :                         if (was_freezing)
     373             :                                 atomic_dec(&system_freezing_cnt);
     374           0 :                         freezer->state &= ~CGROUP_FROZEN;
     375           0 :                         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             :  */
     388           0 : static 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           0 :         mutex_lock(&freezer_mutex);
     398             :         rcu_read_lock();
     399           0 :         css_for_each_descendant_pre(pos, &freezer->css) {
     400           0 :                 struct freezer *pos_f = css_freezer(pos);
     401             :                 struct freezer *parent = parent_freezer(pos_f);
     402             : 
     403           0 :                 if (!css_tryget_online(pos))
     404           0 :                         continue;
     405             :                 rcu_read_unlock();
     406             : 
     407           0 :                 if (pos_f == freezer)
     408           0 :                         freezer_apply_state(pos_f, freeze,
     409             :                                             CGROUP_FREEZING_SELF);
     410             :                 else
     411           0 :                         freezer_apply_state(pos_f,
     412           0 :                                             parent->state & CGROUP_FREEZING,
     413             :                                             CGROUP_FREEZING_PARENT);
     414             : 
     415             :                 rcu_read_lock();
     416             :                 css_put(pos);
     417             :         }
     418             :         rcu_read_unlock();
     419           0 :         mutex_unlock(&freezer_mutex);
     420           0 : }
     421             : 
     422           0 : static 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           0 :         if (strcmp(buf, freezer_state_strs(0)) == 0)
     430             :                 freeze = false;
     431           0 :         else if (strcmp(buf, freezer_state_strs(CGROUP_FROZEN)) == 0)
     432             :                 freeze = true;
     433             :         else
     434             :                 return -EINVAL;
     435             : 
     436           0 :         freezer_change_state(css_freezer(of_css(of)), freeze);
     437           0 :         return nbytes;
     438             : }
     439             : 
     440           0 : static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css,
     441             :                                       struct cftype *cft)
     442             : {
     443             :         struct freezer *freezer = css_freezer(css);
     444             : 
     445           0 :         return (bool)(freezer->state & CGROUP_FREEZING_SELF);
     446             : }
     447             : 
     448           0 : static u64 freezer_parent_freezing_read(struct cgroup_subsys_state *css,
     449             :                                         struct cftype *cft)
     450             : {
     451             :         struct freezer *freezer = css_freezer(css);
     452             : 
     453           0 :         return (bool)(freezer->state & CGROUP_FREEZING_PARENT);
     454             : }
     455             : 
     456             : static 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             : 
     476             : struct 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             : };

Generated by: LCOV version 1.11