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
51int cgroup_freezing_or_frozen(struct task_struct *task)
52{
53 struct freezer *freezer;
54 enum freezer_state state;
55
56 task_lock(task);
57 freezer = task_freezer(task);
58 if (!freezer->css.cgroup->parent)
59 state = CGROUP_THAWED;
60 else
61 state = freezer->state;
62 task_unlock(task);
63
64 return (state == CGROUP_FREEZING) || (state == CGROUP_FROZEN);
65}
66
67
68
69
70
71static const char *freezer_state_strs[] = {
72 "THAWED",
73 "FREEZING",
74 "FROZEN",
75};
76
77
78
79
80
81
82
83
84
85
86
87
88struct cgroup_subsys freezer_subsys;
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
136
137static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss,
138 struct cgroup *cgroup)
139{
140 struct freezer *freezer;
141
142 freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
143 if (!freezer)
144 return ERR_PTR(-ENOMEM);
145
146 spin_lock_init(&freezer->lock);
147 freezer->state = CGROUP_THAWED;
148 return &freezer->css;
149}
150
151static void freezer_destroy(struct cgroup_subsys *ss,
152 struct cgroup *cgroup)
153{
154 kfree(cgroup_freezer(cgroup));
155}
156
157
158static bool is_task_frozen_enough(struct task_struct *task)
159{
160 return frozen(task) ||
161 (task_is_stopped_or_traced(task) && freezing(task));
162}
163
164
165
166
167
168
169static int freezer_can_attach(struct cgroup_subsys *ss,
170 struct cgroup *new_cgroup,
171 struct task_struct *task, bool threadgroup)
172{
173 struct freezer *freezer;
174
175
176
177
178
179
180
181
182 if (is_task_frozen_enough(task))
183 return -EBUSY;
184
185 freezer = cgroup_freezer(new_cgroup);
186 if (freezer->state == CGROUP_FROZEN)
187 return -EBUSY;
188
189 if (threadgroup) {
190 struct task_struct *c;
191
192 rcu_read_lock();
193 list_for_each_entry_rcu(c, &task->thread_group, thread_group) {
194 if (is_task_frozen_enough(c)) {
195 rcu_read_unlock();
196 return -EBUSY;
197 }
198 }
199 rcu_read_unlock();
200 }
201
202 return 0;
203}
204
205static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task)
206{
207 struct freezer *freezer;
208
209
210
211
212
213
214
215
216 rcu_read_lock();
217 freezer = task_freezer(task);
218 rcu_read_unlock();
219
220
221
222
223
224 if (!freezer->css.cgroup->parent)
225 return;
226
227 spin_lock_irq(&freezer->lock);
228 BUG_ON(freezer->state == CGROUP_FROZEN);
229
230
231 if (freezer->state == CGROUP_FREEZING)
232 freeze_task(task, true);
233 spin_unlock_irq(&freezer->lock);
234}
235
236
237
238
239static void update_freezer_state(struct cgroup *cgroup,
240 struct freezer *freezer)
241{
242 struct cgroup_iter it;
243 struct task_struct *task;
244 unsigned int nfrozen = 0, ntotal = 0;
245
246 cgroup_iter_start(cgroup, &it);
247 while ((task = cgroup_iter_next(cgroup, &it))) {
248 ntotal++;
249 if (is_task_frozen_enough(task))
250 nfrozen++;
251 }
252
253
254
255
256
257
258 if (nfrozen == ntotal)
259 freezer->state = CGROUP_FROZEN;
260 else if (nfrozen > 0)
261 freezer->state = CGROUP_FREEZING;
262 else
263 freezer->state = CGROUP_THAWED;
264 cgroup_iter_end(cgroup, &it);
265}
266
267static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
268 struct seq_file *m)
269{
270 struct freezer *freezer;
271 enum freezer_state state;
272
273 if (!cgroup_lock_live_group(cgroup))
274 return -ENODEV;
275
276 freezer = cgroup_freezer(cgroup);
277 spin_lock_irq(&freezer->lock);
278 state = freezer->state;
279 if (state == CGROUP_FREEZING) {
280
281
282 update_freezer_state(cgroup, freezer);
283 state = freezer->state;
284 }
285 spin_unlock_irq(&freezer->lock);
286 cgroup_unlock();
287
288 seq_puts(m, freezer_state_strs[state]);
289 seq_putc(m, '\n');
290 return 0;
291}
292
293static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
294{
295 struct cgroup_iter it;
296 struct task_struct *task;
297 unsigned int num_cant_freeze_now = 0;
298
299 freezer->state = CGROUP_FREEZING;
300 cgroup_iter_start(cgroup, &it);
301 while ((task = cgroup_iter_next(cgroup, &it))) {
302 if (!freeze_task(task, true))
303 continue;
304 if (is_task_frozen_enough(task))
305 continue;
306 if (!freezing(task) && !freezer_should_skip(task))
307 num_cant_freeze_now++;
308 }
309 cgroup_iter_end(cgroup, &it);
310
311 return num_cant_freeze_now ? -EBUSY : 0;
312}
313
314static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
315{
316 struct cgroup_iter it;
317 struct task_struct *task;
318
319 cgroup_iter_start(cgroup, &it);
320 while ((task = cgroup_iter_next(cgroup, &it))) {
321 thaw_process(task);
322 }
323 cgroup_iter_end(cgroup, &it);
324
325 freezer->state = CGROUP_THAWED;
326}
327
328static int freezer_change_state(struct cgroup *cgroup,
329 enum freezer_state goal_state)
330{
331 struct freezer *freezer;
332 int retval = 0;
333
334 freezer = cgroup_freezer(cgroup);
335
336 spin_lock_irq(&freezer->lock);
337
338 update_freezer_state(cgroup, freezer);
339 if (goal_state == freezer->state)
340 goto out;
341
342 switch (goal_state) {
343 case CGROUP_THAWED:
344 unfreeze_cgroup(cgroup, freezer);
345 break;
346 case CGROUP_FROZEN:
347 retval = try_to_freeze_cgroup(cgroup, freezer);
348 break;
349 default:
350 BUG();
351 }
352out:
353 spin_unlock_irq(&freezer->lock);
354
355 return retval;
356}
357
358static int freezer_write(struct cgroup *cgroup,
359 struct cftype *cft,
360 const char *buffer)
361{
362 int retval;
363 enum freezer_state goal_state;
364
365 if (strcmp(buffer, freezer_state_strs[CGROUP_THAWED]) == 0)
366 goal_state = CGROUP_THAWED;
367 else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0)
368 goal_state = CGROUP_FROZEN;
369 else
370 return -EINVAL;
371
372 if (!cgroup_lock_live_group(cgroup))
373 return -ENODEV;
374 retval = freezer_change_state(cgroup, goal_state);
375 cgroup_unlock();
376 return retval;
377}
378
379static struct cftype files[] = {
380 {
381 .name = "state",
382 .read_seq_string = freezer_read,
383 .write_string = freezer_write,
384 },
385};
386
387static int freezer_populate(struct cgroup_subsys *ss, struct cgroup *cgroup)
388{
389 if (!cgroup->parent)
390 return 0;
391 return cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files));
392}
393
394struct cgroup_subsys freezer_subsys = {
395 .name = "freezer",
396 .create = freezer_create,
397 .destroy = freezer_destroy,
398 .populate = freezer_populate,
399 .subsys_id = freezer_subsys_id,
400 .can_attach = freezer_can_attach,
401 .attach = NULL,
402 .fork = freezer_fork,
403 .exit = NULL,
404};
405