Blame view
kernel/cgroup_freezer.c
9.36 KB
dc52ddc0e container freezer... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* * cgroup_freezer.c - control group freezer subsystem * * Copyright IBM Corporation, 2007 * * Author : Cedric Le Goater <clg@fr.ibm.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2.1 of the GNU Lesser General Public License * as published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include <linux/module.h> |
5a0e3ad6a include cleanup: ... |
18 |
#include <linux/slab.h> |
dc52ddc0e container freezer... |
19 20 21 22 23 24 25 |
#include <linux/cgroup.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/freezer.h> #include <linux/seq_file.h> enum freezer_state { |
81dcf33c2 container freezer... |
26 27 28 |
CGROUP_THAWED = 0, CGROUP_FREEZING, CGROUP_FROZEN, |
dc52ddc0e container freezer... |
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
}; struct freezer { struct cgroup_subsys_state css; enum freezer_state state; spinlock_t lock; /* protects _writes_ to state */ }; static inline struct freezer *cgroup_freezer( struct cgroup *cgroup) { return container_of( cgroup_subsys_state(cgroup, freezer_subsys_id), struct freezer, css); } static inline struct freezer *task_freezer(struct task_struct *task) { return container_of(task_subsys_state(task, freezer_subsys_id), struct freezer, css); } |
d5de4ddb1 cgroup_freezer: u... |
50 |
static inline int __cgroup_freezing_or_frozen(struct task_struct *task) |
dc52ddc0e container freezer... |
51 |
{ |
d5de4ddb1 cgroup_freezer: u... |
52 53 54 |
enum freezer_state state = task_freezer(task)->state; return (state == CGROUP_FREEZING) || (state == CGROUP_FROZEN); } |
dc52ddc0e container freezer... |
55 |
|
d5de4ddb1 cgroup_freezer: u... |
56 57 58 |
int cgroup_freezing_or_frozen(struct task_struct *task) { int result; |
dc52ddc0e container freezer... |
59 |
task_lock(task); |
d5de4ddb1 cgroup_freezer: u... |
60 |
result = __cgroup_freezing_or_frozen(task); |
dc52ddc0e container freezer... |
61 |
task_unlock(task); |
d5de4ddb1 cgroup_freezer: u... |
62 |
return result; |
dc52ddc0e container freezer... |
63 64 65 66 67 68 69 |
} /* * cgroups_write_string() limits the size of freezer state strings to * CGROUP_LOCAL_BUFFER_SIZE */ static const char *freezer_state_strs[] = { |
81dcf33c2 container freezer... |
70 |
"THAWED", |
dc52ddc0e container freezer... |
71 72 73 74 75 76 77 78 79 |
"FREEZING", "FROZEN", }; /* * State diagram * Transitions are caused by userspace writes to the freezer.state file. * The values in parenthesis are state labels. The rest are edge labels. * |
81dcf33c2 container freezer... |
80 81 82 83 |
* (THAWED) --FROZEN--> (FREEZING) --FROZEN--> (FROZEN) * ^ ^ | | * | \_______THAWED_______/ | * \__________________________THAWED____________/ |
dc52ddc0e container freezer... |
84 85 86 87 88 89 |
*/ struct cgroup_subsys freezer_subsys; /* Locks taken and their ordering * ------------------------------ |
dc52ddc0e container freezer... |
90 |
* cgroup_mutex (AKA cgroup_lock) |
dc52ddc0e container freezer... |
91 |
* freezer->lock |
8f77578cc Freezer / cgroup ... |
92 93 |
* css_set_lock * task->alloc_lock (AKA task_lock) |
dc52ddc0e container freezer... |
94 95 96 97 98 99 100 |
* task->sighand->siglock * * cgroup code forces css_set_lock to be taken before task->alloc_lock * * freezer_create(), freezer_destroy(): * cgroup_mutex [ by cgroup core ] * |
8f77578cc Freezer / cgroup ... |
101 102 |
* freezer_can_attach(): * cgroup_mutex (held by caller of can_attach) |
dc52ddc0e container freezer... |
103 |
* |
8f77578cc Freezer / cgroup ... |
104 |
* cgroup_freezing_or_frozen(): |
dc52ddc0e container freezer... |
105 106 107 |
* task->alloc_lock (to get task's cgroup) * * freezer_fork() (preserving fork() performance means can't take cgroup_mutex): |
dc52ddc0e container freezer... |
108 109 110 111 112 113 |
* freezer->lock * sighand->siglock (if the cgroup is freezing) * * freezer_read(): * cgroup_mutex * freezer->lock |
8f77578cc Freezer / cgroup ... |
114 115 |
* write_lock css_set_lock (cgroup iterator start) * task->alloc_lock |
dc52ddc0e container freezer... |
116 117 118 119 120 |
* read_lock css_set_lock (cgroup iterator start) * * freezer_write() (freeze): * cgroup_mutex * freezer->lock |
8f77578cc Freezer / cgroup ... |
121 122 |
* write_lock css_set_lock (cgroup iterator start) * task->alloc_lock |
dc52ddc0e container freezer... |
123 |
* read_lock css_set_lock (cgroup iterator start) |
8f77578cc Freezer / cgroup ... |
124 |
* sighand->siglock (fake signal delivery inside freeze_task()) |
dc52ddc0e container freezer... |
125 126 127 128 |
* * freezer_write() (unfreeze): * cgroup_mutex * freezer->lock |
8f77578cc Freezer / cgroup ... |
129 130 |
* write_lock css_set_lock (cgroup iterator start) * task->alloc_lock |
dc52ddc0e container freezer... |
131 |
* read_lock css_set_lock (cgroup iterator start) |
8f77578cc Freezer / cgroup ... |
132 |
* task->alloc_lock (inside thaw_process(), prevents race with refrigerator()) |
dc52ddc0e container freezer... |
133 134 135 136 137 138 139 140 141 142 143 144 |
* sighand->siglock */ static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss, struct cgroup *cgroup) { struct freezer *freezer; freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL); if (!freezer) return ERR_PTR(-ENOMEM); spin_lock_init(&freezer->lock); |
81dcf33c2 container freezer... |
145 |
freezer->state = CGROUP_THAWED; |
dc52ddc0e container freezer... |
146 147 148 149 150 151 152 153 |
return &freezer->css; } static void freezer_destroy(struct cgroup_subsys *ss, struct cgroup *cgroup) { kfree(cgroup_freezer(cgroup)); } |
957a4eeaf container freezer... |
154 155 156 157 158 |
/* * The call to cgroup_lock() in the freezer.state write method prevents * a write to that file racing against an attach, and hence the * can_attach() result will remain valid until the attach completes. */ |
dc52ddc0e container freezer... |
159 160 |
static int freezer_can_attach(struct cgroup_subsys *ss, struct cgroup *new_cgroup, |
f780bdb7c cgroups: add per-... |
161 |
struct task_struct *task) |
dc52ddc0e container freezer... |
162 163 |
{ struct freezer *freezer; |
957a4eeaf container freezer... |
164 |
|
80a6a2cf3 freezer_cg: remov... |
165 166 |
/* * Anything frozen can't move or be moved to/from. |
80a6a2cf3 freezer_cg: remov... |
167 |
*/ |
957a4eeaf container freezer... |
168 |
|
0bdba580a cgroup_freezer: f... |
169 170 |
freezer = cgroup_freezer(new_cgroup); if (freezer->state != CGROUP_THAWED) |
957a4eeaf container freezer... |
171 |
return -EBUSY; |
dc52ddc0e container freezer... |
172 |
|
f780bdb7c cgroups: add per-... |
173 174 175 176 177 |
return 0; } static int freezer_can_attach_task(struct cgroup *cgrp, struct task_struct *tsk) { |
0bdba580a cgroup_freezer: f... |
178 |
rcu_read_lock(); |
f780bdb7c cgroups: add per-... |
179 |
if (__cgroup_freezing_or_frozen(tsk)) { |
0bdba580a cgroup_freezer: f... |
180 |
rcu_read_unlock(); |
957a4eeaf container freezer... |
181 |
return -EBUSY; |
0bdba580a cgroup_freezer: f... |
182 183 |
} rcu_read_unlock(); |
80a6a2cf3 freezer_cg: remov... |
184 |
return 0; |
dc52ddc0e container freezer... |
185 186 187 188 189 |
} static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task) { struct freezer *freezer; |
687446760 freezer_cg: remov... |
190 191 192 193 |
/* * No lock is needed, since the task isn't on tasklist yet, * so it can't be moved to another cgroup, which means the * freezer won't be removed and will be valid during this |
8b46f8808 rcu: Fix RCU lock... |
194 195 |
* function call. Nevertheless, apply RCU read-side critical * section to suppress RCU lockdep false positives. |
687446760 freezer_cg: remov... |
196 |
*/ |
8b46f8808 rcu: Fix RCU lock... |
197 |
rcu_read_lock(); |
dc52ddc0e container freezer... |
198 |
freezer = task_freezer(task); |
8b46f8808 rcu: Fix RCU lock... |
199 |
rcu_read_unlock(); |
dc52ddc0e container freezer... |
200 |
|
3b1b3f6e5 freezer_cg: disab... |
201 202 203 204 205 206 |
/* * The root cgroup is non-freezable, so we can skip the * following check. */ if (!freezer->css.cgroup->parent) return; |
dc52ddc0e container freezer... |
207 |
spin_lock_irq(&freezer->lock); |
7ccb97437 freezer_cg: fix i... |
208 |
BUG_ON(freezer->state == CGROUP_FROZEN); |
81dcf33c2 container freezer... |
209 210 |
/* Locking avoids race with FREEZING -> THAWED transitions. */ if (freezer->state == CGROUP_FREEZING) |
dc52ddc0e container freezer... |
211 212 213 214 215 216 217 |
freeze_task(task, true); spin_unlock_irq(&freezer->lock); } /* * caller must hold freezer->lock */ |
2d3cbf8bc cgroup_freezer: u... |
218 |
static void update_if_frozen(struct cgroup *cgroup, |
1aece3483 container freezer... |
219 |
struct freezer *freezer) |
dc52ddc0e container freezer... |
220 221 222 223 |
{ struct cgroup_iter it; struct task_struct *task; unsigned int nfrozen = 0, ntotal = 0; |
2d3cbf8bc cgroup_freezer: u... |
224 |
enum freezer_state old_state = freezer->state; |
dc52ddc0e container freezer... |
225 226 227 228 |
cgroup_iter_start(cgroup, &it); while ((task = cgroup_iter_next(cgroup, &it))) { ntotal++; |
2d3cbf8bc cgroup_freezer: u... |
229 |
if (frozen(task)) |
dc52ddc0e container freezer... |
230 231 |
nfrozen++; } |
2d3cbf8bc cgroup_freezer: u... |
232 233 234 235 236 237 238 239 |
if (old_state == CGROUP_THAWED) { BUG_ON(nfrozen > 0); } else if (old_state == CGROUP_FREEZING) { if (nfrozen == ntotal) freezer->state = CGROUP_FROZEN; } else { /* old_state == CGROUP_FROZEN */ BUG_ON(nfrozen != ntotal); } |
dc52ddc0e container freezer... |
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
cgroup_iter_end(cgroup, &it); } static int freezer_read(struct cgroup *cgroup, struct cftype *cft, struct seq_file *m) { struct freezer *freezer; enum freezer_state state; if (!cgroup_lock_live_group(cgroup)) return -ENODEV; freezer = cgroup_freezer(cgroup); spin_lock_irq(&freezer->lock); state = freezer->state; |
81dcf33c2 container freezer... |
255 |
if (state == CGROUP_FREEZING) { |
dc52ddc0e container freezer... |
256 257 |
/* We change from FREEZING to FROZEN lazily if the cgroup was * only partially frozen when we exitted write. */ |
2d3cbf8bc cgroup_freezer: u... |
258 |
update_if_frozen(cgroup, freezer); |
dc52ddc0e container freezer... |
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
state = freezer->state; } spin_unlock_irq(&freezer->lock); cgroup_unlock(); seq_puts(m, freezer_state_strs[state]); seq_putc(m, ' '); return 0; } static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer) { struct cgroup_iter it; struct task_struct *task; unsigned int num_cant_freeze_now = 0; |
81dcf33c2 container freezer... |
275 |
freezer->state = CGROUP_FREEZING; |
dc52ddc0e container freezer... |
276 277 278 279 |
cgroup_iter_start(cgroup, &it); while ((task = cgroup_iter_next(cgroup, &it))) { if (!freeze_task(task, true)) continue; |
2d3cbf8bc cgroup_freezer: u... |
280 |
if (frozen(task)) |
dc52ddc0e container freezer... |
281 282 283 284 285 286 287 288 |
continue; if (!freezing(task) && !freezer_should_skip(task)) num_cant_freeze_now++; } cgroup_iter_end(cgroup, &it); return num_cant_freeze_now ? -EBUSY : 0; } |
00c2e63c3 freezer_cg: use t... |
289 |
static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer) |
dc52ddc0e container freezer... |
290 291 292 293 294 295 |
{ struct cgroup_iter it; struct task_struct *task; cgroup_iter_start(cgroup, &it); while ((task = cgroup_iter_next(cgroup, &it))) { |
00c2e63c3 freezer_cg: use t... |
296 |
thaw_process(task); |
dc52ddc0e container freezer... |
297 298 |
} cgroup_iter_end(cgroup, &it); |
dc52ddc0e container freezer... |
299 |
|
00c2e63c3 freezer_cg: use t... |
300 |
freezer->state = CGROUP_THAWED; |
dc52ddc0e container freezer... |
301 302 303 304 305 306 307 308 309 |
} static int freezer_change_state(struct cgroup *cgroup, enum freezer_state goal_state) { struct freezer *freezer; int retval = 0; freezer = cgroup_freezer(cgroup); |
51308ee59 freezer_cg: simpl... |
310 |
|
dc52ddc0e container freezer... |
311 |
spin_lock_irq(&freezer->lock); |
51308ee59 freezer_cg: simpl... |
312 |
|
2d3cbf8bc cgroup_freezer: u... |
313 |
update_if_frozen(cgroup, freezer); |
dc52ddc0e container freezer... |
314 315 |
if (goal_state == freezer->state) goto out; |
51308ee59 freezer_cg: simpl... |
316 317 |
switch (goal_state) { |
81dcf33c2 container freezer... |
318 |
case CGROUP_THAWED: |
51308ee59 freezer_cg: simpl... |
319 |
unfreeze_cgroup(cgroup, freezer); |
dc52ddc0e container freezer... |
320 |
break; |
81dcf33c2 container freezer... |
321 |
case CGROUP_FROZEN: |
51308ee59 freezer_cg: simpl... |
322 |
retval = try_to_freeze_cgroup(cgroup, freezer); |
dc52ddc0e container freezer... |
323 324 |
break; default: |
51308ee59 freezer_cg: simpl... |
325 |
BUG(); |
dc52ddc0e container freezer... |
326 327 328 329 330 331 332 333 334 335 336 337 338 |
} out: spin_unlock_irq(&freezer->lock); return retval; } static int freezer_write(struct cgroup *cgroup, struct cftype *cft, const char *buffer) { int retval; enum freezer_state goal_state; |
81dcf33c2 container freezer... |
339 340 341 342 |
if (strcmp(buffer, freezer_state_strs[CGROUP_THAWED]) == 0) goal_state = CGROUP_THAWED; else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0) goal_state = CGROUP_FROZEN; |
dc52ddc0e container freezer... |
343 |
else |
3b1b3f6e5 freezer_cg: disab... |
344 |
return -EINVAL; |
dc52ddc0e container freezer... |
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 |
if (!cgroup_lock_live_group(cgroup)) return -ENODEV; retval = freezer_change_state(cgroup, goal_state); cgroup_unlock(); return retval; } static struct cftype files[] = { { .name = "state", .read_seq_string = freezer_read, .write_string = freezer_write, }, }; static int freezer_populate(struct cgroup_subsys *ss, struct cgroup *cgroup) { |
3b1b3f6e5 freezer_cg: disab... |
363 364 |
if (!cgroup->parent) return 0; |
dc52ddc0e container freezer... |
365 366 367 368 369 370 371 372 373 374 |
return cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files)); } struct cgroup_subsys freezer_subsys = { .name = "freezer", .create = freezer_create, .destroy = freezer_destroy, .populate = freezer_populate, .subsys_id = freezer_subsys_id, .can_attach = freezer_can_attach, |
f780bdb7c cgroups: add per-... |
375 376 377 |
.can_attach_task = freezer_can_attach_task, .pre_attach = NULL, .attach_task = NULL, |
dc52ddc0e container freezer... |
378 379 380 381 |
.attach = NULL, .fork = freezer_fork, .exit = NULL, }; |