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