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