Blame view
kernel/cgroup_freezer.c
9.78 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); } |
5a7aadfe2 Freezer: Fix bugg... |
50 |
int cgroup_freezing_or_frozen(struct task_struct *task) |
dc52ddc0e container freezer... |
51 52 53 54 55 56 |
{ struct freezer *freezer; enum freezer_state state; task_lock(task); freezer = task_freezer(task); |
5a7aadfe2 Freezer: Fix bugg... |
57 58 59 60 |
if (!freezer->css.cgroup->parent) state = CGROUP_THAWED; /* root cgroup can't be frozen */ else state = freezer->state; |
dc52ddc0e container freezer... |
61 |
task_unlock(task); |
5a7aadfe2 Freezer: Fix bugg... |
62 |
return (state == CGROUP_FREEZING) || (state == CGROUP_FROZEN); |
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 159 |
/* Task is frozen or will freeze immediately when next it gets woken */ static bool is_task_frozen_enough(struct task_struct *task) { return frozen(task) || (task_is_stopped_or_traced(task) && freezing(task)); } |
dc52ddc0e container freezer... |
160 |
|
957a4eeaf container freezer... |
161 162 163 164 165 |
/* * 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... |
166 167 |
static int freezer_can_attach(struct cgroup_subsys *ss, struct cgroup *new_cgroup, |
be367d099 cgroups: let ss->... |
168 |
struct task_struct *task, bool threadgroup) |
dc52ddc0e container freezer... |
169 170 |
{ struct freezer *freezer; |
957a4eeaf container freezer... |
171 |
|
80a6a2cf3 freezer_cg: remov... |
172 173 174 175 176 177 |
/* * Anything frozen can't move or be moved to/from. * * Since orig_freezer->state == FROZEN means that @task has been * frozen, so it's sufficient to check the latter condition. */ |
957a4eeaf container freezer... |
178 179 180 |
if (is_task_frozen_enough(task)) return -EBUSY; |
dc52ddc0e container freezer... |
181 |
|
dc52ddc0e container freezer... |
182 |
freezer = cgroup_freezer(new_cgroup); |
81dcf33c2 container freezer... |
183 |
if (freezer->state == CGROUP_FROZEN) |
957a4eeaf container freezer... |
184 |
return -EBUSY; |
be367d099 cgroups: let ss->... |
185 186 187 188 189 190 191 192 193 194 195 196 |
if (threadgroup) { struct task_struct *c; rcu_read_lock(); list_for_each_entry_rcu(c, &task->thread_group, thread_group) { if (is_task_frozen_enough(c)) { rcu_read_unlock(); return -EBUSY; } } rcu_read_unlock(); } |
80a6a2cf3 freezer_cg: remov... |
197 |
return 0; |
dc52ddc0e container freezer... |
198 199 200 201 202 |
} static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task) { struct freezer *freezer; |
687446760 freezer_cg: remov... |
203 204 205 206 |
/* * 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... |
207 208 |
* function call. Nevertheless, apply RCU read-side critical * section to suppress RCU lockdep false positives. |
687446760 freezer_cg: remov... |
209 |
*/ |
8b46f8808 rcu: Fix RCU lock... |
210 |
rcu_read_lock(); |
dc52ddc0e container freezer... |
211 |
freezer = task_freezer(task); |
8b46f8808 rcu: Fix RCU lock... |
212 |
rcu_read_unlock(); |
dc52ddc0e container freezer... |
213 |
|
3b1b3f6e5 freezer_cg: disab... |
214 215 216 217 218 219 |
/* * The root cgroup is non-freezable, so we can skip the * following check. */ if (!freezer->css.cgroup->parent) return; |
dc52ddc0e container freezer... |
220 |
spin_lock_irq(&freezer->lock); |
7ccb97437 freezer_cg: fix i... |
221 |
BUG_ON(freezer->state == CGROUP_FROZEN); |
81dcf33c2 container freezer... |
222 223 |
/* Locking avoids race with FREEZING -> THAWED transitions. */ if (freezer->state == CGROUP_FREEZING) |
dc52ddc0e container freezer... |
224 225 226 227 228 229 230 |
freeze_task(task, true); spin_unlock_irq(&freezer->lock); } /* * caller must hold freezer->lock */ |
1aece3483 container freezer... |
231 232 |
static void update_freezer_state(struct cgroup *cgroup, struct freezer *freezer) |
dc52ddc0e container freezer... |
233 234 235 236 237 238 239 240 |
{ struct cgroup_iter it; struct task_struct *task; unsigned int nfrozen = 0, ntotal = 0; cgroup_iter_start(cgroup, &it); while ((task = cgroup_iter_next(cgroup, &it))) { ntotal++; |
957a4eeaf container freezer... |
241 |
if (is_task_frozen_enough(task)) |
dc52ddc0e container freezer... |
242 243 244 245 246 247 248 249 250 |
nfrozen++; } /* * Transition to FROZEN when no new tasks can be added ensures * that we never exist in the FROZEN state while there are unfrozen * tasks. */ if (nfrozen == ntotal) |
81dcf33c2 container freezer... |
251 |
freezer->state = CGROUP_FROZEN; |
1aece3483 container freezer... |
252 253 254 255 |
else if (nfrozen > 0) freezer->state = CGROUP_FREEZING; else freezer->state = CGROUP_THAWED; |
dc52ddc0e container freezer... |
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
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... |
271 |
if (state == CGROUP_FREEZING) { |
dc52ddc0e container freezer... |
272 273 |
/* We change from FREEZING to FROZEN lazily if the cgroup was * only partially frozen when we exitted write. */ |
1aece3483 container freezer... |
274 |
update_freezer_state(cgroup, freezer); |
dc52ddc0e container freezer... |
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
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... |
291 |
freezer->state = CGROUP_FREEZING; |
dc52ddc0e container freezer... |
292 293 294 295 |
cgroup_iter_start(cgroup, &it); while ((task = cgroup_iter_next(cgroup, &it))) { if (!freeze_task(task, true)) continue; |
957a4eeaf container freezer... |
296 |
if (is_task_frozen_enough(task)) |
dc52ddc0e container freezer... |
297 298 299 300 301 302 303 304 |
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... |
305 |
static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer) |
dc52ddc0e container freezer... |
306 307 308 309 310 311 |
{ 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... |
312 |
thaw_process(task); |
dc52ddc0e container freezer... |
313 314 |
} cgroup_iter_end(cgroup, &it); |
dc52ddc0e container freezer... |
315 |
|
00c2e63c3 freezer_cg: use t... |
316 |
freezer->state = CGROUP_THAWED; |
dc52ddc0e container freezer... |
317 318 319 320 321 322 323 324 325 |
} 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... |
326 |
|
dc52ddc0e container freezer... |
327 |
spin_lock_irq(&freezer->lock); |
51308ee59 freezer_cg: simpl... |
328 |
|
1aece3483 container freezer... |
329 |
update_freezer_state(cgroup, freezer); |
dc52ddc0e container freezer... |
330 331 |
if (goal_state == freezer->state) goto out; |
51308ee59 freezer_cg: simpl... |
332 333 |
switch (goal_state) { |
81dcf33c2 container freezer... |
334 |
case CGROUP_THAWED: |
51308ee59 freezer_cg: simpl... |
335 |
unfreeze_cgroup(cgroup, freezer); |
dc52ddc0e container freezer... |
336 |
break; |
81dcf33c2 container freezer... |
337 |
case CGROUP_FROZEN: |
51308ee59 freezer_cg: simpl... |
338 |
retval = try_to_freeze_cgroup(cgroup, freezer); |
dc52ddc0e container freezer... |
339 340 |
break; default: |
51308ee59 freezer_cg: simpl... |
341 |
BUG(); |
dc52ddc0e container freezer... |
342 343 344 345 346 347 348 349 350 351 352 353 354 |
} 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... |
355 356 357 358 |
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... |
359 |
else |
3b1b3f6e5 freezer_cg: disab... |
360 |
return -EINVAL; |
dc52ddc0e container freezer... |
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
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... |
379 380 |
if (!cgroup->parent) return 0; |
dc52ddc0e container freezer... |
381 382 383 384 385 386 387 388 389 390 391 392 393 394 |
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, .attach = NULL, .fork = freezer_fork, .exit = NULL, }; |