1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <linux/module.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
25enum freezer_state {
26 CGROUP_THAWED = 0,
27 CGROUP_FREEZING,
28 CGROUP_FROZEN,
29};
30
31struct freezer {
32 struct cgroup_subsys_state css;
33 enum freezer_state state;
34 spinlock_t lock;
35};
36
37static inline struct freezer *cgroup_freezer(
38 struct cgroup *cgroup)
39{
40 return container_of(
41 cgroup_subsys_state(cgroup, freezer_subsys_id),
42 struct freezer, css);
43}
44
45static inline struct freezer *task_freezer(struct task_struct *task)
46{
47 return container_of(task_subsys_state(task, freezer_subsys_id),
48 struct freezer, css);
49}
50
51static inline int __cgroup_freezing_or_frozen(struct task_struct *task)
52{
53 enum freezer_state state = task_freezer(task)->state;
54 return (state == CGROUP_FREEZING) || (state == CGROUP_FROZEN);
55}
56
57int cgroup_freezing_or_frozen(struct task_struct *task)
58{
59 int result;
60 task_lock(task);
61 result = __cgroup_freezing_or_frozen(task);
62 task_unlock(task);
63 return result;
64}
65
66
67
68
69
70static const char *freezer_state_strs[] = {
71 "THAWED",
72 "FREEZING",
73 "FROZEN",
74};
75
76
77
78
79
80
81
82
83
84
85
86
87struct cgroup_subsys freezer_subsys;
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss,
137 struct cgroup *cgroup)
138{
139 struct freezer *freezer;
140
141 freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
142 if (!freezer)
143 return ERR_PTR(-ENOMEM);
144
145 spin_lock_init(&freezer->lock);
146 freezer->state = CGROUP_THAWED;
147 return &freezer->css;
148}
149
150static void freezer_destroy(struct cgroup_subsys *ss,
151 struct cgroup *cgroup)
152{
153 kfree(cgroup_freezer(cgroup));
154}
155
156
157
158
159
160
161static int freezer_can_attach(struct cgroup_subsys *ss,
162 struct cgroup *new_cgroup,
163 struct task_struct *task, bool threadgroup)
164{
165 struct freezer *freezer;
166
167
168
169
170
171 freezer = cgroup_freezer(new_cgroup);
172 if (freezer->state != CGROUP_THAWED)
173 return -EBUSY;
174
175 rcu_read_lock();
176 if (__cgroup_freezing_or_frozen(task)) {
177 rcu_read_unlock();
178 return -EBUSY;
179 }
180 rcu_read_unlock();
181
182 if (threadgroup) {
183 struct task_struct *c;
184
185 rcu_read_lock();
186 list_for_each_entry_rcu(c, &task->thread_group, thread_group) {
187 if (__cgroup_freezing_or_frozen(c)) {
188 rcu_read_unlock();
189 return -EBUSY;
190 }
191 }
192 rcu_read_unlock();
193 }
194
195 return 0;
196}
197
198static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task)
199{
200 struct freezer *freezer;
201
202
203
204
205
206
207
208
209 rcu_read_lock();
210 freezer = task_freezer(task);
211 rcu_read_unlock();
212
213
214
215
216
217 if (!freezer->css.cgroup->parent)
218 return;
219
220 spin_lock_irq(&freezer->lock);
221 BUG_ON(freezer->state == CGROUP_FROZEN);
222
223
224 if (freezer->state == CGROUP_FREEZING)
225 freeze_task(task, true);
226 spin_unlock_irq(&freezer->lock);
227}
228
229
230
231
232static void update_if_frozen(struct cgroup *cgroup,
233 struct freezer *freezer)
234{
235 struct cgroup_iter it;
236 struct task_struct *task;
237 unsigned int nfrozen = 0, ntotal = 0;
238 enum freezer_state old_state = freezer->state;
239
240 cgroup_iter_start(cgroup, &it);
241 while ((task = cgroup_iter_next(cgroup, &it))) {
242 ntotal++;
243 if (frozen(task))
244 nfrozen++;
245 }
246
247 if (old_state == CGROUP_THAWED) {
248 BUG_ON(nfrozen > 0);
249 } else if (old_state == CGROUP_FREEZING) {
250 if (nfrozen == ntotal)
251 freezer->state = CGROUP_FROZEN;
252 } else {
253 BUG_ON(nfrozen != ntotal);
254 }
255
256 cgroup_iter_end(cgroup, &it);
257}
258
259static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
260 struct seq_file *m)
261{
262 struct freezer *freezer;
263 enum freezer_state state;
264
265 if (!cgroup_lock_live_group(cgroup))
266 return -ENODEV;
267
268 freezer = cgroup_freezer(cgroup);
269 spin_lock_irq(&freezer->lock);
270 state = freezer->state;
271 if (state == CGROUP_FREEZING) {
272
273
274 update_if_frozen(cgroup, freezer);
275 state = freezer->state;
276 }
277 spin_unlock_irq(&freezer->lock);
278 cgroup_unlock();
279
280 seq_puts(m, freezer_state_strs[state]);
281 seq_putc(m, '\n');
282 return 0;
283}
284
285static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
286{
287 struct cgroup_iter it;
288 struct task_struct *task;
289 unsigned int num_cant_freeze_now = 0;
290
291 freezer->state = CGROUP_FREEZING;
292 cgroup_iter_start(cgroup, &it);
293 while ((task = cgroup_iter_next(cgroup, &it))) {
294 if (!freeze_task(task, true))
295 continue;
296 if (frozen(task))
297 continue;
298 if (!freezing(task) && !freezer_should_skip(task))
299 num_cant_freeze_now++;
300 }
301 cgroup_iter_end(cgroup, &it);
302
303 return num_cant_freeze_now ? -EBUSY : 0;
304}
305
306static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
307{
308 struct cgroup_iter it;
309 struct task_struct *task;
310
311 cgroup_iter_start(cgroup, &it);
312 while ((task = cgroup_iter_next(cgroup, &it))) {
313 thaw_process(task);
314 }
315 cgroup_iter_end(cgroup, &it);
316
317 freezer->state = CGROUP_THAWED;
318}
319
320static int freezer_change_state(struct cgroup *cgroup,
321 enum freezer_state goal_state)
322{
323 struct freezer *freezer;
324 int retval = 0;
325
326 freezer = cgroup_freezer(cgroup);
327
328 spin_lock_irq(&freezer->lock);
329
330 update_if_frozen(cgroup, freezer);
331 if (goal_state == freezer->state)
332 goto out;
333
334 switch (goal_state) {
335 case CGROUP_THAWED:
336 unfreeze_cgroup(cgroup, freezer);
337 break;
338 case CGROUP_FROZEN:
339 retval = try_to_freeze_cgroup(cgroup, freezer);
340 break;
341 default:
342 BUG();
343 }
344out:
345 spin_unlock_irq(&freezer->lock);
346
347 return retval;
348}
349
350static int freezer_write(struct cgroup *cgroup,
351 struct cftype *cft,
352 const char *buffer)
353{
354 int retval;
355 enum freezer_state goal_state;
356
357 if (strcmp(buffer, freezer_state_strs[CGROUP_THAWED]) == 0)
358 goal_state = CGROUP_THAWED;
359 else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0)
360 goal_state = CGROUP_FROZEN;
361 else
362 return -EINVAL;
363
364 if (!cgroup_lock_live_group(cgroup))
365 return -ENODEV;
366 retval = freezer_change_state(cgroup, goal_state);
367 cgroup_unlock();
368 return retval;
369}
370
371static struct cftype files[] = {
372 {
373 .name = "state",
374 .read_seq_string = freezer_read,
375 .write_string = freezer_write,
376 },
377};
378
379static int freezer_populate(struct cgroup_subsys *ss, struct cgroup *cgroup)
380{
381 if (!cgroup->parent)
382 return 0;
383 return cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files));
384}
385
386struct cgroup_subsys freezer_subsys = {
387 .name = "freezer",
388 .create = freezer_create,
389 .destroy = freezer_destroy,
390 .populate = freezer_populate,
391 .subsys_id = freezer_subsys_id,
392 .can_attach = freezer_can_attach,
393 .attach = NULL,
394 .fork = freezer_fork,
395 .exit = NULL,
396};
397