150943f3eSRoman Gushchin /*
250943f3eSRoman Gushchin * cgroup_freezer.c - control group freezer subsystem
350943f3eSRoman Gushchin *
450943f3eSRoman Gushchin * Copyright IBM Corporation, 2007
550943f3eSRoman Gushchin *
650943f3eSRoman Gushchin * Author : Cedric Le Goater <clg@fr.ibm.com>
750943f3eSRoman Gushchin *
850943f3eSRoman Gushchin * This program is free software; you can redistribute it and/or modify it
950943f3eSRoman Gushchin * under the terms of version 2.1 of the GNU Lesser General Public License
1050943f3eSRoman Gushchin * as published by the Free Software Foundation.
1150943f3eSRoman Gushchin *
1250943f3eSRoman Gushchin * This program is distributed in the hope that it would be useful, but
1350943f3eSRoman Gushchin * WITHOUT ANY WARRANTY; without even the implied warranty of
1450943f3eSRoman Gushchin * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1550943f3eSRoman Gushchin */
1650943f3eSRoman Gushchin
1750943f3eSRoman Gushchin #include <linux/export.h>
1850943f3eSRoman Gushchin #include <linux/slab.h>
1950943f3eSRoman Gushchin #include <linux/cgroup.h>
2050943f3eSRoman Gushchin #include <linux/fs.h>
2150943f3eSRoman Gushchin #include <linux/uaccess.h>
2250943f3eSRoman Gushchin #include <linux/freezer.h>
2350943f3eSRoman Gushchin #include <linux/seq_file.h>
2450943f3eSRoman Gushchin #include <linux/mutex.h>
2557dcd64cSTetsuo Handa #include <linux/cpu.h>
2650943f3eSRoman Gushchin
2750943f3eSRoman Gushchin /*
2850943f3eSRoman Gushchin * A cgroup is freezing if any FREEZING flags are set. FREEZING_SELF is
2950943f3eSRoman Gushchin * set if "FROZEN" is written to freezer.state cgroupfs file, and cleared
3050943f3eSRoman Gushchin * for "THAWED". FREEZING_PARENT is set if the parent freezer is FREEZING
3150943f3eSRoman Gushchin * for whatever reason. IOW, a cgroup has FREEZING_PARENT set if one of
3250943f3eSRoman Gushchin * its ancestors has FREEZING_SELF set.
3350943f3eSRoman Gushchin */
3450943f3eSRoman Gushchin enum freezer_state_flags {
3550943f3eSRoman Gushchin CGROUP_FREEZER_ONLINE = (1 << 0), /* freezer is fully online */
3650943f3eSRoman Gushchin CGROUP_FREEZING_SELF = (1 << 1), /* this freezer is freezing */
3750943f3eSRoman Gushchin CGROUP_FREEZING_PARENT = (1 << 2), /* the parent freezer is freezing */
3850943f3eSRoman Gushchin CGROUP_FROZEN = (1 << 3), /* this and its descendants frozen */
3950943f3eSRoman Gushchin
4050943f3eSRoman Gushchin /* mask for all FREEZING flags */
4150943f3eSRoman Gushchin CGROUP_FREEZING = CGROUP_FREEZING_SELF | CGROUP_FREEZING_PARENT,
4250943f3eSRoman Gushchin };
4350943f3eSRoman Gushchin
4450943f3eSRoman Gushchin struct freezer {
4550943f3eSRoman Gushchin struct cgroup_subsys_state css;
4650943f3eSRoman Gushchin unsigned int state;
4750943f3eSRoman Gushchin };
4850943f3eSRoman Gushchin
4950943f3eSRoman Gushchin static DEFINE_MUTEX(freezer_mutex);
5050943f3eSRoman Gushchin
css_freezer(struct cgroup_subsys_state * css)5150943f3eSRoman Gushchin static inline struct freezer *css_freezer(struct cgroup_subsys_state *css)
5250943f3eSRoman Gushchin {
5350943f3eSRoman Gushchin return css ? container_of(css, struct freezer, css) : NULL;
5450943f3eSRoman Gushchin }
5550943f3eSRoman Gushchin
task_freezer(struct task_struct * task)5650943f3eSRoman Gushchin static inline struct freezer *task_freezer(struct task_struct *task)
5750943f3eSRoman Gushchin {
5850943f3eSRoman Gushchin return css_freezer(task_css(task, freezer_cgrp_id));
5950943f3eSRoman Gushchin }
6050943f3eSRoman Gushchin
parent_freezer(struct freezer * freezer)6150943f3eSRoman Gushchin static struct freezer *parent_freezer(struct freezer *freezer)
6250943f3eSRoman Gushchin {
6350943f3eSRoman Gushchin return css_freezer(freezer->css.parent);
6450943f3eSRoman Gushchin }
6550943f3eSRoman Gushchin
cgroup_freezing(struct task_struct * task)6650943f3eSRoman Gushchin bool cgroup_freezing(struct task_struct *task)
6750943f3eSRoman Gushchin {
6850943f3eSRoman Gushchin bool ret;
69*9ec2d926STim Van Patten unsigned int state;
7050943f3eSRoman Gushchin
7150943f3eSRoman Gushchin rcu_read_lock();
72*9ec2d926STim Van Patten /* Check if the cgroup is still FREEZING, but not FROZEN. The extra
73*9ec2d926STim Van Patten * !FROZEN check is required, because the FREEZING bit is not cleared
74*9ec2d926STim Van Patten * when the state FROZEN is reached.
75*9ec2d926STim Van Patten */
76*9ec2d926STim Van Patten state = task_freezer(task)->state;
77*9ec2d926STim Van Patten ret = (state & CGROUP_FREEZING) && !(state & CGROUP_FROZEN);
7850943f3eSRoman Gushchin rcu_read_unlock();
7950943f3eSRoman Gushchin
8050943f3eSRoman Gushchin return ret;
8150943f3eSRoman Gushchin }
8250943f3eSRoman Gushchin
freezer_state_strs(unsigned int state)8350943f3eSRoman Gushchin static const char *freezer_state_strs(unsigned int state)
8450943f3eSRoman Gushchin {
8550943f3eSRoman Gushchin if (state & CGROUP_FROZEN)
8650943f3eSRoman Gushchin return "FROZEN";
8750943f3eSRoman Gushchin if (state & CGROUP_FREEZING)
8850943f3eSRoman Gushchin return "FREEZING";
8950943f3eSRoman Gushchin return "THAWED";
9050943f3eSRoman Gushchin };
9150943f3eSRoman Gushchin
9250943f3eSRoman Gushchin static struct cgroup_subsys_state *
freezer_css_alloc(struct cgroup_subsys_state * parent_css)9350943f3eSRoman Gushchin freezer_css_alloc(struct cgroup_subsys_state *parent_css)
9450943f3eSRoman Gushchin {
9550943f3eSRoman Gushchin struct freezer *freezer;
9650943f3eSRoman Gushchin
9750943f3eSRoman Gushchin freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
9850943f3eSRoman Gushchin if (!freezer)
9950943f3eSRoman Gushchin return ERR_PTR(-ENOMEM);
10050943f3eSRoman Gushchin
10150943f3eSRoman Gushchin return &freezer->css;
10250943f3eSRoman Gushchin }
10350943f3eSRoman Gushchin
10450943f3eSRoman Gushchin /**
10550943f3eSRoman Gushchin * freezer_css_online - commit creation of a freezer css
10650943f3eSRoman Gushchin * @css: css being created
10750943f3eSRoman Gushchin *
10850943f3eSRoman Gushchin * We're committing to creation of @css. Mark it online and inherit
10950943f3eSRoman Gushchin * parent's freezing state while holding both parent's and our
11050943f3eSRoman Gushchin * freezer->lock.
11150943f3eSRoman Gushchin */
freezer_css_online(struct cgroup_subsys_state * css)11250943f3eSRoman Gushchin static int freezer_css_online(struct cgroup_subsys_state *css)
11350943f3eSRoman Gushchin {
11450943f3eSRoman Gushchin struct freezer *freezer = css_freezer(css);
11550943f3eSRoman Gushchin struct freezer *parent = parent_freezer(freezer);
11650943f3eSRoman Gushchin
117f0cc7492STetsuo Handa cpus_read_lock();
11850943f3eSRoman Gushchin mutex_lock(&freezer_mutex);
11950943f3eSRoman Gushchin
12050943f3eSRoman Gushchin freezer->state |= CGROUP_FREEZER_ONLINE;
12150943f3eSRoman Gushchin
12250943f3eSRoman Gushchin if (parent && (parent->state & CGROUP_FREEZING)) {
12350943f3eSRoman Gushchin freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN;
124f0cc7492STetsuo Handa static_branch_inc_cpuslocked(&freezer_active);
12550943f3eSRoman Gushchin }
12650943f3eSRoman Gushchin
12750943f3eSRoman Gushchin mutex_unlock(&freezer_mutex);
128f0cc7492STetsuo Handa cpus_read_unlock();
12950943f3eSRoman Gushchin return 0;
13050943f3eSRoman Gushchin }
13150943f3eSRoman Gushchin
13250943f3eSRoman Gushchin /**
13350943f3eSRoman Gushchin * freezer_css_offline - initiate destruction of a freezer css
13450943f3eSRoman Gushchin * @css: css being destroyed
13550943f3eSRoman Gushchin *
13650943f3eSRoman Gushchin * @css is going away. Mark it dead and decrement system_freezing_count if
13750943f3eSRoman Gushchin * it was holding one.
13850943f3eSRoman Gushchin */
freezer_css_offline(struct cgroup_subsys_state * css)13950943f3eSRoman Gushchin static void freezer_css_offline(struct cgroup_subsys_state *css)
14050943f3eSRoman Gushchin {
14150943f3eSRoman Gushchin struct freezer *freezer = css_freezer(css);
14250943f3eSRoman Gushchin
143f0cc7492STetsuo Handa cpus_read_lock();
14450943f3eSRoman Gushchin mutex_lock(&freezer_mutex);
14550943f3eSRoman Gushchin
14650943f3eSRoman Gushchin if (freezer->state & CGROUP_FREEZING)
147f0cc7492STetsuo Handa static_branch_dec_cpuslocked(&freezer_active);
14850943f3eSRoman Gushchin
14950943f3eSRoman Gushchin freezer->state = 0;
15050943f3eSRoman Gushchin
15150943f3eSRoman Gushchin mutex_unlock(&freezer_mutex);
152f0cc7492STetsuo Handa cpus_read_unlock();
15350943f3eSRoman Gushchin }
15450943f3eSRoman Gushchin
freezer_css_free(struct cgroup_subsys_state * css)15550943f3eSRoman Gushchin static void freezer_css_free(struct cgroup_subsys_state *css)
15650943f3eSRoman Gushchin {
15750943f3eSRoman Gushchin kfree(css_freezer(css));
15850943f3eSRoman Gushchin }
15950943f3eSRoman Gushchin
16050943f3eSRoman Gushchin /*
16150943f3eSRoman Gushchin * Tasks can be migrated into a different freezer anytime regardless of its
16250943f3eSRoman Gushchin * current state. freezer_attach() is responsible for making new tasks
16350943f3eSRoman Gushchin * conform to the current state.
16450943f3eSRoman Gushchin *
16550943f3eSRoman Gushchin * Freezer state changes and task migration are synchronized via
16650943f3eSRoman Gushchin * @freezer->lock. freezer_attach() makes the new tasks conform to the
16750943f3eSRoman Gushchin * current state and all following state changes can see the new tasks.
16850943f3eSRoman Gushchin */
freezer_attach(struct cgroup_taskset * tset)16950943f3eSRoman Gushchin static void freezer_attach(struct cgroup_taskset *tset)
17050943f3eSRoman Gushchin {
17150943f3eSRoman Gushchin struct task_struct *task;
17250943f3eSRoman Gushchin struct cgroup_subsys_state *new_css;
17350943f3eSRoman Gushchin
17450943f3eSRoman Gushchin mutex_lock(&freezer_mutex);
17550943f3eSRoman Gushchin
17650943f3eSRoman Gushchin /*
17750943f3eSRoman Gushchin * Make the new tasks conform to the current state of @new_css.
17850943f3eSRoman Gushchin * For simplicity, when migrating any task to a FROZEN cgroup, we
17950943f3eSRoman Gushchin * revert it to FREEZING and let update_if_frozen() determine the
18050943f3eSRoman Gushchin * correct state later.
18150943f3eSRoman Gushchin *
18250943f3eSRoman Gushchin * Tasks in @tset are on @new_css but may not conform to its
18350943f3eSRoman Gushchin * current state before executing the following - !frozen tasks may
18450943f3eSRoman Gushchin * be visible in a FROZEN cgroup and frozen tasks in a THAWED one.
18550943f3eSRoman Gushchin */
18650943f3eSRoman Gushchin cgroup_taskset_for_each(task, new_css, tset) {
18750943f3eSRoman Gushchin struct freezer *freezer = css_freezer(new_css);
18850943f3eSRoman Gushchin
18950943f3eSRoman Gushchin if (!(freezer->state & CGROUP_FREEZING)) {
19050943f3eSRoman Gushchin __thaw_task(task);
19150943f3eSRoman Gushchin } else {
19250943f3eSRoman Gushchin freeze_task(task);
193f5d39b02SPeter Zijlstra
19450943f3eSRoman Gushchin /* clear FROZEN and propagate upwards */
19550943f3eSRoman Gushchin while (freezer && (freezer->state & CGROUP_FROZEN)) {
19650943f3eSRoman Gushchin freezer->state &= ~CGROUP_FROZEN;
19750943f3eSRoman Gushchin freezer = parent_freezer(freezer);
19850943f3eSRoman Gushchin }
19950943f3eSRoman Gushchin }
20050943f3eSRoman Gushchin }
20150943f3eSRoman Gushchin
20250943f3eSRoman Gushchin mutex_unlock(&freezer_mutex);
20350943f3eSRoman Gushchin }
20450943f3eSRoman Gushchin
20550943f3eSRoman Gushchin /**
20650943f3eSRoman Gushchin * freezer_fork - cgroup post fork callback
20750943f3eSRoman Gushchin * @task: a task which has just been forked
20850943f3eSRoman Gushchin *
20950943f3eSRoman Gushchin * @task has just been created and should conform to the current state of
21050943f3eSRoman Gushchin * the cgroup_freezer it belongs to. This function may race against
21150943f3eSRoman Gushchin * freezer_attach(). Losing to freezer_attach() means that we don't have
21250943f3eSRoman Gushchin * to do anything as freezer_attach() will put @task into the appropriate
21350943f3eSRoman Gushchin * state.
21450943f3eSRoman Gushchin */
freezer_fork(struct task_struct * task)21550943f3eSRoman Gushchin static void freezer_fork(struct task_struct *task)
21650943f3eSRoman Gushchin {
21750943f3eSRoman Gushchin struct freezer *freezer;
21850943f3eSRoman Gushchin
21950943f3eSRoman Gushchin /*
22050943f3eSRoman Gushchin * The root cgroup is non-freezable, so we can skip locking the
22150943f3eSRoman Gushchin * freezer. This is safe regardless of race with task migration.
22250943f3eSRoman Gushchin * If we didn't race or won, skipping is obviously the right thing
22350943f3eSRoman Gushchin * to do. If we lost and root is the new cgroup, noop is still the
22450943f3eSRoman Gushchin * right thing to do.
22550943f3eSRoman Gushchin */
22650943f3eSRoman Gushchin if (task_css_is_root(task, freezer_cgrp_id))
22750943f3eSRoman Gushchin return;
22850943f3eSRoman Gushchin
22950943f3eSRoman Gushchin mutex_lock(&freezer_mutex);
23050943f3eSRoman Gushchin rcu_read_lock();
23150943f3eSRoman Gushchin
23250943f3eSRoman Gushchin freezer = task_freezer(task);
23350943f3eSRoman Gushchin if (freezer->state & CGROUP_FREEZING)
23450943f3eSRoman Gushchin freeze_task(task);
23550943f3eSRoman Gushchin
23650943f3eSRoman Gushchin rcu_read_unlock();
23750943f3eSRoman Gushchin mutex_unlock(&freezer_mutex);
23850943f3eSRoman Gushchin }
23950943f3eSRoman Gushchin
24050943f3eSRoman Gushchin /**
24150943f3eSRoman Gushchin * update_if_frozen - update whether a cgroup finished freezing
24250943f3eSRoman Gushchin * @css: css of interest
24350943f3eSRoman Gushchin *
24450943f3eSRoman Gushchin * Once FREEZING is initiated, transition to FROZEN is lazily updated by
24550943f3eSRoman Gushchin * calling this function. If the current state is FREEZING but not FROZEN,
24650943f3eSRoman Gushchin * this function checks whether all tasks of this cgroup and the descendant
24750943f3eSRoman Gushchin * cgroups finished freezing and, if so, sets FROZEN.
24850943f3eSRoman Gushchin *
24950943f3eSRoman Gushchin * The caller is responsible for grabbing RCU read lock and calling
25050943f3eSRoman Gushchin * update_if_frozen() on all descendants prior to invoking this function.
25150943f3eSRoman Gushchin *
25250943f3eSRoman Gushchin * Task states and freezer state might disagree while tasks are being
25350943f3eSRoman Gushchin * migrated into or out of @css, so we can't verify task states against
25450943f3eSRoman Gushchin * @freezer state here. See freezer_attach() for details.
25550943f3eSRoman Gushchin */
update_if_frozen(struct cgroup_subsys_state * css)25650943f3eSRoman Gushchin static void update_if_frozen(struct cgroup_subsys_state *css)
25750943f3eSRoman Gushchin {
25850943f3eSRoman Gushchin struct freezer *freezer = css_freezer(css);
25950943f3eSRoman Gushchin struct cgroup_subsys_state *pos;
26050943f3eSRoman Gushchin struct css_task_iter it;
26150943f3eSRoman Gushchin struct task_struct *task;
26250943f3eSRoman Gushchin
26350943f3eSRoman Gushchin lockdep_assert_held(&freezer_mutex);
26450943f3eSRoman Gushchin
26550943f3eSRoman Gushchin if (!(freezer->state & CGROUP_FREEZING) ||
26650943f3eSRoman Gushchin (freezer->state & CGROUP_FROZEN))
26750943f3eSRoman Gushchin return;
26850943f3eSRoman Gushchin
26950943f3eSRoman Gushchin /* are all (live) children frozen? */
27050943f3eSRoman Gushchin rcu_read_lock();
27150943f3eSRoman Gushchin css_for_each_child(pos, css) {
27250943f3eSRoman Gushchin struct freezer *child = css_freezer(pos);
27350943f3eSRoman Gushchin
27450943f3eSRoman Gushchin if ((child->state & CGROUP_FREEZER_ONLINE) &&
27550943f3eSRoman Gushchin !(child->state & CGROUP_FROZEN)) {
27650943f3eSRoman Gushchin rcu_read_unlock();
27750943f3eSRoman Gushchin return;
27850943f3eSRoman Gushchin }
27950943f3eSRoman Gushchin }
28050943f3eSRoman Gushchin rcu_read_unlock();
28150943f3eSRoman Gushchin
28250943f3eSRoman Gushchin /* are all tasks frozen? */
28350943f3eSRoman Gushchin css_task_iter_start(css, 0, &it);
28450943f3eSRoman Gushchin
28550943f3eSRoman Gushchin while ((task = css_task_iter_next(&it))) {
286f5d39b02SPeter Zijlstra if (freezing(task) && !frozen(task))
28750943f3eSRoman Gushchin goto out_iter_end;
28850943f3eSRoman Gushchin }
28950943f3eSRoman Gushchin
29050943f3eSRoman Gushchin freezer->state |= CGROUP_FROZEN;
29150943f3eSRoman Gushchin out_iter_end:
29250943f3eSRoman Gushchin css_task_iter_end(&it);
29350943f3eSRoman Gushchin }
29450943f3eSRoman Gushchin
freezer_read(struct seq_file * m,void * v)29550943f3eSRoman Gushchin static int freezer_read(struct seq_file *m, void *v)
29650943f3eSRoman Gushchin {
29750943f3eSRoman Gushchin struct cgroup_subsys_state *css = seq_css(m), *pos;
29850943f3eSRoman Gushchin
29950943f3eSRoman Gushchin mutex_lock(&freezer_mutex);
30050943f3eSRoman Gushchin rcu_read_lock();
30150943f3eSRoman Gushchin
30250943f3eSRoman Gushchin /* update states bottom-up */
30350943f3eSRoman Gushchin css_for_each_descendant_post(pos, css) {
30450943f3eSRoman Gushchin if (!css_tryget_online(pos))
30550943f3eSRoman Gushchin continue;
30650943f3eSRoman Gushchin rcu_read_unlock();
30750943f3eSRoman Gushchin
30850943f3eSRoman Gushchin update_if_frozen(pos);
30950943f3eSRoman Gushchin
31050943f3eSRoman Gushchin rcu_read_lock();
31150943f3eSRoman Gushchin css_put(pos);
31250943f3eSRoman Gushchin }
31350943f3eSRoman Gushchin
31450943f3eSRoman Gushchin rcu_read_unlock();
31550943f3eSRoman Gushchin mutex_unlock(&freezer_mutex);
31650943f3eSRoman Gushchin
31750943f3eSRoman Gushchin seq_puts(m, freezer_state_strs(css_freezer(css)->state));
31850943f3eSRoman Gushchin seq_putc(m, '\n');
31950943f3eSRoman Gushchin return 0;
32050943f3eSRoman Gushchin }
32150943f3eSRoman Gushchin
freeze_cgroup(struct freezer * freezer)32250943f3eSRoman Gushchin static void freeze_cgroup(struct freezer *freezer)
32350943f3eSRoman Gushchin {
32450943f3eSRoman Gushchin struct css_task_iter it;
32550943f3eSRoman Gushchin struct task_struct *task;
32650943f3eSRoman Gushchin
32750943f3eSRoman Gushchin css_task_iter_start(&freezer->css, 0, &it);
32850943f3eSRoman Gushchin while ((task = css_task_iter_next(&it)))
32950943f3eSRoman Gushchin freeze_task(task);
33050943f3eSRoman Gushchin css_task_iter_end(&it);
33150943f3eSRoman Gushchin }
33250943f3eSRoman Gushchin
unfreeze_cgroup(struct freezer * freezer)33350943f3eSRoman Gushchin static void unfreeze_cgroup(struct freezer *freezer)
33450943f3eSRoman Gushchin {
33550943f3eSRoman Gushchin struct css_task_iter it;
33650943f3eSRoman Gushchin struct task_struct *task;
33750943f3eSRoman Gushchin
33850943f3eSRoman Gushchin css_task_iter_start(&freezer->css, 0, &it);
33950943f3eSRoman Gushchin while ((task = css_task_iter_next(&it)))
34050943f3eSRoman Gushchin __thaw_task(task);
34150943f3eSRoman Gushchin css_task_iter_end(&it);
34250943f3eSRoman Gushchin }
34350943f3eSRoman Gushchin
34450943f3eSRoman Gushchin /**
34550943f3eSRoman Gushchin * freezer_apply_state - apply state change to a single cgroup_freezer
34650943f3eSRoman Gushchin * @freezer: freezer to apply state change to
34750943f3eSRoman Gushchin * @freeze: whether to freeze or unfreeze
34850943f3eSRoman Gushchin * @state: CGROUP_FREEZING_* flag to set or clear
34950943f3eSRoman Gushchin *
35050943f3eSRoman Gushchin * Set or clear @state on @cgroup according to @freeze, and perform
35150943f3eSRoman Gushchin * freezing or thawing as necessary.
35250943f3eSRoman Gushchin */
freezer_apply_state(struct freezer * freezer,bool freeze,unsigned int state)35350943f3eSRoman Gushchin static void freezer_apply_state(struct freezer *freezer, bool freeze,
35450943f3eSRoman Gushchin unsigned int state)
35550943f3eSRoman Gushchin {
35650943f3eSRoman Gushchin /* also synchronizes against task migration, see freezer_attach() */
35750943f3eSRoman Gushchin lockdep_assert_held(&freezer_mutex);
35850943f3eSRoman Gushchin
35950943f3eSRoman Gushchin if (!(freezer->state & CGROUP_FREEZER_ONLINE))
36050943f3eSRoman Gushchin return;
36150943f3eSRoman Gushchin
36250943f3eSRoman Gushchin if (freeze) {
36350943f3eSRoman Gushchin if (!(freezer->state & CGROUP_FREEZING))
36457dcd64cSTetsuo Handa static_branch_inc_cpuslocked(&freezer_active);
36550943f3eSRoman Gushchin freezer->state |= state;
36650943f3eSRoman Gushchin freeze_cgroup(freezer);
36750943f3eSRoman Gushchin } else {
36850943f3eSRoman Gushchin bool was_freezing = freezer->state & CGROUP_FREEZING;
36950943f3eSRoman Gushchin
37050943f3eSRoman Gushchin freezer->state &= ~state;
37150943f3eSRoman Gushchin
37250943f3eSRoman Gushchin if (!(freezer->state & CGROUP_FREEZING)) {
37350943f3eSRoman Gushchin freezer->state &= ~CGROUP_FROZEN;
374f5d39b02SPeter Zijlstra if (was_freezing)
37557dcd64cSTetsuo Handa static_branch_dec_cpuslocked(&freezer_active);
37650943f3eSRoman Gushchin unfreeze_cgroup(freezer);
37750943f3eSRoman Gushchin }
37850943f3eSRoman Gushchin }
37950943f3eSRoman Gushchin }
38050943f3eSRoman Gushchin
38150943f3eSRoman Gushchin /**
38250943f3eSRoman Gushchin * freezer_change_state - change the freezing state of a cgroup_freezer
38350943f3eSRoman Gushchin * @freezer: freezer of interest
38450943f3eSRoman Gushchin * @freeze: whether to freeze or thaw
38550943f3eSRoman Gushchin *
38650943f3eSRoman Gushchin * Freeze or thaw @freezer according to @freeze. The operations are
38750943f3eSRoman Gushchin * recursive - all descendants of @freezer will be affected.
38850943f3eSRoman Gushchin */
freezer_change_state(struct freezer * freezer,bool freeze)38950943f3eSRoman Gushchin static void freezer_change_state(struct freezer *freezer, bool freeze)
39050943f3eSRoman Gushchin {
39150943f3eSRoman Gushchin struct cgroup_subsys_state *pos;
39250943f3eSRoman Gushchin
39357dcd64cSTetsuo Handa cpus_read_lock();
39450943f3eSRoman Gushchin /*
39550943f3eSRoman Gushchin * Update all its descendants in pre-order traversal. Each
39650943f3eSRoman Gushchin * descendant will try to inherit its parent's FREEZING state as
39750943f3eSRoman Gushchin * CGROUP_FREEZING_PARENT.
39850943f3eSRoman Gushchin */
39950943f3eSRoman Gushchin mutex_lock(&freezer_mutex);
40050943f3eSRoman Gushchin rcu_read_lock();
40150943f3eSRoman Gushchin css_for_each_descendant_pre(pos, &freezer->css) {
40250943f3eSRoman Gushchin struct freezer *pos_f = css_freezer(pos);
40350943f3eSRoman Gushchin struct freezer *parent = parent_freezer(pos_f);
40450943f3eSRoman Gushchin
40550943f3eSRoman Gushchin if (!css_tryget_online(pos))
40650943f3eSRoman Gushchin continue;
40750943f3eSRoman Gushchin rcu_read_unlock();
40850943f3eSRoman Gushchin
40950943f3eSRoman Gushchin if (pos_f == freezer)
41050943f3eSRoman Gushchin freezer_apply_state(pos_f, freeze,
41150943f3eSRoman Gushchin CGROUP_FREEZING_SELF);
41250943f3eSRoman Gushchin else
41350943f3eSRoman Gushchin freezer_apply_state(pos_f,
41450943f3eSRoman Gushchin parent->state & CGROUP_FREEZING,
41550943f3eSRoman Gushchin CGROUP_FREEZING_PARENT);
41650943f3eSRoman Gushchin
41750943f3eSRoman Gushchin rcu_read_lock();
41850943f3eSRoman Gushchin css_put(pos);
41950943f3eSRoman Gushchin }
42050943f3eSRoman Gushchin rcu_read_unlock();
42150943f3eSRoman Gushchin mutex_unlock(&freezer_mutex);
42257dcd64cSTetsuo Handa cpus_read_unlock();
42350943f3eSRoman Gushchin }
42450943f3eSRoman Gushchin
freezer_write(struct kernfs_open_file * of,char * buf,size_t nbytes,loff_t off)42550943f3eSRoman Gushchin static ssize_t freezer_write(struct kernfs_open_file *of,
42650943f3eSRoman Gushchin char *buf, size_t nbytes, loff_t off)
42750943f3eSRoman Gushchin {
42850943f3eSRoman Gushchin bool freeze;
42950943f3eSRoman Gushchin
43050943f3eSRoman Gushchin buf = strstrip(buf);
43150943f3eSRoman Gushchin
43250943f3eSRoman Gushchin if (strcmp(buf, freezer_state_strs(0)) == 0)
43350943f3eSRoman Gushchin freeze = false;
43450943f3eSRoman Gushchin else if (strcmp(buf, freezer_state_strs(CGROUP_FROZEN)) == 0)
43550943f3eSRoman Gushchin freeze = true;
43650943f3eSRoman Gushchin else
43750943f3eSRoman Gushchin return -EINVAL;
43850943f3eSRoman Gushchin
43950943f3eSRoman Gushchin freezer_change_state(css_freezer(of_css(of)), freeze);
44050943f3eSRoman Gushchin return nbytes;
44150943f3eSRoman Gushchin }
44250943f3eSRoman Gushchin
freezer_self_freezing_read(struct cgroup_subsys_state * css,struct cftype * cft)44350943f3eSRoman Gushchin static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css,
44450943f3eSRoman Gushchin struct cftype *cft)
44550943f3eSRoman Gushchin {
44650943f3eSRoman Gushchin struct freezer *freezer = css_freezer(css);
44750943f3eSRoman Gushchin
44850943f3eSRoman Gushchin return (bool)(freezer->state & CGROUP_FREEZING_SELF);
44950943f3eSRoman Gushchin }
45050943f3eSRoman Gushchin
freezer_parent_freezing_read(struct cgroup_subsys_state * css,struct cftype * cft)45150943f3eSRoman Gushchin static u64 freezer_parent_freezing_read(struct cgroup_subsys_state *css,
45250943f3eSRoman Gushchin struct cftype *cft)
45350943f3eSRoman Gushchin {
45450943f3eSRoman Gushchin struct freezer *freezer = css_freezer(css);
45550943f3eSRoman Gushchin
45650943f3eSRoman Gushchin return (bool)(freezer->state & CGROUP_FREEZING_PARENT);
45750943f3eSRoman Gushchin }
45850943f3eSRoman Gushchin
45950943f3eSRoman Gushchin static struct cftype files[] = {
46050943f3eSRoman Gushchin {
46150943f3eSRoman Gushchin .name = "state",
46250943f3eSRoman Gushchin .flags = CFTYPE_NOT_ON_ROOT,
46350943f3eSRoman Gushchin .seq_show = freezer_read,
46450943f3eSRoman Gushchin .write = freezer_write,
46550943f3eSRoman Gushchin },
46650943f3eSRoman Gushchin {
46750943f3eSRoman Gushchin .name = "self_freezing",
46850943f3eSRoman Gushchin .flags = CFTYPE_NOT_ON_ROOT,
46950943f3eSRoman Gushchin .read_u64 = freezer_self_freezing_read,
47050943f3eSRoman Gushchin },
47150943f3eSRoman Gushchin {
47250943f3eSRoman Gushchin .name = "parent_freezing",
47350943f3eSRoman Gushchin .flags = CFTYPE_NOT_ON_ROOT,
47450943f3eSRoman Gushchin .read_u64 = freezer_parent_freezing_read,
47550943f3eSRoman Gushchin },
47650943f3eSRoman Gushchin { } /* terminate */
47750943f3eSRoman Gushchin };
47850943f3eSRoman Gushchin
47950943f3eSRoman Gushchin struct cgroup_subsys freezer_cgrp_subsys = {
48050943f3eSRoman Gushchin .css_alloc = freezer_css_alloc,
48150943f3eSRoman Gushchin .css_online = freezer_css_online,
48250943f3eSRoman Gushchin .css_offline = freezer_css_offline,
48350943f3eSRoman Gushchin .css_free = freezer_css_free,
48450943f3eSRoman Gushchin .attach = freezer_attach,
48550943f3eSRoman Gushchin .fork = freezer_fork,
48650943f3eSRoman Gushchin .legacy_cftypes = files,
48750943f3eSRoman Gushchin };
488