cgroup_freezer: make freezer->state mask of flags
[cascardo/linux.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_flags {
26         CGROUP_FREEZING         = (1 << 1), /* this freezer is freezing */
27         CGROUP_FROZEN           = (1 << 3), /* this and its descendants frozen */
28 };
29
30 struct freezer {
31         struct cgroup_subsys_state      css;
32         unsigned int                    state;
33         spinlock_t                      lock;
34 };
35
36 static inline struct freezer *cgroup_freezer(struct cgroup *cgroup)
37 {
38         return container_of(cgroup_subsys_state(cgroup, freezer_subsys_id),
39                             struct freezer, css);
40 }
41
42 static 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
48 bool cgroup_freezing(struct task_struct *task)
49 {
50         bool ret;
51
52         rcu_read_lock();
53         ret = task_freezer(task)->state & CGROUP_FREEZING;
54         rcu_read_unlock();
55
56         return ret;
57 }
58
59 /*
60  * cgroups_write_string() limits the size of freezer state strings to
61  * CGROUP_LOCAL_BUFFER_SIZE
62  */
63 static 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";
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         return &freezer->css;
95 }
96
97 static void freezer_destroy(struct cgroup *cgroup)
98 {
99         struct freezer *freezer = cgroup_freezer(cgroup);
100
101         if (freezer->state & CGROUP_FREEZING)
102                 atomic_dec(&system_freezing_cnt);
103         kfree(freezer);
104 }
105
106 /*
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.
114  */
115 static void freezer_attach(struct cgroup *new_cgrp, struct cgroup_taskset *tset)
116 {
117         struct freezer *freezer = cgroup_freezer(new_cgrp);
118         struct task_struct *task;
119
120         spin_lock_irq(&freezer->lock);
121
122         /*
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.
131          */
132         cgroup_taskset_for_each(task, new_cgrp, tset) {
133                 if (!(freezer->state & CGROUP_FREEZING)) {
134                         __thaw_task(task);
135                 } else {
136                         freeze_task(task);
137                         freezer->state &= ~CGROUP_FROZEN;
138                 }
139         }
140
141         spin_unlock_irq(&freezer->lock);
142 }
143
144 static void freezer_fork(struct task_struct *task)
145 {
146         struct freezer *freezer;
147
148         rcu_read_lock();
149         freezer = task_freezer(task);
150
151         /*
152          * The root cgroup is non-freezable, so we can skip the
153          * following check.
154          */
155         if (!freezer->css.cgroup->parent)
156                 goto out;
157
158         spin_lock_irq(&freezer->lock);
159         if (freezer->state & CGROUP_FREEZING)
160                 freeze_task(task);
161         spin_unlock_irq(&freezer->lock);
162 out:
163         rcu_read_unlock();
164 }
165
166 /*
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
171  * migrated into or out of @cgroup, so we can't verify task states against
172  * @freezer state here.  See freezer_attach() for details.
173  */
174 static void update_if_frozen(struct freezer *freezer)
175 {
176         struct cgroup *cgroup = freezer->css.cgroup;
177         struct cgroup_iter it;
178         struct task_struct *task;
179
180         if (!(freezer->state & CGROUP_FREEZING) ||
181             (freezer->state & CGROUP_FROZEN))
182                 return;
183
184         cgroup_iter_start(cgroup, &it);
185
186         while ((task = cgroup_iter_next(cgroup, &it))) {
187                 if (freezing(task)) {
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                          */
194                         if (!frozen(task) && !freezer_should_skip(task))
195                                 goto notyet;
196                 }
197         }
198
199         freezer->state |= CGROUP_FROZEN;
200 notyet:
201         cgroup_iter_end(cgroup, &it);
202 }
203
204 static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
205                         struct seq_file *m)
206 {
207         struct freezer *freezer = cgroup_freezer(cgroup);
208         unsigned int state;
209
210         spin_lock_irq(&freezer->lock);
211         update_if_frozen(freezer);
212         state = freezer->state;
213         spin_unlock_irq(&freezer->lock);
214
215         seq_puts(m, freezer_state_strs(state));
216         seq_putc(m, '\n');
217         return 0;
218 }
219
220 static void freeze_cgroup(struct freezer *freezer)
221 {
222         struct cgroup *cgroup = freezer->css.cgroup;
223         struct cgroup_iter it;
224         struct task_struct *task;
225
226         cgroup_iter_start(cgroup, &it);
227         while ((task = cgroup_iter_next(cgroup, &it)))
228                 freeze_task(task);
229         cgroup_iter_end(cgroup, &it);
230 }
231
232 static void unfreeze_cgroup(struct freezer *freezer)
233 {
234         struct cgroup *cgroup = freezer->css.cgroup;
235         struct cgroup_iter it;
236         struct task_struct *task;
237
238         cgroup_iter_start(cgroup, &it);
239         while ((task = cgroup_iter_next(cgroup, &it)))
240                 __thaw_task(task);
241         cgroup_iter_end(cgroup, &it);
242 }
243
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  */
249 static void freezer_apply_state(struct freezer *freezer, bool freeze)
250 {
251         /* also synchronizes against task migration, see freezer_attach() */
252         lockdep_assert_held(&freezer->lock);
253
254         if (freeze) {
255                 if (!(freezer->state & CGROUP_FREEZING))
256                         atomic_inc(&system_freezing_cnt);
257                 freezer->state |= CGROUP_FREEZING;
258                 freeze_cgroup(freezer);
259         } else {
260                 if (freezer->state & CGROUP_FREEZING)
261                         atomic_dec(&system_freezing_cnt);
262                 freezer->state &= ~(CGROUP_FREEZING | CGROUP_FROZEN);
263                 unfreeze_cgroup(freezer);
264         }
265 }
266
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  */
274 static 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);
279         spin_unlock_irq(&freezer->lock);
280 }
281
282 static int freezer_write(struct cgroup *cgroup, struct cftype *cft,
283                          const char *buffer)
284 {
285         bool freeze;
286
287         if (strcmp(buffer, freezer_state_strs(0)) == 0)
288                 freeze = false;
289         else if (strcmp(buffer, freezer_state_strs(CGROUP_FROZEN)) == 0)
290                 freeze = true;
291         else
292                 return -EINVAL;
293
294         freezer_change_state(cgroup_freezer(cgroup), freeze);
295         return 0;
296 }
297
298 static struct cftype files[] = {
299         {
300                 .name = "state",
301                 .flags = CFTYPE_NOT_ON_ROOT,
302                 .read_seq_string = freezer_read,
303                 .write_string = freezer_write,
304         },
305         { }     /* terminate */
306 };
307
308 struct cgroup_subsys freezer_subsys = {
309         .name           = "freezer",
310         .create         = freezer_create,
311         .destroy        = freezer_destroy,
312         .subsys_id      = freezer_subsys_id,
313         .attach         = freezer_attach,
314         .fork           = freezer_fork,
315         .base_cftypes   = files,
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,
324 };