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