1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3 * cgroup_freezer.c - control group freezer subsystem
4 *
5 * Copyright IBM Corporation, 2007
6 *
7 * Author : Cedric Le Goater <clg@fr.ibm.com>
8 */
9
10 #include <linux/export.h>
11 #include <linux/slab.h>
12 #include <linux/cgroup.h>
13 #include <linux/fs.h>
14 #include <linux/uaccess.h>
15 #include <linux/freezer.h>
16 #include <linux/seq_file.h>
17 #include <linux/mutex.h>
18 #include <linux/cpu.h>
19
20 /*
21 * A cgroup is freezing if any FREEZING flags are set. FREEZING_SELF is
22 * set if "FROZEN" is written to freezer.state cgroupfs file, and cleared
23 * for "THAWED". FREEZING_PARENT is set if the parent freezer is FREEZING
24 * for whatever reason. IOW, a cgroup has FREEZING_PARENT set if one of
25 * its ancestors has FREEZING_SELF set.
26 */
27 enum freezer_state_flags {
28 CGROUP_FREEZER_ONLINE = (1 << 0), /* freezer is fully online */
29 CGROUP_FREEZING_SELF = (1 << 1), /* this freezer is freezing */
30 CGROUP_FREEZING_PARENT = (1 << 2), /* the parent freezer is freezing */
31 CGROUP_FROZEN = (1 << 3), /* this and its descendants frozen */
32
33 /* mask for all FREEZING flags */
34 CGROUP_FREEZING = CGROUP_FREEZING_SELF | CGROUP_FREEZING_PARENT,
35 };
36
37 struct freezer {
38 struct cgroup_subsys_state css;
39 unsigned int state;
40 };
41
42 static DEFINE_MUTEX(freezer_mutex);
43
css_freezer(struct cgroup_subsys_state * css)44 static inline struct freezer *css_freezer(struct cgroup_subsys_state *css)
45 {
46 return css ? container_of(css, struct freezer, css) : NULL;
47 }
48
task_freezer(struct task_struct * task)49 static inline struct freezer *task_freezer(struct task_struct *task)
50 {
51 return css_freezer(task_css(task, freezer_cgrp_id));
52 }
53
parent_freezer(struct freezer * freezer)54 static struct freezer *parent_freezer(struct freezer *freezer)
55 {
56 return css_freezer(freezer->css.parent);
57 }
58
cgroup1_freezing(struct task_struct * task)59 bool cgroup1_freezing(struct task_struct *task)
60 {
61 bool ret;
62
63 rcu_read_lock();
64 ret = task_freezer(task)->state & CGROUP_FREEZING;
65 rcu_read_unlock();
66
67 return ret;
68 }
69
freezer_state_strs(unsigned int state)70 static const char *freezer_state_strs(unsigned int state)
71 {
72 if (state & CGROUP_FROZEN)
73 return "FROZEN";
74 if (state & CGROUP_FREEZING)
75 return "FREEZING";
76 return "THAWED";
77 };
78
79 static struct cgroup_subsys_state *
freezer_css_alloc(struct cgroup_subsys_state * parent_css)80 freezer_css_alloc(struct cgroup_subsys_state *parent_css)
81 {
82 struct freezer *freezer;
83
84 freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
85 if (!freezer)
86 return ERR_PTR(-ENOMEM);
87
88 return &freezer->css;
89 }
90
91 /**
92 * freezer_css_online - commit creation of a freezer css
93 * @css: css being created
94 *
95 * We're committing to creation of @css. Mark it online and inherit
96 * parent's freezing state while holding cpus read lock and freezer_mutex.
97 */
freezer_css_online(struct cgroup_subsys_state * css)98 static int freezer_css_online(struct cgroup_subsys_state *css)
99 {
100 struct freezer *freezer = css_freezer(css);
101 struct freezer *parent = parent_freezer(freezer);
102
103 cpus_read_lock();
104 mutex_lock(&freezer_mutex);
105
106 freezer->state |= CGROUP_FREEZER_ONLINE;
107
108 if (parent && (parent->state & CGROUP_FREEZING)) {
109 freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN;
110 static_branch_inc_cpuslocked(&freezer_active);
111 }
112
113 mutex_unlock(&freezer_mutex);
114 cpus_read_unlock();
115 return 0;
116 }
117
118 /**
119 * freezer_css_offline - initiate destruction of a freezer css
120 * @css: css being destroyed
121 *
122 * @css is going away. Mark it dead and decrement freezer_active if
123 * it was holding one.
124 */
freezer_css_offline(struct cgroup_subsys_state * css)125 static void freezer_css_offline(struct cgroup_subsys_state *css)
126 {
127 struct freezer *freezer = css_freezer(css);
128
129 cpus_read_lock();
130 mutex_lock(&freezer_mutex);
131
132 if (freezer->state & CGROUP_FREEZING)
133 static_branch_dec_cpuslocked(&freezer_active);
134
135 freezer->state = 0;
136
137 mutex_unlock(&freezer_mutex);
138 cpus_read_unlock();
139 }
140
freezer_css_free(struct cgroup_subsys_state * css)141 static void freezer_css_free(struct cgroup_subsys_state *css)
142 {
143 kfree(css_freezer(css));
144 }
145
146 /*
147 * Tasks can be migrated into a different freezer anytime regardless of its
148 * current state. freezer_attach() is responsible for making new tasks
149 * conform to the current state.
150 *
151 * Freezer state changes and task migration are synchronized via
152 * @freezer->lock. freezer_attach() makes the new tasks conform to the
153 * current state and all following state changes can see the new tasks.
154 */
freezer_attach(struct cgroup_taskset * tset)155 static void freezer_attach(struct cgroup_taskset *tset)
156 {
157 struct task_struct *task;
158 struct cgroup_subsys_state *new_css;
159
160 mutex_lock(&freezer_mutex);
161
162 /*
163 * Make the new tasks conform to the current state of @new_css.
164 * For simplicity, when migrating any task to a FROZEN cgroup, we
165 * revert it to FREEZING and let update_if_frozen() determine the
166 * correct state later.
167 *
168 * Tasks in @tset are on @new_css but may not conform to its
169 * current state before executing the following - !frozen tasks may
170 * be visible in a FROZEN cgroup and frozen tasks in a THAWED one.
171 */
172 cgroup_taskset_for_each(task, new_css, tset) {
173 struct freezer *freezer = css_freezer(new_css);
174
175 if (!(freezer->state & CGROUP_FREEZING)) {
176 __thaw_task(task);
177 } else {
178 /* clear FROZEN and propagate upwards */
179 while (freezer && (freezer->state & CGROUP_FROZEN)) {
180 freezer->state &= ~CGROUP_FROZEN;
181 freezer = parent_freezer(freezer);
182 }
183 freeze_task(task);
184 }
185 }
186
187 mutex_unlock(&freezer_mutex);
188 }
189
190 /**
191 * freezer_fork - cgroup post fork callback
192 * @task: a task which has just been forked
193 *
194 * @task has just been created and should conform to the current state of
195 * the cgroup_freezer it belongs to. This function may race against
196 * freezer_attach(). Losing to freezer_attach() means that we don't have
197 * to do anything as freezer_attach() will put @task into the appropriate
198 * state.
199 */
freezer_fork(struct task_struct * task)200 static void freezer_fork(struct task_struct *task)
201 {
202 struct freezer *freezer;
203
204 /*
205 * The root cgroup is non-freezable, so we can skip locking the
206 * freezer. This is safe regardless of race with task migration.
207 * If we didn't race or won, skipping is obviously the right thing
208 * to do. If we lost and root is the new cgroup, noop is still the
209 * right thing to do.
210 */
211 if (task_css_is_root(task, freezer_cgrp_id))
212 return;
213
214 mutex_lock(&freezer_mutex);
215 rcu_read_lock();
216
217 freezer = task_freezer(task);
218 if (freezer->state & CGROUP_FREEZING)
219 freeze_task(task);
220
221 rcu_read_unlock();
222 mutex_unlock(&freezer_mutex);
223 }
224
225 /**
226 * update_if_frozen - update whether a cgroup finished freezing
227 * @css: css of interest
228 *
229 * Once FREEZING is initiated, transition to FROZEN is lazily updated by
230 * calling this function. If the current state is FREEZING but not FROZEN,
231 * this function checks whether all tasks of this cgroup and the descendant
232 * cgroups finished freezing and, if so, sets FROZEN.
233 *
234 * The caller is responsible for grabbing RCU read lock and calling
235 * update_if_frozen() on all descendants prior to invoking this function.
236 *
237 * Task states and freezer state might disagree while tasks are being
238 * migrated into or out of @css, so we can't verify task states against
239 * @freezer state here. See freezer_attach() for details.
240 */
update_if_frozen(struct cgroup_subsys_state * css)241 static void update_if_frozen(struct cgroup_subsys_state *css)
242 {
243 struct freezer *freezer = css_freezer(css);
244 struct cgroup_subsys_state *pos;
245 struct css_task_iter it;
246 struct task_struct *task;
247
248 lockdep_assert_held(&freezer_mutex);
249
250 if (!(freezer->state & CGROUP_FREEZING) ||
251 (freezer->state & CGROUP_FROZEN))
252 return;
253
254 /* are all (live) children frozen? */
255 rcu_read_lock();
256 css_for_each_child(pos, css) {
257 struct freezer *child = css_freezer(pos);
258
259 if ((child->state & CGROUP_FREEZER_ONLINE) &&
260 !(child->state & CGROUP_FROZEN)) {
261 rcu_read_unlock();
262 return;
263 }
264 }
265 rcu_read_unlock();
266
267 /* are all tasks frozen? */
268 css_task_iter_start(css, 0, &it);
269
270 while ((task = css_task_iter_next(&it))) {
271 if (freezing(task) && !frozen(task))
272 goto out_iter_end;
273 }
274
275 freezer->state |= CGROUP_FROZEN;
276 out_iter_end:
277 css_task_iter_end(&it);
278 }
279
freezer_read(struct seq_file * m,void * v)280 static int freezer_read(struct seq_file *m, void *v)
281 {
282 struct cgroup_subsys_state *css = seq_css(m), *pos;
283
284 mutex_lock(&freezer_mutex);
285 rcu_read_lock();
286
287 /* update states bottom-up */
288 css_for_each_descendant_post(pos, css) {
289 if (!css_tryget_online(pos))
290 continue;
291 rcu_read_unlock();
292
293 update_if_frozen(pos);
294
295 rcu_read_lock();
296 css_put(pos);
297 }
298
299 rcu_read_unlock();
300 mutex_unlock(&freezer_mutex);
301
302 seq_puts(m, freezer_state_strs(css_freezer(css)->state));
303 seq_putc(m, '\n');
304 return 0;
305 }
306
freeze_cgroup(struct freezer * freezer)307 static void freeze_cgroup(struct freezer *freezer)
308 {
309 struct css_task_iter it;
310 struct task_struct *task;
311
312 css_task_iter_start(&freezer->css, 0, &it);
313 while ((task = css_task_iter_next(&it)))
314 freeze_task(task);
315 css_task_iter_end(&it);
316 }
317
unfreeze_cgroup(struct freezer * freezer)318 static void unfreeze_cgroup(struct freezer *freezer)
319 {
320 struct css_task_iter it;
321 struct task_struct *task;
322
323 css_task_iter_start(&freezer->css, 0, &it);
324 while ((task = css_task_iter_next(&it)))
325 __thaw_task(task);
326 css_task_iter_end(&it);
327 }
328
329 /**
330 * freezer_apply_state - apply state change to a single cgroup_freezer
331 * @freezer: freezer to apply state change to
332 * @freeze: whether to freeze or unfreeze
333 * @state: CGROUP_FREEZING_* flag to set or clear
334 *
335 * Set or clear @state on @cgroup according to @freeze, and perform
336 * freezing or thawing as necessary.
337 */
freezer_apply_state(struct freezer * freezer,bool freeze,unsigned int state)338 static void freezer_apply_state(struct freezer *freezer, bool freeze,
339 unsigned int state)
340 {
341 /* also synchronizes against task migration, see freezer_attach() */
342 lockdep_assert_held(&freezer_mutex);
343
344 if (!(freezer->state & CGROUP_FREEZER_ONLINE))
345 return;
346
347 if (freeze) {
348 if (!(freezer->state & CGROUP_FREEZING))
349 static_branch_inc_cpuslocked(&freezer_active);
350 freezer->state |= state;
351 freeze_cgroup(freezer);
352 } else {
353 bool was_freezing = freezer->state & CGROUP_FREEZING;
354
355 freezer->state &= ~state;
356
357 if (!(freezer->state & CGROUP_FREEZING)) {
358 freezer->state &= ~CGROUP_FROZEN;
359 if (was_freezing)
360 static_branch_dec_cpuslocked(&freezer_active);
361 unfreeze_cgroup(freezer);
362 }
363 }
364 }
365
366 /**
367 * freezer_change_state - change the freezing state of a cgroup_freezer
368 * @freezer: freezer of interest
369 * @freeze: whether to freeze or thaw
370 *
371 * Freeze or thaw @freezer according to @freeze. The operations are
372 * recursive - all descendants of @freezer will be affected.
373 */
freezer_change_state(struct freezer * freezer,bool freeze)374 static void freezer_change_state(struct freezer *freezer, bool freeze)
375 {
376 struct cgroup_subsys_state *pos;
377
378 cpus_read_lock();
379 /*
380 * Update all its descendants in pre-order traversal. Each
381 * descendant will try to inherit its parent's FREEZING state as
382 * CGROUP_FREEZING_PARENT.
383 */
384 mutex_lock(&freezer_mutex);
385 rcu_read_lock();
386 css_for_each_descendant_pre(pos, &freezer->css) {
387 struct freezer *pos_f = css_freezer(pos);
388 struct freezer *parent = parent_freezer(pos_f);
389
390 if (!css_tryget_online(pos))
391 continue;
392 rcu_read_unlock();
393
394 if (pos_f == freezer)
395 freezer_apply_state(pos_f, freeze,
396 CGROUP_FREEZING_SELF);
397 else
398 freezer_apply_state(pos_f,
399 parent->state & CGROUP_FREEZING,
400 CGROUP_FREEZING_PARENT);
401
402 rcu_read_lock();
403 css_put(pos);
404 }
405 rcu_read_unlock();
406 mutex_unlock(&freezer_mutex);
407 cpus_read_unlock();
408 }
409
freezer_write(struct kernfs_open_file * of,char * buf,size_t nbytes,loff_t off)410 static ssize_t freezer_write(struct kernfs_open_file *of,
411 char *buf, size_t nbytes, loff_t off)
412 {
413 bool freeze;
414
415 buf = strstrip(buf);
416
417 if (strcmp(buf, freezer_state_strs(0)) == 0)
418 freeze = false;
419 else if (strcmp(buf, freezer_state_strs(CGROUP_FROZEN)) == 0) {
420 pr_info_once("Freezing with imperfect legacy cgroup freezer. "
421 "See cgroup.freeze of cgroup v2\n");
422 freeze = true;
423 } else
424 return -EINVAL;
425
426 freezer_change_state(css_freezer(of_css(of)), freeze);
427 return nbytes;
428 }
429
freezer_self_freezing_read(struct cgroup_subsys_state * css,struct cftype * cft)430 static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css,
431 struct cftype *cft)
432 {
433 struct freezer *freezer = css_freezer(css);
434
435 return (bool)(freezer->state & CGROUP_FREEZING_SELF);
436 }
437
freezer_parent_freezing_read(struct cgroup_subsys_state * css,struct cftype * cft)438 static u64 freezer_parent_freezing_read(struct cgroup_subsys_state *css,
439 struct cftype *cft)
440 {
441 struct freezer *freezer = css_freezer(css);
442
443 return (bool)(freezer->state & CGROUP_FREEZING_PARENT);
444 }
445
446 static struct cftype files[] = {
447 {
448 .name = "state",
449 .flags = CFTYPE_NOT_ON_ROOT,
450 .seq_show = freezer_read,
451 .write = freezer_write,
452 },
453 {
454 .name = "self_freezing",
455 .flags = CFTYPE_NOT_ON_ROOT,
456 .read_u64 = freezer_self_freezing_read,
457 },
458 {
459 .name = "parent_freezing",
460 .flags = CFTYPE_NOT_ON_ROOT,
461 .read_u64 = freezer_parent_freezing_read,
462 },
463 { } /* terminate */
464 };
465
466 struct cgroup_subsys freezer_cgrp_subsys = {
467 .css_alloc = freezer_css_alloc,
468 .css_online = freezer_css_online,
469 .css_offline = freezer_css_offline,
470 .css_free = freezer_css_free,
471 .attach = freezer_attach,
472 .fork = freezer_fork,
473 .legacy_cftypes = files,
474 };
475