cgroup_freezer: make freezer->state mask of flags
[GitHub/LineageOS/android_kernel_samsung_universal7580.git] / kernel / cgroup_freezer.c
CommitLineData
dc52ddc0
MH
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
9984de1a 17#include <linux/export.h>
5a0e3ad6 18#include <linux/slab.h>
dc52ddc0
MH
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
d6a2fe13
TH
25enum freezer_state_flags {
26 CGROUP_FREEZING = (1 << 1), /* this freezer is freezing */
27 CGROUP_FROZEN = (1 << 3), /* this and its descendants frozen */
dc52ddc0
MH
28};
29
30struct freezer {
bcd66c89 31 struct cgroup_subsys_state css;
d6a2fe13 32 unsigned int state;
bcd66c89 33 spinlock_t lock;
dc52ddc0
MH
34};
35
bcd66c89 36static inline struct freezer *cgroup_freezer(struct cgroup *cgroup)
dc52ddc0 37{
bcd66c89
TH
38 return container_of(cgroup_subsys_state(cgroup, freezer_subsys_id),
39 struct freezer, css);
dc52ddc0
MH
40}
41
42static inline struct freezer *task_freezer(struct task_struct *task)
43{
44 return container_of(task_subsys_state(task, freezer_subsys_id),
45 struct freezer, css);
46}
47
22b4e111 48bool cgroup_freezing(struct task_struct *task)
dc52ddc0 49{
22b4e111 50 bool ret;
dc52ddc0 51
22b4e111 52 rcu_read_lock();
d6a2fe13 53 ret = task_freezer(task)->state & CGROUP_FREEZING;
22b4e111
TH
54 rcu_read_unlock();
55
56 return ret;
dc52ddc0
MH
57}
58
59/*
60 * cgroups_write_string() limits the size of freezer state strings to
61 * CGROUP_LOCAL_BUFFER_SIZE
62 */
d6a2fe13
TH
63static const char *freezer_state_strs(unsigned int state)
64{
65 if (state & CGROUP_FROZEN)
66 return "FROZEN";
67 if (state & CGROUP_FREEZING)
68 return "FREEZING";
69 return "THAWED";
dc52ddc0
MH
70};
71
72/*
73 * State diagram
74 * Transitions are caused by userspace writes to the freezer.state file.
75 * The values in parenthesis are state labels. The rest are edge labels.
76 *
81dcf33c
MH
77 * (THAWED) --FROZEN--> (FREEZING) --FROZEN--> (FROZEN)
78 * ^ ^ | |
79 * | \_______THAWED_______/ |
80 * \__________________________THAWED____________/
dc52ddc0
MH
81 */
82
83struct cgroup_subsys freezer_subsys;
84
761b3ef5 85static struct cgroup_subsys_state *freezer_create(struct cgroup *cgroup)
dc52ddc0
MH
86{
87 struct freezer *freezer;
88
89 freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
90 if (!freezer)
91 return ERR_PTR(-ENOMEM);
92
93 spin_lock_init(&freezer->lock);
dc52ddc0
MH
94 return &freezer->css;
95}
96
761b3ef5 97static void freezer_destroy(struct cgroup *cgroup)
dc52ddc0 98{
a3201227
TH
99 struct freezer *freezer = cgroup_freezer(cgroup);
100
d6a2fe13 101 if (freezer->state & CGROUP_FREEZING)
a3201227
TH
102 atomic_dec(&system_freezing_cnt);
103 kfree(freezer);
dc52ddc0
MH
104}
105
957a4eea 106/*
ead5c473
TH
107 * Tasks can be migrated into a different freezer anytime regardless of its
108 * current state. freezer_attach() is responsible for making new tasks
109 * conform to the current state.
110 *
111 * Freezer state changes and task migration are synchronized via
112 * @freezer->lock. freezer_attach() makes the new tasks conform to the
113 * current state and all following state changes can see the new tasks.
957a4eea 114 */
8755ade6 115static void freezer_attach(struct cgroup *new_cgrp, struct cgroup_taskset *tset)
dc52ddc0 116{
8755ade6 117 struct freezer *freezer = cgroup_freezer(new_cgrp);
bb9d97b6 118 struct task_struct *task;
957a4eea 119
8755ade6
TH
120 spin_lock_irq(&freezer->lock);
121
80a6a2cf 122 /*
8755ade6
TH
123 * Make the new tasks conform to the current state of @new_cgrp.
124 * For simplicity, when migrating any task to a FROZEN cgroup, we
125 * revert it to FREEZING and let update_if_frozen() determine the
126 * correct state later.
127 *
128 * Tasks in @tset are on @new_cgrp but may not conform to its
129 * current state before executing the following - !frozen tasks may
130 * be visible in a FROZEN cgroup and frozen tasks in a THAWED one.
80a6a2cf 131 */
8755ade6 132 cgroup_taskset_for_each(task, new_cgrp, tset) {
d6a2fe13 133 if (!(freezer->state & CGROUP_FREEZING)) {
8755ade6
TH
134 __thaw_task(task);
135 } else {
136 freeze_task(task);
d6a2fe13 137 freezer->state &= ~CGROUP_FROZEN;
8755ade6
TH
138 }
139 }
dc52ddc0 140
8755ade6 141 spin_unlock_irq(&freezer->lock);
f780bdb7
BB
142}
143
761b3ef5 144static void freezer_fork(struct task_struct *task)
dc52ddc0
MH
145{
146 struct freezer *freezer;
147
8b46f880 148 rcu_read_lock();
dc52ddc0 149 freezer = task_freezer(task);
dc52ddc0 150
3b1b3f6e
LZ
151 /*
152 * The root cgroup is non-freezable, so we can skip the
153 * following check.
154 */
155 if (!freezer->css.cgroup->parent)
5edee61e 156 goto out;
3b1b3f6e 157
dc52ddc0 158 spin_lock_irq(&freezer->lock);
d6a2fe13 159 if (freezer->state & CGROUP_FREEZING)
839e3407 160 freeze_task(task);
dc52ddc0 161 spin_unlock_irq(&freezer->lock);
5edee61e
TH
162out:
163 rcu_read_unlock();
dc52ddc0
MH
164}
165
166/*
b4d18311
TH
167 * We change from FREEZING to FROZEN lazily if the cgroup was only
168 * partially frozen when we exitted write. Caller must hold freezer->lock.
169 *
170 * Task states and freezer state might disagree while tasks are being
ead5c473
TH
171 * migrated into or out of @cgroup, so we can't verify task states against
172 * @freezer state here. See freezer_attach() for details.
dc52ddc0 173 */
bcd66c89 174static void update_if_frozen(struct freezer *freezer)
dc52ddc0 175{
bcd66c89 176 struct cgroup *cgroup = freezer->css.cgroup;
dc52ddc0
MH
177 struct cgroup_iter it;
178 struct task_struct *task;
b4d18311 179
d6a2fe13
TH
180 if (!(freezer->state & CGROUP_FREEZING) ||
181 (freezer->state & CGROUP_FROZEN))
b4d18311 182 return;
dc52ddc0
MH
183
184 cgroup_iter_start(cgroup, &it);
b4d18311 185
dc52ddc0 186 while ((task = cgroup_iter_next(cgroup, &it))) {
3c426d5e 187 if (freezing(task)) {
3c426d5e
TH
188 /*
189 * freezer_should_skip() indicates that the task
190 * should be skipped when determining freezing
191 * completion. Consider it frozen in addition to
192 * the usual frozen condition.
193 */
5d8f72b5 194 if (!frozen(task) && !freezer_should_skip(task))
b4d18311 195 goto notyet;
3c426d5e 196 }
dc52ddc0
MH
197 }
198
d6a2fe13 199 freezer->state |= CGROUP_FROZEN;
b4d18311 200notyet:
dc52ddc0
MH
201 cgroup_iter_end(cgroup, &it);
202}
203
204static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
205 struct seq_file *m)
206{
bcd66c89 207 struct freezer *freezer = cgroup_freezer(cgroup);
d6a2fe13 208 unsigned int state;
dc52ddc0 209
dc52ddc0 210 spin_lock_irq(&freezer->lock);
bcd66c89 211 update_if_frozen(freezer);
dc52ddc0 212 state = freezer->state;
dc52ddc0 213 spin_unlock_irq(&freezer->lock);
dc52ddc0 214
d6a2fe13 215 seq_puts(m, freezer_state_strs(state));
dc52ddc0
MH
216 seq_putc(m, '\n');
217 return 0;
218}
219
bcd66c89 220static void freeze_cgroup(struct freezer *freezer)
dc52ddc0 221{
bcd66c89 222 struct cgroup *cgroup = freezer->css.cgroup;
dc52ddc0
MH
223 struct cgroup_iter it;
224 struct task_struct *task;
dc52ddc0 225
dc52ddc0 226 cgroup_iter_start(cgroup, &it);
51f246ed
TH
227 while ((task = cgroup_iter_next(cgroup, &it)))
228 freeze_task(task);
dc52ddc0 229 cgroup_iter_end(cgroup, &it);
dc52ddc0
MH
230}
231
bcd66c89 232static void unfreeze_cgroup(struct freezer *freezer)
dc52ddc0 233{
bcd66c89 234 struct cgroup *cgroup = freezer->css.cgroup;
dc52ddc0
MH
235 struct cgroup_iter it;
236 struct task_struct *task;
237
238 cgroup_iter_start(cgroup, &it);
a5be2d0d
TH
239 while ((task = cgroup_iter_next(cgroup, &it)))
240 __thaw_task(task);
dc52ddc0 241 cgroup_iter_end(cgroup, &it);
dc52ddc0
MH
242}
243
04a4ec32
TH
244/**
245 * freezer_apply_state - apply state change to a single cgroup_freezer
246 * @freezer: freezer to apply state change to
247 * @freeze: whether to freeze or unfreeze
248 */
249static void freezer_apply_state(struct freezer *freezer, bool freeze)
dc52ddc0 250{
ead5c473 251 /* also synchronizes against task migration, see freezer_attach() */
04a4ec32 252 lockdep_assert_held(&freezer->lock);
51308ee5 253
04a4ec32 254 if (freeze) {
d6a2fe13 255 if (!(freezer->state & CGROUP_FREEZING))
a3201227 256 atomic_inc(&system_freezing_cnt);
d6a2fe13 257 freezer->state |= CGROUP_FREEZING;
bcd66c89 258 freeze_cgroup(freezer);
04a4ec32 259 } else {
d6a2fe13 260 if (freezer->state & CGROUP_FREEZING)
04a4ec32 261 atomic_dec(&system_freezing_cnt);
d6a2fe13 262 freezer->state &= ~(CGROUP_FREEZING | CGROUP_FROZEN);
04a4ec32 263 unfreeze_cgroup(freezer);
dc52ddc0 264 }
04a4ec32 265}
22b4e111 266
04a4ec32
TH
267/**
268 * freezer_change_state - change the freezing state of a cgroup_freezer
269 * @freezer: freezer of interest
270 * @freeze: whether to freeze or thaw
271 *
272 * Freeze or thaw @cgroup according to @freeze.
273 */
274static void freezer_change_state(struct freezer *freezer, bool freeze)
275{
276 /* update @freezer */
277 spin_lock_irq(&freezer->lock);
278 freezer_apply_state(freezer, freeze);
dc52ddc0 279 spin_unlock_irq(&freezer->lock);
dc52ddc0
MH
280}
281
bcd66c89 282static int freezer_write(struct cgroup *cgroup, struct cftype *cft,
dc52ddc0
MH
283 const char *buffer)
284{
04a4ec32 285 bool freeze;
dc52ddc0 286
d6a2fe13 287 if (strcmp(buffer, freezer_state_strs(0)) == 0)
04a4ec32 288 freeze = false;
d6a2fe13 289 else if (strcmp(buffer, freezer_state_strs(CGROUP_FROZEN)) == 0)
04a4ec32 290 freeze = true;
dc52ddc0 291 else
3b1b3f6e 292 return -EINVAL;
dc52ddc0 293
04a4ec32 294 freezer_change_state(cgroup_freezer(cgroup), freeze);
51f246ed 295 return 0;
dc52ddc0
MH
296}
297
298static struct cftype files[] = {
299 {
300 .name = "state",
4baf6e33 301 .flags = CFTYPE_NOT_ON_ROOT,
dc52ddc0
MH
302 .read_seq_string = freezer_read,
303 .write_string = freezer_write,
304 },
4baf6e33 305 { } /* terminate */
dc52ddc0
MH
306};
307
dc52ddc0
MH
308struct cgroup_subsys freezer_subsys = {
309 .name = "freezer",
310 .create = freezer_create,
311 .destroy = freezer_destroy,
dc52ddc0 312 .subsys_id = freezer_subsys_id,
8755ade6 313 .attach = freezer_attach,
dc52ddc0 314 .fork = freezer_fork,
4baf6e33 315 .base_cftypes = files,
8c7f6edb
TH
316
317 /*
318 * freezer subsys doesn't handle hierarchy at all. Frozen state
319 * should be inherited through the hierarchy - if a parent is
320 * frozen, all its children should be frozen. Fix it and remove
321 * the following.
322 */
323 .broken_hierarchy = true,
dc52ddc0 324};