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