cgroup_freezer: trivial cleanups
[linux-3.10.git] / kernel / cgroup_freezer.c
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
25 enum freezer_state {
26         CGROUP_THAWED = 0,
27         CGROUP_FREEZING,
28         CGROUP_FROZEN,
29 };
30
31 struct freezer {
32         struct cgroup_subsys_state      css;
33         enum freezer_state              state;
34         spinlock_t                      lock;
35 };
36
37 static inline struct freezer *cgroup_freezer(struct cgroup *cgroup)
38 {
39         return container_of(cgroup_subsys_state(cgroup, freezer_subsys_id),
40                             struct freezer, css);
41 }
42
43 static inline struct freezer *task_freezer(struct task_struct *task)
44 {
45         return container_of(task_subsys_state(task, freezer_subsys_id),
46                             struct freezer, css);
47 }
48
49 bool cgroup_freezing(struct task_struct *task)
50 {
51         enum freezer_state state;
52         bool ret;
53
54         rcu_read_lock();
55         state = task_freezer(task)->state;
56         ret = state == CGROUP_FREEZING || state == CGROUP_FROZEN;
57         rcu_read_unlock();
58
59         return ret;
60 }
61
62 /*
63  * cgroups_write_string() limits the size of freezer state strings to
64  * CGROUP_LOCAL_BUFFER_SIZE
65  */
66 static const char *freezer_state_strs[] = {
67         "THAWED",
68         "FREEZING",
69         "FROZEN",
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  *
77  * (THAWED) --FROZEN--> (FREEZING) --FROZEN--> (FROZEN)
78  *    ^ ^                    |                     |
79  *    | \_______THAWED_______/                     |
80  *    \__________________________THAWED____________/
81  */
82
83 struct cgroup_subsys freezer_subsys;
84
85 static struct cgroup_subsys_state *freezer_create(struct cgroup *cgroup)
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);
94         freezer->state = CGROUP_THAWED;
95         return &freezer->css;
96 }
97
98 static void freezer_destroy(struct cgroup *cgroup)
99 {
100         struct freezer *freezer = cgroup_freezer(cgroup);
101
102         if (freezer->state != CGROUP_THAWED)
103                 atomic_dec(&system_freezing_cnt);
104         kfree(freezer);
105 }
106
107 /*
108  * Tasks can be migrated into a different freezer anytime regardless of its
109  * current state.  freezer_attach() is responsible for making new tasks
110  * conform to the current state.
111  *
112  * Freezer state changes and task migration are synchronized via
113  * @freezer->lock.  freezer_attach() makes the new tasks conform to the
114  * current state and all following state changes can see the new tasks.
115  */
116 static void freezer_attach(struct cgroup *new_cgrp, struct cgroup_taskset *tset)
117 {
118         struct freezer *freezer = cgroup_freezer(new_cgrp);
119         struct task_struct *task;
120
121         spin_lock_irq(&freezer->lock);
122
123         /*
124          * Make the new tasks conform to the current state of @new_cgrp.
125          * For simplicity, when migrating any task to a FROZEN cgroup, we
126          * revert it to FREEZING and let update_if_frozen() determine the
127          * correct state later.
128          *
129          * Tasks in @tset are on @new_cgrp but may not conform to its
130          * current state before executing the following - !frozen tasks may
131          * be visible in a FROZEN cgroup and frozen tasks in a THAWED one.
132          * This means that, to determine whether to freeze, one should test
133          * whether the state equals THAWED.
134          */
135         cgroup_taskset_for_each(task, new_cgrp, tset) {
136                 if (freezer->state == CGROUP_THAWED) {
137                         __thaw_task(task);
138                 } else {
139                         freeze_task(task);
140                         freezer->state = CGROUP_FREEZING;
141                 }
142         }
143
144         spin_unlock_irq(&freezer->lock);
145 }
146
147 static void freezer_fork(struct task_struct *task)
148 {
149         struct freezer *freezer;
150
151         rcu_read_lock();
152         freezer = task_freezer(task);
153
154         /*
155          * The root cgroup is non-freezable, so we can skip the
156          * following check.
157          */
158         if (!freezer->css.cgroup->parent)
159                 goto out;
160
161         spin_lock_irq(&freezer->lock);
162         /*
163          * @task might have been just migrated into a FROZEN cgroup.  Test
164          * equality with THAWED.  Read the comment in freezer_attach().
165          */
166         if (freezer->state != CGROUP_THAWED)
167                 freeze_task(task);
168         spin_unlock_irq(&freezer->lock);
169 out:
170         rcu_read_unlock();
171 }
172
173 /*
174  * We change from FREEZING to FROZEN lazily if the cgroup was only
175  * partially frozen when we exitted write.  Caller must hold freezer->lock.
176  *
177  * Task states and freezer state might disagree while tasks are being
178  * migrated into or out of @cgroup, so we can't verify task states against
179  * @freezer state here.  See freezer_attach() for details.
180  */
181 static void update_if_frozen(struct freezer *freezer)
182 {
183         struct cgroup *cgroup = freezer->css.cgroup;
184         struct cgroup_iter it;
185         struct task_struct *task;
186
187         if (freezer->state != CGROUP_FREEZING)
188                 return;
189
190         cgroup_iter_start(cgroup, &it);
191
192         while ((task = cgroup_iter_next(cgroup, &it))) {
193                 if (freezing(task)) {
194                         /*
195                          * freezer_should_skip() indicates that the task
196                          * should be skipped when determining freezing
197                          * completion.  Consider it frozen in addition to
198                          * the usual frozen condition.
199                          */
200                         if (!frozen(task) && !freezer_should_skip(task))
201                                 goto notyet;
202                 }
203         }
204
205         freezer->state = CGROUP_FROZEN;
206 notyet:
207         cgroup_iter_end(cgroup, &it);
208 }
209
210 static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
211                         struct seq_file *m)
212 {
213         struct freezer *freezer = cgroup_freezer(cgroup);
214         enum freezer_state state;
215
216         spin_lock_irq(&freezer->lock);
217         update_if_frozen(freezer);
218         state = freezer->state;
219         spin_unlock_irq(&freezer->lock);
220
221         seq_puts(m, freezer_state_strs[state]);
222         seq_putc(m, '\n');
223         return 0;
224 }
225
226 static void freeze_cgroup(struct freezer *freezer)
227 {
228         struct cgroup *cgroup = freezer->css.cgroup;
229         struct cgroup_iter it;
230         struct task_struct *task;
231
232         cgroup_iter_start(cgroup, &it);
233         while ((task = cgroup_iter_next(cgroup, &it)))
234                 freeze_task(task);
235         cgroup_iter_end(cgroup, &it);
236 }
237
238 static void unfreeze_cgroup(struct freezer *freezer)
239 {
240         struct cgroup *cgroup = freezer->css.cgroup;
241         struct cgroup_iter it;
242         struct task_struct *task;
243
244         cgroup_iter_start(cgroup, &it);
245         while ((task = cgroup_iter_next(cgroup, &it)))
246                 __thaw_task(task);
247         cgroup_iter_end(cgroup, &it);
248 }
249
250 static void freezer_change_state(struct freezer *freezer,
251                                  enum freezer_state goal_state)
252 {
253         /* also synchronizes against task migration, see freezer_attach() */
254         spin_lock_irq(&freezer->lock);
255
256         switch (goal_state) {
257         case CGROUP_THAWED:
258                 if (freezer->state != CGROUP_THAWED)
259                         atomic_dec(&system_freezing_cnt);
260                 freezer->state = CGROUP_THAWED;
261                 unfreeze_cgroup(freezer);
262                 break;
263         case CGROUP_FROZEN:
264                 if (freezer->state == CGROUP_THAWED)
265                         atomic_inc(&system_freezing_cnt);
266                 freezer->state = CGROUP_FREEZING;
267                 freeze_cgroup(freezer);
268                 break;
269         default:
270                 BUG();
271         }
272
273         spin_unlock_irq(&freezer->lock);
274 }
275
276 static int freezer_write(struct cgroup *cgroup, struct cftype *cft,
277                          const char *buffer)
278 {
279         enum freezer_state goal_state;
280
281         if (strcmp(buffer, freezer_state_strs[CGROUP_THAWED]) == 0)
282                 goal_state = CGROUP_THAWED;
283         else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0)
284                 goal_state = CGROUP_FROZEN;
285         else
286                 return -EINVAL;
287
288         freezer_change_state(cgroup_freezer(cgroup), goal_state);
289         return 0;
290 }
291
292 static struct cftype files[] = {
293         {
294                 .name = "state",
295                 .flags = CFTYPE_NOT_ON_ROOT,
296                 .read_seq_string = freezer_read,
297                 .write_string = freezer_write,
298         },
299         { }     /* terminate */
300 };
301
302 struct cgroup_subsys freezer_subsys = {
303         .name           = "freezer",
304         .create         = freezer_create,
305         .destroy        = freezer_destroy,
306         .subsys_id      = freezer_subsys_id,
307         .attach         = freezer_attach,
308         .fork           = freezer_fork,
309         .base_cftypes   = files,
310
311         /*
312          * freezer subsys doesn't handle hierarchy at all.  Frozen state
313          * should be inherited through the hierarchy - if a parent is
314          * frozen, all its children should be frozen.  Fix it and remove
315          * the following.
316          */
317         .broken_hierarchy = true,
318 };