Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/kernel/cgroup/legacy_freezer.c
29278 views
1
/*
2
* cgroup_freezer.c - control group freezer subsystem
3
*
4
* Copyright IBM Corporation, 2007
5
*
6
* Author : Cedric Le Goater <[email protected]>
7
*
8
* This program is free software; you can redistribute it and/or modify it
9
* under the terms of version 2.1 of the GNU Lesser General Public License
10
* as published by the Free Software Foundation.
11
*
12
* This program is distributed in the hope that it would be useful, but
13
* WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
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
#include <linux/mutex.h>
25
#include <linux/cpu.h>
26
27
/*
28
* A cgroup is freezing if any FREEZING flags are set. FREEZING_SELF is
29
* set if "FROZEN" is written to freezer.state cgroupfs file, and cleared
30
* for "THAWED". FREEZING_PARENT is set if the parent freezer is FREEZING
31
* for whatever reason. IOW, a cgroup has FREEZING_PARENT set if one of
32
* its ancestors has FREEZING_SELF set.
33
*/
34
enum freezer_state_flags {
35
CGROUP_FREEZER_ONLINE = (1 << 0), /* freezer is fully online */
36
CGROUP_FREEZING_SELF = (1 << 1), /* this freezer is freezing */
37
CGROUP_FREEZING_PARENT = (1 << 2), /* the parent freezer is freezing */
38
CGROUP_FROZEN = (1 << 3), /* this and its descendants frozen */
39
40
/* mask for all FREEZING flags */
41
CGROUP_FREEZING = CGROUP_FREEZING_SELF | CGROUP_FREEZING_PARENT,
42
};
43
44
struct freezer {
45
struct cgroup_subsys_state css;
46
unsigned int state;
47
};
48
49
static DEFINE_MUTEX(freezer_mutex);
50
51
static inline struct freezer *css_freezer(struct cgroup_subsys_state *css)
52
{
53
return css ? container_of(css, struct freezer, css) : NULL;
54
}
55
56
static inline struct freezer *task_freezer(struct task_struct *task)
57
{
58
return css_freezer(task_css(task, freezer_cgrp_id));
59
}
60
61
static struct freezer *parent_freezer(struct freezer *freezer)
62
{
63
return css_freezer(freezer->css.parent);
64
}
65
66
bool cgroup_freezing(struct task_struct *task)
67
{
68
bool ret;
69
70
rcu_read_lock();
71
ret = task_freezer(task)->state & CGROUP_FREEZING;
72
rcu_read_unlock();
73
74
return ret;
75
}
76
77
static const char *freezer_state_strs(unsigned int state)
78
{
79
if (state & CGROUP_FROZEN)
80
return "FROZEN";
81
if (state & CGROUP_FREEZING)
82
return "FREEZING";
83
return "THAWED";
84
};
85
86
static struct cgroup_subsys_state *
87
freezer_css_alloc(struct cgroup_subsys_state *parent_css)
88
{
89
struct freezer *freezer;
90
91
freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
92
if (!freezer)
93
return ERR_PTR(-ENOMEM);
94
95
return &freezer->css;
96
}
97
98
/**
99
* freezer_css_online - commit creation of a freezer css
100
* @css: css being created
101
*
102
* We're committing to creation of @css. Mark it online and inherit
103
* parent's freezing state while holding cpus read lock and freezer_mutex.
104
*/
105
static int freezer_css_online(struct cgroup_subsys_state *css)
106
{
107
struct freezer *freezer = css_freezer(css);
108
struct freezer *parent = parent_freezer(freezer);
109
110
cpus_read_lock();
111
mutex_lock(&freezer_mutex);
112
113
freezer->state |= CGROUP_FREEZER_ONLINE;
114
115
if (parent && (parent->state & CGROUP_FREEZING)) {
116
freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN;
117
static_branch_inc_cpuslocked(&freezer_active);
118
}
119
120
mutex_unlock(&freezer_mutex);
121
cpus_read_unlock();
122
return 0;
123
}
124
125
/**
126
* freezer_css_offline - initiate destruction of a freezer css
127
* @css: css being destroyed
128
*
129
* @css is going away. Mark it dead and decrement freezer_active if
130
* it was holding one.
131
*/
132
static void freezer_css_offline(struct cgroup_subsys_state *css)
133
{
134
struct freezer *freezer = css_freezer(css);
135
136
cpus_read_lock();
137
mutex_lock(&freezer_mutex);
138
139
if (freezer->state & CGROUP_FREEZING)
140
static_branch_dec_cpuslocked(&freezer_active);
141
142
freezer->state = 0;
143
144
mutex_unlock(&freezer_mutex);
145
cpus_read_unlock();
146
}
147
148
static void freezer_css_free(struct cgroup_subsys_state *css)
149
{
150
kfree(css_freezer(css));
151
}
152
153
/*
154
* Tasks can be migrated into a different freezer anytime regardless of its
155
* current state. freezer_attach() is responsible for making new tasks
156
* conform to the current state.
157
*
158
* Freezer state changes and task migration are synchronized via
159
* @freezer->lock. freezer_attach() makes the new tasks conform to the
160
* current state and all following state changes can see the new tasks.
161
*/
162
static void freezer_attach(struct cgroup_taskset *tset)
163
{
164
struct task_struct *task;
165
struct cgroup_subsys_state *new_css;
166
167
mutex_lock(&freezer_mutex);
168
169
/*
170
* Make the new tasks conform to the current state of @new_css.
171
* For simplicity, when migrating any task to a FROZEN cgroup, we
172
* revert it to FREEZING and let update_if_frozen() determine the
173
* correct state later.
174
*
175
* Tasks in @tset are on @new_css but may not conform to its
176
* current state before executing the following - !frozen tasks may
177
* be visible in a FROZEN cgroup and frozen tasks in a THAWED one.
178
*/
179
cgroup_taskset_for_each(task, new_css, tset) {
180
struct freezer *freezer = css_freezer(new_css);
181
182
if (!(freezer->state & CGROUP_FREEZING)) {
183
__thaw_task(task);
184
} else {
185
/* clear FROZEN and propagate upwards */
186
while (freezer && (freezer->state & CGROUP_FROZEN)) {
187
freezer->state &= ~CGROUP_FROZEN;
188
freezer = parent_freezer(freezer);
189
}
190
freeze_task(task);
191
}
192
}
193
194
mutex_unlock(&freezer_mutex);
195
}
196
197
/**
198
* freezer_fork - cgroup post fork callback
199
* @task: a task which has just been forked
200
*
201
* @task has just been created and should conform to the current state of
202
* the cgroup_freezer it belongs to. This function may race against
203
* freezer_attach(). Losing to freezer_attach() means that we don't have
204
* to do anything as freezer_attach() will put @task into the appropriate
205
* state.
206
*/
207
static void freezer_fork(struct task_struct *task)
208
{
209
struct freezer *freezer;
210
211
/*
212
* The root cgroup is non-freezable, so we can skip locking the
213
* freezer. This is safe regardless of race with task migration.
214
* If we didn't race or won, skipping is obviously the right thing
215
* to do. If we lost and root is the new cgroup, noop is still the
216
* right thing to do.
217
*/
218
if (task_css_is_root(task, freezer_cgrp_id))
219
return;
220
221
mutex_lock(&freezer_mutex);
222
rcu_read_lock();
223
224
freezer = task_freezer(task);
225
if (freezer->state & CGROUP_FREEZING)
226
freeze_task(task);
227
228
rcu_read_unlock();
229
mutex_unlock(&freezer_mutex);
230
}
231
232
/**
233
* update_if_frozen - update whether a cgroup finished freezing
234
* @css: css of interest
235
*
236
* Once FREEZING is initiated, transition to FROZEN is lazily updated by
237
* calling this function. If the current state is FREEZING but not FROZEN,
238
* this function checks whether all tasks of this cgroup and the descendant
239
* cgroups finished freezing and, if so, sets FROZEN.
240
*
241
* The caller is responsible for grabbing RCU read lock and calling
242
* update_if_frozen() on all descendants prior to invoking this function.
243
*
244
* Task states and freezer state might disagree while tasks are being
245
* migrated into or out of @css, so we can't verify task states against
246
* @freezer state here. See freezer_attach() for details.
247
*/
248
static void update_if_frozen(struct cgroup_subsys_state *css)
249
{
250
struct freezer *freezer = css_freezer(css);
251
struct cgroup_subsys_state *pos;
252
struct css_task_iter it;
253
struct task_struct *task;
254
255
lockdep_assert_held(&freezer_mutex);
256
257
if (!(freezer->state & CGROUP_FREEZING) ||
258
(freezer->state & CGROUP_FROZEN))
259
return;
260
261
/* are all (live) children frozen? */
262
rcu_read_lock();
263
css_for_each_child(pos, css) {
264
struct freezer *child = css_freezer(pos);
265
266
if ((child->state & CGROUP_FREEZER_ONLINE) &&
267
!(child->state & CGROUP_FROZEN)) {
268
rcu_read_unlock();
269
return;
270
}
271
}
272
rcu_read_unlock();
273
274
/* are all tasks frozen? */
275
css_task_iter_start(css, 0, &it);
276
277
while ((task = css_task_iter_next(&it))) {
278
if (freezing(task) && !frozen(task))
279
goto out_iter_end;
280
}
281
282
freezer->state |= CGROUP_FROZEN;
283
out_iter_end:
284
css_task_iter_end(&it);
285
}
286
287
static int freezer_read(struct seq_file *m, void *v)
288
{
289
struct cgroup_subsys_state *css = seq_css(m), *pos;
290
291
mutex_lock(&freezer_mutex);
292
rcu_read_lock();
293
294
/* update states bottom-up */
295
css_for_each_descendant_post(pos, css) {
296
if (!css_tryget_online(pos))
297
continue;
298
rcu_read_unlock();
299
300
update_if_frozen(pos);
301
302
rcu_read_lock();
303
css_put(pos);
304
}
305
306
rcu_read_unlock();
307
mutex_unlock(&freezer_mutex);
308
309
seq_puts(m, freezer_state_strs(css_freezer(css)->state));
310
seq_putc(m, '\n');
311
return 0;
312
}
313
314
static void freeze_cgroup(struct freezer *freezer)
315
{
316
struct css_task_iter it;
317
struct task_struct *task;
318
319
css_task_iter_start(&freezer->css, 0, &it);
320
while ((task = css_task_iter_next(&it)))
321
freeze_task(task);
322
css_task_iter_end(&it);
323
}
324
325
static void unfreeze_cgroup(struct freezer *freezer)
326
{
327
struct css_task_iter it;
328
struct task_struct *task;
329
330
css_task_iter_start(&freezer->css, 0, &it);
331
while ((task = css_task_iter_next(&it)))
332
__thaw_task(task);
333
css_task_iter_end(&it);
334
}
335
336
/**
337
* freezer_apply_state - apply state change to a single cgroup_freezer
338
* @freezer: freezer to apply state change to
339
* @freeze: whether to freeze or unfreeze
340
* @state: CGROUP_FREEZING_* flag to set or clear
341
*
342
* Set or clear @state on @cgroup according to @freeze, and perform
343
* freezing or thawing as necessary.
344
*/
345
static void freezer_apply_state(struct freezer *freezer, bool freeze,
346
unsigned int state)
347
{
348
/* also synchronizes against task migration, see freezer_attach() */
349
lockdep_assert_held(&freezer_mutex);
350
351
if (!(freezer->state & CGROUP_FREEZER_ONLINE))
352
return;
353
354
if (freeze) {
355
if (!(freezer->state & CGROUP_FREEZING))
356
static_branch_inc_cpuslocked(&freezer_active);
357
freezer->state |= state;
358
freeze_cgroup(freezer);
359
} else {
360
bool was_freezing = freezer->state & CGROUP_FREEZING;
361
362
freezer->state &= ~state;
363
364
if (!(freezer->state & CGROUP_FREEZING)) {
365
freezer->state &= ~CGROUP_FROZEN;
366
if (was_freezing)
367
static_branch_dec_cpuslocked(&freezer_active);
368
unfreeze_cgroup(freezer);
369
}
370
}
371
}
372
373
/**
374
* freezer_change_state - change the freezing state of a cgroup_freezer
375
* @freezer: freezer of interest
376
* @freeze: whether to freeze or thaw
377
*
378
* Freeze or thaw @freezer according to @freeze. The operations are
379
* recursive - all descendants of @freezer will be affected.
380
*/
381
static void freezer_change_state(struct freezer *freezer, bool freeze)
382
{
383
struct cgroup_subsys_state *pos;
384
385
cpus_read_lock();
386
/*
387
* Update all its descendants in pre-order traversal. Each
388
* descendant will try to inherit its parent's FREEZING state as
389
* CGROUP_FREEZING_PARENT.
390
*/
391
mutex_lock(&freezer_mutex);
392
rcu_read_lock();
393
css_for_each_descendant_pre(pos, &freezer->css) {
394
struct freezer *pos_f = css_freezer(pos);
395
struct freezer *parent = parent_freezer(pos_f);
396
397
if (!css_tryget_online(pos))
398
continue;
399
rcu_read_unlock();
400
401
if (pos_f == freezer)
402
freezer_apply_state(pos_f, freeze,
403
CGROUP_FREEZING_SELF);
404
else
405
freezer_apply_state(pos_f,
406
parent->state & CGROUP_FREEZING,
407
CGROUP_FREEZING_PARENT);
408
409
rcu_read_lock();
410
css_put(pos);
411
}
412
rcu_read_unlock();
413
mutex_unlock(&freezer_mutex);
414
cpus_read_unlock();
415
}
416
417
static ssize_t freezer_write(struct kernfs_open_file *of,
418
char *buf, size_t nbytes, loff_t off)
419
{
420
bool freeze;
421
422
buf = strstrip(buf);
423
424
if (strcmp(buf, freezer_state_strs(0)) == 0)
425
freeze = false;
426
else if (strcmp(buf, freezer_state_strs(CGROUP_FROZEN)) == 0) {
427
pr_info_once("Freezing with imperfect legacy cgroup freezer. "
428
"See cgroup.freeze of cgroup v2\n");
429
freeze = true;
430
} else
431
return -EINVAL;
432
433
freezer_change_state(css_freezer(of_css(of)), freeze);
434
return nbytes;
435
}
436
437
static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css,
438
struct cftype *cft)
439
{
440
struct freezer *freezer = css_freezer(css);
441
442
return (bool)(freezer->state & CGROUP_FREEZING_SELF);
443
}
444
445
static u64 freezer_parent_freezing_read(struct cgroup_subsys_state *css,
446
struct cftype *cft)
447
{
448
struct freezer *freezer = css_freezer(css);
449
450
return (bool)(freezer->state & CGROUP_FREEZING_PARENT);
451
}
452
453
static struct cftype files[] = {
454
{
455
.name = "state",
456
.flags = CFTYPE_NOT_ON_ROOT,
457
.seq_show = freezer_read,
458
.write = freezer_write,
459
},
460
{
461
.name = "self_freezing",
462
.flags = CFTYPE_NOT_ON_ROOT,
463
.read_u64 = freezer_self_freezing_read,
464
},
465
{
466
.name = "parent_freezing",
467
.flags = CFTYPE_NOT_ON_ROOT,
468
.read_u64 = freezer_parent_freezing_read,
469
},
470
{ } /* terminate */
471
};
472
473
struct cgroup_subsys freezer_cgrp_subsys = {
474
.css_alloc = freezer_css_alloc,
475
.css_online = freezer_css_online,
476
.css_offline = freezer_css_offline,
477
.css_free = freezer_css_free,
478
.attach = freezer_attach,
479
.fork = freezer_fork,
480
.legacy_cftypes = files,
481
};
482
483