Branch data 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 : 156 : static inline struct freezer *css_freezer(struct cgroup_subsys_state *css)
51 : : {
52 [ # # # # : 0 : return css ? container_of(css, struct freezer, css) : NULL;
# # ]
53 : : }
54 : :
55 : 0 : static inline struct freezer *task_freezer(struct task_struct *task)
56 : : {
57 : 0 : return css_freezer(task_css(task, freezer_cgrp_id));
58 : : }
59 : :
60 : 78 : static struct freezer *parent_freezer(struct freezer *freezer)
61 : : {
62 : 78 : return css_freezer(freezer->css.parent);
63 : : }
64 : :
65 : 0 : bool cgroup_freezing(struct task_struct *task)
66 : : {
67 : 0 : bool ret;
68 : :
69 : 0 : rcu_read_lock();
70 [ # # ]: 0 : ret = task_freezer(task)->state & CGROUP_FREEZING;
71 : 0 : rcu_read_unlock();
72 : :
73 : 0 : return ret;
74 : : }
75 : :
76 : 0 : 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 : 0 : return "FREEZING";
82 : : return "THAWED";
83 : : };
84 : :
85 : : static struct cgroup_subsys_state *
86 : 78 : freezer_css_alloc(struct cgroup_subsys_state *parent_css)
87 : : {
88 : 78 : struct freezer *freezer;
89 : :
90 : 78 : freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
91 [ + - ]: 78 : if (!freezer)
92 : : return ERR_PTR(-ENOMEM);
93 : :
94 : 78 : 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 : 78 : static int freezer_css_online(struct cgroup_subsys_state *css)
106 : : {
107 [ - + ]: 78 : struct freezer *freezer = css_freezer(css);
108 [ + - ]: 78 : struct freezer *parent = parent_freezer(freezer);
109 : :
110 : 78 : mutex_lock(&freezer_mutex);
111 : :
112 : 78 : freezer->state |= CGROUP_FREEZER_ONLINE;
113 : :
114 [ - + - - ]: 78 : if (parent && (parent->state & CGROUP_FREEZING)) {
115 : 0 : freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN;
116 : 0 : atomic_inc(&system_freezing_cnt);
117 : : }
118 : :
119 : 78 : mutex_unlock(&freezer_mutex);
120 : 78 : 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 [ # # ]: 0 : struct freezer *freezer = css_freezer(css);
133 : :
134 : 0 : mutex_lock(&freezer_mutex);
135 : :
136 [ # # ]: 0 : if (freezer->state & CGROUP_FREEZING)
137 : 0 : 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_taskset *tset)
159 : : {
160 : 0 : struct task_struct *task;
161 : 0 : struct cgroup_subsys_state *new_css;
162 : :
163 : 0 : mutex_lock(&freezer_mutex);
164 : :
165 : : /*
166 : : * Make the new tasks conform to the current state of @new_css.
167 : : * For simplicity, when migrating any task to a FROZEN cgroup, we
168 : : * revert it to FREEZING and let update_if_frozen() determine the
169 : : * correct state later.
170 : : *
171 : : * Tasks in @tset are on @new_css but may not conform to its
172 : : * current state before executing the following - !frozen tasks may
173 : : * be visible in a FROZEN cgroup and frozen tasks in a THAWED one.
174 : : */
175 [ # # ]: 0 : cgroup_taskset_for_each(task, new_css, tset) {
176 [ # # ]: 0 : struct freezer *freezer = css_freezer(new_css);
177 : :
178 [ # # ]: 0 : if (!(freezer->state & CGROUP_FREEZING)) {
179 : 0 : __thaw_task(task);
180 : : } else {
181 : 0 : freeze_task(task);
182 : : /* clear FROZEN and propagate upwards */
183 [ # # # # ]: 0 : while (freezer && (freezer->state & CGROUP_FROZEN)) {
184 : 0 : freezer->state &= ~CGROUP_FROZEN;
185 [ # # ]: 0 : freezer = parent_freezer(freezer);
186 : : }
187 : : }
188 : : }
189 : :
190 : 0 : mutex_unlock(&freezer_mutex);
191 : 0 : }
192 : :
193 : : /**
194 : : * freezer_fork - cgroup post fork callback
195 : : * @task: a task which has just been forked
196 : : *
197 : : * @task has just been created and should conform to the current state of
198 : : * the cgroup_freezer it belongs to. This function may race against
199 : : * freezer_attach(). Losing to freezer_attach() means that we don't have
200 : : * to do anything as freezer_attach() will put @task into the appropriate
201 : : * state.
202 : : */
203 : 59502 : static void freezer_fork(struct task_struct *task)
204 : : {
205 : 59502 : struct freezer *freezer;
206 : :
207 : : /*
208 : : * The root cgroup is non-freezable, so we can skip locking the
209 : : * freezer. This is safe regardless of race with task migration.
210 : : * If we didn't race or won, skipping is obviously the right thing
211 : : * to do. If we lost and root is the new cgroup, noop is still the
212 : : * right thing to do.
213 : : */
214 [ - + ]: 59502 : if (task_css_is_root(task, freezer_cgrp_id))
215 : : return;
216 : :
217 : 0 : mutex_lock(&freezer_mutex);
218 : 0 : rcu_read_lock();
219 : :
220 [ # # ]: 0 : freezer = task_freezer(task);
221 [ # # ]: 0 : if (freezer->state & CGROUP_FREEZING)
222 : 0 : freeze_task(task);
223 : :
224 : 0 : rcu_read_unlock();
225 : 0 : mutex_unlock(&freezer_mutex);
226 : : }
227 : :
228 : : /**
229 : : * update_if_frozen - update whether a cgroup finished freezing
230 : : * @css: css of interest
231 : : *
232 : : * Once FREEZING is initiated, transition to FROZEN is lazily updated by
233 : : * calling this function. If the current state is FREEZING but not FROZEN,
234 : : * this function checks whether all tasks of this cgroup and the descendant
235 : : * cgroups finished freezing and, if so, sets FROZEN.
236 : : *
237 : : * The caller is responsible for grabbing RCU read lock and calling
238 : : * update_if_frozen() on all descendants prior to invoking this function.
239 : : *
240 : : * Task states and freezer state might disagree while tasks are being
241 : : * migrated into or out of @css, so we can't verify task states against
242 : : * @freezer state here. See freezer_attach() for details.
243 : : */
244 : 0 : static void update_if_frozen(struct cgroup_subsys_state *css)
245 : : {
246 [ # # ]: 0 : struct freezer *freezer = css_freezer(css);
247 : 0 : struct cgroup_subsys_state *pos;
248 : 0 : struct css_task_iter it;
249 : 0 : struct task_struct *task;
250 : :
251 : 0 : lockdep_assert_held(&freezer_mutex);
252 : :
253 [ # # ]: 0 : if (!(freezer->state & CGROUP_FREEZING) ||
254 [ # # ]: 0 : (freezer->state & CGROUP_FROZEN))
255 : 0 : return;
256 : :
257 : : /* are all (live) children frozen? */
258 : 0 : rcu_read_lock();
259 [ # # ]: 0 : css_for_each_child(pos, css) {
260 : 0 : struct freezer *child = css_freezer(pos);
261 : :
262 [ # # ]: 0 : if ((child->state & CGROUP_FREEZER_ONLINE) &&
263 : : !(child->state & CGROUP_FROZEN)) {
264 : 0 : rcu_read_unlock();
265 : 0 : return;
266 : : }
267 : : }
268 : 0 : rcu_read_unlock();
269 : :
270 : : /* are all tasks frozen? */
271 : 0 : css_task_iter_start(css, 0, &it);
272 : :
273 [ # # ]: 0 : while ((task = css_task_iter_next(&it))) {
274 [ # # ]: 0 : if (freezing(task)) {
275 : : /*
276 : : * freezer_should_skip() indicates that the task
277 : : * should be skipped when determining freezing
278 : : * completion. Consider it frozen in addition to
279 : : * the usual frozen condition.
280 : : */
281 [ # # # # ]: 0 : if (!frozen(task) && !freezer_should_skip(task))
282 : 0 : goto out_iter_end;
283 : : }
284 : : }
285 : :
286 : 0 : freezer->state |= CGROUP_FROZEN;
287 : 0 : out_iter_end:
288 : 0 : css_task_iter_end(&it);
289 : : }
290 : :
291 : 0 : static int freezer_read(struct seq_file *m, void *v)
292 : : {
293 : 0 : struct cgroup_subsys_state *css = seq_css(m), *pos;
294 : :
295 : 0 : mutex_lock(&freezer_mutex);
296 : 0 : rcu_read_lock();
297 : :
298 : : /* update states bottom-up */
299 [ # # ]: 0 : css_for_each_descendant_post(pos, css) {
300 [ # # # # ]: 0 : if (!css_tryget_online(pos))
301 : 0 : continue;
302 : 0 : rcu_read_unlock();
303 : :
304 : 0 : update_if_frozen(pos);
305 : :
306 : 0 : rcu_read_lock();
307 [ # # ]: 0 : css_put(pos);
308 : : }
309 : :
310 : 0 : rcu_read_unlock();
311 : 0 : mutex_unlock(&freezer_mutex);
312 : :
313 [ # # # # ]: 0 : seq_puts(m, freezer_state_strs(css_freezer(css)->state));
314 : 0 : seq_putc(m, '\n');
315 : 0 : return 0;
316 : : }
317 : :
318 : 0 : static void freeze_cgroup(struct freezer *freezer)
319 : : {
320 : 0 : struct css_task_iter it;
321 : 0 : struct task_struct *task;
322 : :
323 : 0 : css_task_iter_start(&freezer->css, 0, &it);
324 [ # # ]: 0 : while ((task = css_task_iter_next(&it)))
325 : 0 : freeze_task(task);
326 : 0 : css_task_iter_end(&it);
327 : 0 : }
328 : :
329 : 0 : static void unfreeze_cgroup(struct freezer *freezer)
330 : : {
331 : 0 : struct css_task_iter it;
332 : 0 : struct task_struct *task;
333 : :
334 : 0 : css_task_iter_start(&freezer->css, 0, &it);
335 [ # # ]: 0 : while ((task = css_task_iter_next(&it)))
336 : 0 : __thaw_task(task);
337 : 0 : css_task_iter_end(&it);
338 : 0 : }
339 : :
340 : : /**
341 : : * freezer_apply_state - apply state change to a single cgroup_freezer
342 : : * @freezer: freezer to apply state change to
343 : : * @freeze: whether to freeze or unfreeze
344 : : * @state: CGROUP_FREEZING_* flag to set or clear
345 : : *
346 : : * Set or clear @state on @cgroup according to @freeze, and perform
347 : : * freezing or thawing as necessary.
348 : : */
349 : 0 : static void freezer_apply_state(struct freezer *freezer, bool freeze,
350 : : unsigned int state)
351 : : {
352 : : /* also synchronizes against task migration, see freezer_attach() */
353 : 0 : lockdep_assert_held(&freezer_mutex);
354 : :
355 [ # # ]: 0 : if (!(freezer->state & CGROUP_FREEZER_ONLINE))
356 : : return;
357 : :
358 [ # # ]: 0 : if (freeze) {
359 [ # # ]: 0 : if (!(freezer->state & CGROUP_FREEZING))
360 : 0 : atomic_inc(&system_freezing_cnt);
361 : 0 : freezer->state |= state;
362 : 0 : freeze_cgroup(freezer);
363 : : } else {
364 : 0 : bool was_freezing = freezer->state & CGROUP_FREEZING;
365 : :
366 : 0 : freezer->state &= ~state;
367 : :
368 [ # # ]: 0 : if (!(freezer->state & CGROUP_FREEZING)) {
369 [ # # ]: 0 : if (was_freezing)
370 : 0 : atomic_dec(&system_freezing_cnt);
371 : 0 : freezer->state &= ~CGROUP_FROZEN;
372 : 0 : unfreeze_cgroup(freezer);
373 : : }
374 : : }
375 : : }
376 : :
377 : : /**
378 : : * freezer_change_state - change the freezing state of a cgroup_freezer
379 : : * @freezer: freezer of interest
380 : : * @freeze: whether to freeze or thaw
381 : : *
382 : : * Freeze or thaw @freezer according to @freeze. The operations are
383 : : * recursive - all descendants of @freezer will be affected.
384 : : */
385 : 0 : static void freezer_change_state(struct freezer *freezer, bool freeze)
386 : : {
387 : 0 : struct cgroup_subsys_state *pos;
388 : :
389 : : /*
390 : : * Update all its descendants in pre-order traversal. Each
391 : : * descendant will try to inherit its parent's FREEZING state as
392 : : * CGROUP_FREEZING_PARENT.
393 : : */
394 : 0 : mutex_lock(&freezer_mutex);
395 : 0 : rcu_read_lock();
396 [ # # ]: 0 : css_for_each_descendant_pre(pos, &freezer->css) {
397 : 0 : struct freezer *pos_f = css_freezer(pos);
398 [ # # ]: 0 : struct freezer *parent = parent_freezer(pos_f);
399 : :
400 [ # # # # ]: 0 : if (!css_tryget_online(pos))
401 : 0 : continue;
402 : 0 : rcu_read_unlock();
403 : :
404 [ # # ]: 0 : if (pos_f == freezer)
405 : 0 : freezer_apply_state(pos_f, freeze,
406 : : CGROUP_FREEZING_SELF);
407 : : else
408 : 0 : freezer_apply_state(pos_f,
409 : 0 : parent->state & CGROUP_FREEZING,
410 : : CGROUP_FREEZING_PARENT);
411 : :
412 : 0 : rcu_read_lock();
413 [ # # ]: 0 : css_put(pos);
414 : : }
415 : 0 : rcu_read_unlock();
416 : 0 : mutex_unlock(&freezer_mutex);
417 : 0 : }
418 : :
419 : 0 : static ssize_t freezer_write(struct kernfs_open_file *of,
420 : : char *buf, size_t nbytes, loff_t off)
421 : : {
422 : 0 : bool freeze;
423 : :
424 : 0 : buf = strstrip(buf);
425 : :
426 [ # # ]: 0 : if (strcmp(buf, freezer_state_strs(0)) == 0)
427 : : freeze = false;
428 [ # # ]: 0 : else if (strcmp(buf, freezer_state_strs(CGROUP_FROZEN)) == 0)
429 : : freeze = true;
430 : : else
431 : : return -EINVAL;
432 : :
433 : 0 : freezer_change_state(css_freezer(of_css(of)), freeze);
434 : 0 : return nbytes;
435 : : }
436 : :
437 : 0 : static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css,
438 : : struct cftype *cft)
439 : : {
440 [ # # ]: 0 : struct freezer *freezer = css_freezer(css);
441 : :
442 : 0 : return (bool)(freezer->state & CGROUP_FREEZING_SELF);
443 : : }
444 : :
445 : 0 : static u64 freezer_parent_freezing_read(struct cgroup_subsys_state *css,
446 : : struct cftype *cft)
447 : : {
448 [ # # ]: 0 : struct freezer *freezer = css_freezer(css);
449 : :
450 : 0 : return (bool)(freezer->state & CGROUP_FREEZING_PARENT);
451 : : }
452 : :
453 : : static struct cftype files[] = {
454 : : {
455 : : .name = "state",
456 : : .flags = CFTYPE_NOT_ON_ROOT,
457 : : .seq_show = freezer_read,
458 : : .write = freezer_write,
459 : : },
460 : : {
461 : : .name = "self_freezing",
462 : : .flags = CFTYPE_NOT_ON_ROOT,
463 : : .read_u64 = freezer_self_freezing_read,
464 : : },
465 : : {
466 : : .name = "parent_freezing",
467 : : .flags = CFTYPE_NOT_ON_ROOT,
468 : : .read_u64 = freezer_parent_freezing_read,
469 : : },
470 : : { } /* terminate */
471 : : };
472 : :
473 : : struct cgroup_subsys freezer_cgrp_subsys = {
474 : : .css_alloc = freezer_css_alloc,
475 : : .css_online = freezer_css_online,
476 : : .css_offline = freezer_css_offline,
477 : : .css_free = freezer_css_free,
478 : : .attach = freezer_attach,
479 : : .fork = freezer_fork,
480 : : .legacy_cftypes = files,
481 : : };
|