1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
157static bool is_task_frozen_enough(struct task_struct *task)
158{
159 return frozen(task) ||
160 (task_is_stopped_or_traced(task) && freezing(task));
161}
162
163
164
165
166
167
168static int freezer_can_attach(struct cgroup_subsys *ss,
169 struct cgroup *new_cgroup,
170 struct task_struct *task)
171{
172 struct freezer *freezer;
173
174
175
176
177
178 freezer = cgroup_freezer(new_cgroup);
179 if (freezer->state != CGROUP_THAWED)
180 return -EBUSY;
181
182 return 0;
183}
184
185static int freezer_can_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
186{
187 rcu_read_lock();
188 if (__cgroup_freezing_or_frozen(tsk)) {
189 rcu_read_unlock();
190 return -EBUSY;
191 }
192 rcu_read_unlock();
193 return 0;
194}
195
196static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task)
197{
198 struct freezer *freezer;
199
200
201
202
203
204
205
206
207 rcu_read_lock();
208 freezer = task_freezer(task);
209 rcu_read_unlock();
210
211
212
213
214
215 if (!freezer->css.cgroup->parent)
216 return;
217
218 spin_lock_irq(&freezer->lock);
219 BUG_ON(freezer->state == CGROUP_FROZEN);
220
221
222 if (freezer->state == CGROUP_FREEZING)
223 freeze_task(task, true);
224 spin_unlock_irq(&freezer->lock);
225}
226
227
228
229
230static void update_if_frozen(struct cgroup *cgroup,
231 struct freezer *freezer)
232{
233 struct cgroup_iter it;
234 struct task_struct *task;
235 unsigned int nfrozen = 0, ntotal = 0;
236 enum freezer_state old_state = freezer->state;
237
238 cgroup_iter_start(cgroup, &it);
239 while ((task = cgroup_iter_next(cgroup, &it))) {
240 ntotal++;
241 if (is_task_frozen_enough(task))
242 nfrozen++;
243 }
244
245 if (old_state == CGROUP_THAWED) {
246 BUG_ON(nfrozen > 0);
247 } else if (old_state == CGROUP_FREEZING) {
248 if (nfrozen == ntotal)
249 freezer->state = CGROUP_FROZEN;
250 } else {
251 BUG_ON(nfrozen != ntotal);
252 }
253
254 cgroup_iter_end(cgroup, &it);
255}
256
257static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
258 struct seq_file *m)
259{
260 struct freezer *freezer;
261 enum freezer_state state;
262
263 if (!cgroup_lock_live_group(cgroup))
264 return -ENODEV;
265
266 freezer = cgroup_freezer(cgroup);
267 spin_lock_irq(&freezer->lock);
268 state = freezer->state;
269 if (state == CGROUP_FREEZING) {
270
271
272 update_if_frozen(cgroup, freezer);
273 state = freezer->state;
274 }
275 spin_unlock_irq(&freezer->lock);
276 cgroup_unlock();
277
278 seq_puts(m, freezer_state_strs[state]);
279 seq_putc(m, '\n');
280 return 0;
281}
282
283static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
284{
285 struct cgroup_iter it;
286 struct task_struct *task;
287 unsigned int num_cant_freeze_now = 0;
288
289 freezer->state = CGROUP_FREEZING;
290 cgroup_iter_start(cgroup, &it);
291 while ((task = cgroup_iter_next(cgroup, &it))) {
292 if (!freeze_task(task, true))
293 continue;
294 if (is_task_frozen_enough(task))
295 continue;
296 if (!freezing(task) && !freezer_should_skip(task))
297 num_cant_freeze_now++;
298 }
299 cgroup_iter_end(cgroup, &it);
300
301 return num_cant_freeze_now ? -EBUSY : 0;
302}
303
304static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
305{
306 struct cgroup_iter it;
307 struct task_struct *task;
308
309 cgroup_iter_start(cgroup, &it);
310 while ((task = cgroup_iter_next(cgroup, &it))) {
311 thaw_process(task);
312 }
313 cgroup_iter_end(cgroup, &it);
314
315 freezer->state = CGROUP_THAWED;
316}
317
318static int freezer_change_state(struct cgroup *cgroup,
319 enum freezer_state goal_state)
320{
321 struct freezer *freezer;
322 int retval = 0;
323
324 freezer = cgroup_freezer(cgroup);
325
326 spin_lock_irq(&freezer->lock);
327
328 update_if_frozen(cgroup, freezer);
329 if (goal_state == freezer->state)
330 goto out;
331
332 switch (goal_state) {
333 case CGROUP_THAWED:
334 unfreeze_cgroup(cgroup, freezer);
335 break;
336 case CGROUP_FROZEN:
337 retval = try_to_freeze_cgroup(cgroup, freezer);
338 break;
339 default:
340 BUG();
341 }
342out:
343 spin_unlock_irq(&freezer->lock);
344
345 return retval;
346}
347
348static int freezer_write(struct cgroup *cgroup,
349 struct cftype *cft,
350 const char *buffer)
351{
352 int retval;
353 enum freezer_state goal_state;
354
355 if (strcmp(buffer, freezer_state_strs[CGROUP_THAWED]) == 0)
356 goal_state = CGROUP_THAWED;
357 else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0)
358 goal_state = CGROUP_FROZEN;
359 else
360 return -EINVAL;
361
362 if (!cgroup_lock_live_group(cgroup))
363 return -ENODEV;
364 retval = freezer_change_state(cgroup, goal_state);
365 cgroup_unlock();
366 return retval;
367}
368
369static struct cftype files[] = {
370 {
371 .name = "state",
372 .read_seq_string = freezer_read,
373 .write_string = freezer_write,
374 },
375};
376
377static int freezer_populate(struct cgroup_subsys *ss, struct cgroup *cgroup)
378{
379 if (!cgroup->parent)
380 return 0;
381 return cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files));
382}
383
384struct cgroup_subsys freezer_subsys = {
385 .name = "freezer",
386 .create = freezer_create,
387 .destroy = freezer_destroy,
388 .populate = freezer_populate,
389 .subsys_id = freezer_subsys_id,
390 .can_attach = freezer_can_attach,
391 .can_attach_task = freezer_can_attach_task,
392 .pre_attach = NULL,
393 .attach_task = NULL,
394 .attach = NULL,
395 .fork = freezer_fork,
396 .exit = NULL,
397};
398