Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * cgroup_freezer.c -  control group freezer subsystem
0003  *
0004  * Copyright IBM Corporation, 2007
0005  *
0006  * Author : Cedric Le Goater <clg@fr.ibm.com>
0007  *
0008  * This program is free software; you can redistribute it and/or modify it
0009  * under the terms of version 2.1 of the GNU Lesser General Public License
0010  * as published by the Free Software Foundation.
0011  *
0012  * This program is distributed in the hope that it would be useful, but
0013  * WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
0015  */
0016 
0017 #include <linux/export.h>
0018 #include <linux/slab.h>
0019 #include <linux/cgroup.h>
0020 #include <linux/fs.h>
0021 #include <linux/uaccess.h>
0022 #include <linux/freezer.h>
0023 #include <linux/seq_file.h>
0024 #include <linux/mutex.h>
0025 
0026 /*
0027  * A cgroup is freezing if any FREEZING flags are set.  FREEZING_SELF is
0028  * set if "FROZEN" is written to freezer.state cgroupfs file, and cleared
0029  * for "THAWED".  FREEZING_PARENT is set if the parent freezer is FREEZING
0030  * for whatever reason.  IOW, a cgroup has FREEZING_PARENT set if one of
0031  * its ancestors has FREEZING_SELF set.
0032  */
0033 enum freezer_state_flags {
0034     CGROUP_FREEZER_ONLINE   = (1 << 0), /* freezer is fully online */
0035     CGROUP_FREEZING_SELF    = (1 << 1), /* this freezer is freezing */
0036     CGROUP_FREEZING_PARENT  = (1 << 2), /* the parent freezer is freezing */
0037     CGROUP_FROZEN       = (1 << 3), /* this and its descendants frozen */
0038 
0039     /* mask for all FREEZING flags */
0040     CGROUP_FREEZING     = CGROUP_FREEZING_SELF | CGROUP_FREEZING_PARENT,
0041 };
0042 
0043 struct freezer {
0044     struct cgroup_subsys_state  css;
0045     unsigned int            state;
0046 };
0047 
0048 static DEFINE_MUTEX(freezer_mutex);
0049 
0050 static inline struct freezer *css_freezer(struct cgroup_subsys_state *css)
0051 {
0052     return css ? container_of(css, struct freezer, css) : NULL;
0053 }
0054 
0055 static inline struct freezer *task_freezer(struct task_struct *task)
0056 {
0057     return css_freezer(task_css(task, freezer_cgrp_id));
0058 }
0059 
0060 static struct freezer *parent_freezer(struct freezer *freezer)
0061 {
0062     return css_freezer(freezer->css.parent);
0063 }
0064 
0065 bool cgroup_freezing(struct task_struct *task)
0066 {
0067     bool ret;
0068 
0069     rcu_read_lock();
0070     ret = task_freezer(task)->state & CGROUP_FREEZING;
0071     rcu_read_unlock();
0072 
0073     return ret;
0074 }
0075 
0076 static const char *freezer_state_strs(unsigned int state)
0077 {
0078     if (state & CGROUP_FROZEN)
0079         return "FROZEN";
0080     if (state & CGROUP_FREEZING)
0081         return "FREEZING";
0082     return "THAWED";
0083 };
0084 
0085 static struct cgroup_subsys_state *
0086 freezer_css_alloc(struct cgroup_subsys_state *parent_css)
0087 {
0088     struct freezer *freezer;
0089 
0090     freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
0091     if (!freezer)
0092         return ERR_PTR(-ENOMEM);
0093 
0094     return &freezer->css;
0095 }
0096 
0097 /**
0098  * freezer_css_online - commit creation of a freezer css
0099  * @css: css being created
0100  *
0101  * We're committing to creation of @css.  Mark it online and inherit
0102  * parent's freezing state while holding both parent's and our
0103  * freezer->lock.
0104  */
0105 static int freezer_css_online(struct cgroup_subsys_state *css)
0106 {
0107     struct freezer *freezer = css_freezer(css);
0108     struct freezer *parent = parent_freezer(freezer);
0109 
0110     mutex_lock(&freezer_mutex);
0111 
0112     freezer->state |= CGROUP_FREEZER_ONLINE;
0113 
0114     if (parent && (parent->state & CGROUP_FREEZING)) {
0115         freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN;
0116         atomic_inc(&system_freezing_cnt);
0117     }
0118 
0119     mutex_unlock(&freezer_mutex);
0120     return 0;
0121 }
0122 
0123 /**
0124  * freezer_css_offline - initiate destruction of a freezer css
0125  * @css: css being destroyed
0126  *
0127  * @css is going away.  Mark it dead and decrement system_freezing_count if
0128  * it was holding one.
0129  */
0130 static void freezer_css_offline(struct cgroup_subsys_state *css)
0131 {
0132     struct freezer *freezer = css_freezer(css);
0133 
0134     mutex_lock(&freezer_mutex);
0135 
0136     if (freezer->state & CGROUP_FREEZING)
0137         atomic_dec(&system_freezing_cnt);
0138 
0139     freezer->state = 0;
0140 
0141     mutex_unlock(&freezer_mutex);
0142 }
0143 
0144 static void freezer_css_free(struct cgroup_subsys_state *css)
0145 {
0146     kfree(css_freezer(css));
0147 }
0148 
0149 /*
0150  * Tasks can be migrated into a different freezer anytime regardless of its
0151  * current state.  freezer_attach() is responsible for making new tasks
0152  * conform to the current state.
0153  *
0154  * Freezer state changes and task migration are synchronized via
0155  * @freezer->lock.  freezer_attach() makes the new tasks conform to the
0156  * current state and all following state changes can see the new tasks.
0157  */
0158 static void freezer_attach(struct cgroup_taskset *tset)
0159 {
0160     struct task_struct *task;
0161     struct cgroup_subsys_state *new_css;
0162 
0163     mutex_lock(&freezer_mutex);
0164 
0165     /*
0166      * Make the new tasks conform to the current state of @new_css.
0167      * For simplicity, when migrating any task to a FROZEN cgroup, we
0168      * revert it to FREEZING and let update_if_frozen() determine the
0169      * correct state later.
0170      *
0171      * Tasks in @tset are on @new_css but may not conform to its
0172      * current state before executing the following - !frozen tasks may
0173      * be visible in a FROZEN cgroup and frozen tasks in a THAWED one.
0174      */
0175     cgroup_taskset_for_each(task, new_css, tset) {
0176         struct freezer *freezer = css_freezer(new_css);
0177 
0178         if (!(freezer->state & CGROUP_FREEZING)) {
0179             __thaw_task(task);
0180         } else {
0181             freeze_task(task);
0182             /* clear FROZEN and propagate upwards */
0183             while (freezer && (freezer->state & CGROUP_FROZEN)) {
0184                 freezer->state &= ~CGROUP_FROZEN;
0185                 freezer = parent_freezer(freezer);
0186             }
0187         }
0188     }
0189 
0190     mutex_unlock(&freezer_mutex);
0191 }
0192 
0193 /**
0194  * freezer_fork - cgroup post fork callback
0195  * @task: a task which has just been forked
0196  *
0197  * @task has just been created and should conform to the current state of
0198  * the cgroup_freezer it belongs to.  This function may race against
0199  * freezer_attach().  Losing to freezer_attach() means that we don't have
0200  * to do anything as freezer_attach() will put @task into the appropriate
0201  * state.
0202  */
0203 static void freezer_fork(struct task_struct *task)
0204 {
0205     struct freezer *freezer;
0206 
0207     /*
0208      * The root cgroup is non-freezable, so we can skip locking the
0209      * freezer.  This is safe regardless of race with task migration.
0210      * If we didn't race or won, skipping is obviously the right thing
0211      * to do.  If we lost and root is the new cgroup, noop is still the
0212      * right thing to do.
0213      */
0214     if (task_css_is_root(task, freezer_cgrp_id))
0215         return;
0216 
0217     mutex_lock(&freezer_mutex);
0218     rcu_read_lock();
0219 
0220     freezer = task_freezer(task);
0221     if (freezer->state & CGROUP_FREEZING)
0222         freeze_task(task);
0223 
0224     rcu_read_unlock();
0225     mutex_unlock(&freezer_mutex);
0226 }
0227 
0228 /**
0229  * update_if_frozen - update whether a cgroup finished freezing
0230  * @css: css of interest
0231  *
0232  * Once FREEZING is initiated, transition to FROZEN is lazily updated by
0233  * calling this function.  If the current state is FREEZING but not FROZEN,
0234  * this function checks whether all tasks of this cgroup and the descendant
0235  * cgroups finished freezing and, if so, sets FROZEN.
0236  *
0237  * The caller is responsible for grabbing RCU read lock and calling
0238  * update_if_frozen() on all descendants prior to invoking this function.
0239  *
0240  * Task states and freezer state might disagree while tasks are being
0241  * migrated into or out of @css, so we can't verify task states against
0242  * @freezer state here.  See freezer_attach() for details.
0243  */
0244 static void update_if_frozen(struct cgroup_subsys_state *css)
0245 {
0246     struct freezer *freezer = css_freezer(css);
0247     struct cgroup_subsys_state *pos;
0248     struct css_task_iter it;
0249     struct task_struct *task;
0250 
0251     lockdep_assert_held(&freezer_mutex);
0252 
0253     if (!(freezer->state & CGROUP_FREEZING) ||
0254         (freezer->state & CGROUP_FROZEN))
0255         return;
0256 
0257     /* are all (live) children frozen? */
0258     rcu_read_lock();
0259     css_for_each_child(pos, css) {
0260         struct freezer *child = css_freezer(pos);
0261 
0262         if ((child->state & CGROUP_FREEZER_ONLINE) &&
0263             !(child->state & CGROUP_FROZEN)) {
0264             rcu_read_unlock();
0265             return;
0266         }
0267     }
0268     rcu_read_unlock();
0269 
0270     /* are all tasks frozen? */
0271     css_task_iter_start(css, 0, &it);
0272 
0273     while ((task = css_task_iter_next(&it))) {
0274         if (freezing(task)) {
0275             /*
0276              * freezer_should_skip() indicates that the task
0277              * should be skipped when determining freezing
0278              * completion.  Consider it frozen in addition to
0279              * the usual frozen condition.
0280              */
0281             if (!frozen(task) && !freezer_should_skip(task))
0282                 goto out_iter_end;
0283         }
0284     }
0285 
0286     freezer->state |= CGROUP_FROZEN;
0287 out_iter_end:
0288     css_task_iter_end(&it);
0289 }
0290 
0291 static int freezer_read(struct seq_file *m, void *v)
0292 {
0293     struct cgroup_subsys_state *css = seq_css(m), *pos;
0294 
0295     mutex_lock(&freezer_mutex);
0296     rcu_read_lock();
0297 
0298     /* update states bottom-up */
0299     css_for_each_descendant_post(pos, css) {
0300         if (!css_tryget_online(pos))
0301             continue;
0302         rcu_read_unlock();
0303 
0304         update_if_frozen(pos);
0305 
0306         rcu_read_lock();
0307         css_put(pos);
0308     }
0309 
0310     rcu_read_unlock();
0311     mutex_unlock(&freezer_mutex);
0312 
0313     seq_puts(m, freezer_state_strs(css_freezer(css)->state));
0314     seq_putc(m, '\n');
0315     return 0;
0316 }
0317 
0318 static void freeze_cgroup(struct freezer *freezer)
0319 {
0320     struct css_task_iter it;
0321     struct task_struct *task;
0322 
0323     css_task_iter_start(&freezer->css, 0, &it);
0324     while ((task = css_task_iter_next(&it)))
0325         freeze_task(task);
0326     css_task_iter_end(&it);
0327 }
0328 
0329 static void unfreeze_cgroup(struct freezer *freezer)
0330 {
0331     struct css_task_iter it;
0332     struct task_struct *task;
0333 
0334     css_task_iter_start(&freezer->css, 0, &it);
0335     while ((task = css_task_iter_next(&it)))
0336         __thaw_task(task);
0337     css_task_iter_end(&it);
0338 }
0339 
0340 /**
0341  * freezer_apply_state - apply state change to a single cgroup_freezer
0342  * @freezer: freezer to apply state change to
0343  * @freeze: whether to freeze or unfreeze
0344  * @state: CGROUP_FREEZING_* flag to set or clear
0345  *
0346  * Set or clear @state on @cgroup according to @freeze, and perform
0347  * freezing or thawing as necessary.
0348  */
0349 static void freezer_apply_state(struct freezer *freezer, bool freeze,
0350                 unsigned int state)
0351 {
0352     /* also synchronizes against task migration, see freezer_attach() */
0353     lockdep_assert_held(&freezer_mutex);
0354 
0355     if (!(freezer->state & CGROUP_FREEZER_ONLINE))
0356         return;
0357 
0358     if (freeze) {
0359         if (!(freezer->state & CGROUP_FREEZING))
0360             atomic_inc(&system_freezing_cnt);
0361         freezer->state |= state;
0362         freeze_cgroup(freezer);
0363     } else {
0364         bool was_freezing = freezer->state & CGROUP_FREEZING;
0365 
0366         freezer->state &= ~state;
0367 
0368         if (!(freezer->state & CGROUP_FREEZING)) {
0369             if (was_freezing)
0370                 atomic_dec(&system_freezing_cnt);
0371             freezer->state &= ~CGROUP_FROZEN;
0372             unfreeze_cgroup(freezer);
0373         }
0374     }
0375 }
0376 
0377 /**
0378  * freezer_change_state - change the freezing state of a cgroup_freezer
0379  * @freezer: freezer of interest
0380  * @freeze: whether to freeze or thaw
0381  *
0382  * Freeze or thaw @freezer according to @freeze.  The operations are
0383  * recursive - all descendants of @freezer will be affected.
0384  */
0385 static void freezer_change_state(struct freezer *freezer, bool freeze)
0386 {
0387     struct cgroup_subsys_state *pos;
0388 
0389     /*
0390      * Update all its descendants in pre-order traversal.  Each
0391      * descendant will try to inherit its parent's FREEZING state as
0392      * CGROUP_FREEZING_PARENT.
0393      */
0394     mutex_lock(&freezer_mutex);
0395     rcu_read_lock();
0396     css_for_each_descendant_pre(pos, &freezer->css) {
0397         struct freezer *pos_f = css_freezer(pos);
0398         struct freezer *parent = parent_freezer(pos_f);
0399 
0400         if (!css_tryget_online(pos))
0401             continue;
0402         rcu_read_unlock();
0403 
0404         if (pos_f == freezer)
0405             freezer_apply_state(pos_f, freeze,
0406                         CGROUP_FREEZING_SELF);
0407         else
0408             freezer_apply_state(pos_f,
0409                         parent->state & CGROUP_FREEZING,
0410                         CGROUP_FREEZING_PARENT);
0411 
0412         rcu_read_lock();
0413         css_put(pos);
0414     }
0415     rcu_read_unlock();
0416     mutex_unlock(&freezer_mutex);
0417 }
0418 
0419 static ssize_t freezer_write(struct kernfs_open_file *of,
0420                  char *buf, size_t nbytes, loff_t off)
0421 {
0422     bool freeze;
0423 
0424     buf = strstrip(buf);
0425 
0426     if (strcmp(buf, freezer_state_strs(0)) == 0)
0427         freeze = false;
0428     else if (strcmp(buf, freezer_state_strs(CGROUP_FROZEN)) == 0)
0429         freeze = true;
0430     else
0431         return -EINVAL;
0432 
0433     freezer_change_state(css_freezer(of_css(of)), freeze);
0434     return nbytes;
0435 }
0436 
0437 static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css,
0438                       struct cftype *cft)
0439 {
0440     struct freezer *freezer = css_freezer(css);
0441 
0442     return (bool)(freezer->state & CGROUP_FREEZING_SELF);
0443 }
0444 
0445 static u64 freezer_parent_freezing_read(struct cgroup_subsys_state *css,
0446                     struct cftype *cft)
0447 {
0448     struct freezer *freezer = css_freezer(css);
0449 
0450     return (bool)(freezer->state & CGROUP_FREEZING_PARENT);
0451 }
0452 
0453 static struct cftype files[] = {
0454     {
0455         .name = "state",
0456         .flags = CFTYPE_NOT_ON_ROOT,
0457         .seq_show = freezer_read,
0458         .write = freezer_write,
0459     },
0460     {
0461         .name = "self_freezing",
0462         .flags = CFTYPE_NOT_ON_ROOT,
0463         .read_u64 = freezer_self_freezing_read,
0464     },
0465     {
0466         .name = "parent_freezing",
0467         .flags = CFTYPE_NOT_ON_ROOT,
0468         .read_u64 = freezer_parent_freezing_read,
0469     },
0470     { } /* terminate */
0471 };
0472 
0473 struct cgroup_subsys freezer_cgrp_subsys = {
0474     .css_alloc  = freezer_css_alloc,
0475     .css_online = freezer_css_online,
0476     .css_offline    = freezer_css_offline,
0477     .css_free   = freezer_css_free,
0478     .attach     = freezer_attach,
0479     .fork       = freezer_fork,
0480     .legacy_cftypes = files,
0481 };