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