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