Blame view
kernel/cgroup_freezer.c
9.14 KB
dc52ddc0e container freezer... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/* * 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. */ |
9984de1a5 kernel: Map most ... |
16 |
#include <linux/export.h> |
5a0e3ad6a include cleanup: ... |
17 |
#include <linux/slab.h> |
dc52ddc0e container freezer... |
18 19 20 21 22 23 24 |
#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... |
25 26 27 |
CGROUP_THAWED = 0, CGROUP_FREEZING, CGROUP_FROZEN, |
dc52ddc0e container freezer... |
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
}; 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); } |
22b4e111f cgroup_freezer: p... |
49 |
bool cgroup_freezing(struct task_struct *task) |
dc52ddc0e container freezer... |
50 |
{ |
22b4e111f cgroup_freezer: p... |
51 52 |
enum freezer_state state; bool ret; |
dc52ddc0e container freezer... |
53 |
|
22b4e111f cgroup_freezer: p... |
54 55 56 57 58 59 |
rcu_read_lock(); state = task_freezer(task)->state; ret = state == CGROUP_FREEZING || state == CGROUP_FROZEN; rcu_read_unlock(); return ret; |
dc52ddc0e container freezer... |
60 61 62 63 64 65 66 |
} /* * cgroups_write_string() limits the size of freezer state strings to * CGROUP_LOCAL_BUFFER_SIZE */ static const char *freezer_state_strs[] = { |
81dcf33c2 container freezer... |
67 |
"THAWED", |
dc52ddc0e container freezer... |
68 69 70 71 72 73 74 75 76 |
"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... |
77 78 79 80 |
* (THAWED) --FROZEN--> (FREEZING) --FROZEN--> (FROZEN) * ^ ^ | | * | \_______THAWED_______/ | * \__________________________THAWED____________/ |
dc52ddc0e container freezer... |
81 82 83 84 85 86 |
*/ struct cgroup_subsys freezer_subsys; /* Locks taken and their ordering * ------------------------------ |
dc52ddc0e container freezer... |
87 |
* cgroup_mutex (AKA cgroup_lock) |
dc52ddc0e container freezer... |
88 |
* freezer->lock |
8f77578cc Freezer / cgroup ... |
89 90 |
* css_set_lock * task->alloc_lock (AKA task_lock) |
dc52ddc0e container freezer... |
91 92 93 94 95 96 97 |
* 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 ... |
98 99 |
* freezer_can_attach(): * cgroup_mutex (held by caller of can_attach) |
dc52ddc0e container freezer... |
100 |
* |
dc52ddc0e container freezer... |
101 |
* freezer_fork() (preserving fork() performance means can't take cgroup_mutex): |
dc52ddc0e container freezer... |
102 103 104 105 106 107 |
* freezer->lock * sighand->siglock (if the cgroup is freezing) * * freezer_read(): * cgroup_mutex * freezer->lock |
8f77578cc Freezer / cgroup ... |
108 109 |
* write_lock css_set_lock (cgroup iterator start) * task->alloc_lock |
dc52ddc0e container freezer... |
110 111 112 113 114 |
* read_lock css_set_lock (cgroup iterator start) * * freezer_write() (freeze): * cgroup_mutex * freezer->lock |
8f77578cc Freezer / cgroup ... |
115 116 |
* write_lock css_set_lock (cgroup iterator start) * task->alloc_lock |
dc52ddc0e container freezer... |
117 |
* read_lock css_set_lock (cgroup iterator start) |
8f77578cc Freezer / cgroup ... |
118 |
* sighand->siglock (fake signal delivery inside freeze_task()) |
dc52ddc0e container freezer... |
119 120 121 122 |
* * freezer_write() (unfreeze): * cgroup_mutex * freezer->lock |
8f77578cc Freezer / cgroup ... |
123 124 |
* write_lock css_set_lock (cgroup iterator start) * task->alloc_lock |
dc52ddc0e container freezer... |
125 |
* read_lock css_set_lock (cgroup iterator start) |
a5be2d0d1 freezer: rename t... |
126 |
* task->alloc_lock (inside __thaw_task(), prevents race with refrigerator()) |
dc52ddc0e container freezer... |
127 128 |
* sighand->siglock */ |
761b3ef50 cgroup: remove cg... |
129 |
static struct cgroup_subsys_state *freezer_create(struct cgroup *cgroup) |
dc52ddc0e container freezer... |
130 131 132 133 134 135 136 137 |
{ struct freezer *freezer; freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL); if (!freezer) return ERR_PTR(-ENOMEM); spin_lock_init(&freezer->lock); |
81dcf33c2 container freezer... |
138 |
freezer->state = CGROUP_THAWED; |
dc52ddc0e container freezer... |
139 140 |
return &freezer->css; } |
761b3ef50 cgroup: remove cg... |
141 |
static void freezer_destroy(struct cgroup *cgroup) |
dc52ddc0e container freezer... |
142 |
{ |
a3201227f freezer: make fre... |
143 144 145 146 147 |
struct freezer *freezer = cgroup_freezer(cgroup); if (freezer->state != CGROUP_THAWED) atomic_dec(&system_freezing_cnt); kfree(freezer); |
dc52ddc0e container freezer... |
148 |
} |
884a45d96 cgroup_freezer: f... |
149 150 151 152 153 154 |
/* 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)); } |
957a4eeaf container freezer... |
155 156 157 158 159 |
/* * 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. */ |
761b3ef50 cgroup: remove cg... |
160 |
static int freezer_can_attach(struct cgroup *new_cgroup, |
2f7ee5691 cgroup: introduce... |
161 |
struct cgroup_taskset *tset) |
dc52ddc0e container freezer... |
162 163 |
{ struct freezer *freezer; |
bb9d97b6d cgroup: don't use... |
164 |
struct task_struct *task; |
957a4eeaf container freezer... |
165 |
|
80a6a2cf3 freezer_cg: remov... |
166 167 |
/* * Anything frozen can't move or be moved to/from. |
80a6a2cf3 freezer_cg: remov... |
168 |
*/ |
bb9d97b6d cgroup: don't use... |
169 170 171 |
cgroup_taskset_for_each(task, new_cgroup, tset) if (cgroup_freezing(task)) return -EBUSY; |
957a4eeaf container freezer... |
172 |
|
0bdba580a cgroup_freezer: f... |
173 174 |
freezer = cgroup_freezer(new_cgroup); if (freezer->state != CGROUP_THAWED) |
957a4eeaf container freezer... |
175 |
return -EBUSY; |
dc52ddc0e container freezer... |
176 |
|
f780bdb7c cgroups: add per-... |
177 178 |
return 0; } |
761b3ef50 cgroup: remove cg... |
179 |
static void freezer_fork(struct task_struct *task) |
dc52ddc0e container freezer... |
180 181 |
{ struct freezer *freezer; |
687446760 freezer_cg: remov... |
182 183 184 185 |
/* * 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... |
186 187 |
* function call. Nevertheless, apply RCU read-side critical * section to suppress RCU lockdep false positives. |
687446760 freezer_cg: remov... |
188 |
*/ |
8b46f8808 rcu: Fix RCU lock... |
189 |
rcu_read_lock(); |
dc52ddc0e container freezer... |
190 |
freezer = task_freezer(task); |
8b46f8808 rcu: Fix RCU lock... |
191 |
rcu_read_unlock(); |
dc52ddc0e container freezer... |
192 |
|
3b1b3f6e5 freezer_cg: disab... |
193 194 195 196 197 198 |
/* * The root cgroup is non-freezable, so we can skip the * following check. */ if (!freezer->css.cgroup->parent) return; |
dc52ddc0e container freezer... |
199 |
spin_lock_irq(&freezer->lock); |
7ccb97437 freezer_cg: fix i... |
200 |
BUG_ON(freezer->state == CGROUP_FROZEN); |
81dcf33c2 container freezer... |
201 202 |
/* Locking avoids race with FREEZING -> THAWED transitions. */ if (freezer->state == CGROUP_FREEZING) |
839e3407d freezer: remove u... |
203 |
freeze_task(task); |
dc52ddc0e container freezer... |
204 205 206 207 208 209 |
spin_unlock_irq(&freezer->lock); } /* * caller must hold freezer->lock */ |
2d3cbf8bc cgroup_freezer: u... |
210 |
static void update_if_frozen(struct cgroup *cgroup, |
1aece3483 container freezer... |
211 |
struct freezer *freezer) |
dc52ddc0e container freezer... |
212 213 214 215 |
{ struct cgroup_iter it; struct task_struct *task; unsigned int nfrozen = 0, ntotal = 0; |
2d3cbf8bc cgroup_freezer: u... |
216 |
enum freezer_state old_state = freezer->state; |
dc52ddc0e container freezer... |
217 218 219 220 |
cgroup_iter_start(cgroup, &it); while ((task = cgroup_iter_next(cgroup, &it))) { ntotal++; |
b00f4dc5f Merge branch 'mas... |
221 |
if (freezing(task) && is_task_frozen_enough(task)) |
dc52ddc0e container freezer... |
222 223 |
nfrozen++; } |
2d3cbf8bc cgroup_freezer: u... |
224 225 226 227 228 229 230 231 |
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... |
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
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... |
247 |
if (state == CGROUP_FREEZING) { |
dc52ddc0e container freezer... |
248 249 |
/* We change from FREEZING to FROZEN lazily if the cgroup was * only partially frozen when we exitted write. */ |
2d3cbf8bc cgroup_freezer: u... |
250 |
update_if_frozen(cgroup, freezer); |
dc52ddc0e container freezer... |
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
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; |
dc52ddc0e container freezer... |
267 268 |
cgroup_iter_start(cgroup, &it); while ((task = cgroup_iter_next(cgroup, &it))) { |
839e3407d freezer: remove u... |
269 |
if (!freeze_task(task)) |
dc52ddc0e container freezer... |
270 |
continue; |
884a45d96 cgroup_freezer: f... |
271 |
if (is_task_frozen_enough(task)) |
dc52ddc0e container freezer... |
272 273 274 275 276 277 278 279 |
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... |
280 |
static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer) |
dc52ddc0e container freezer... |
281 282 283 284 285 |
{ struct cgroup_iter it; struct task_struct *task; cgroup_iter_start(cgroup, &it); |
a5be2d0d1 freezer: rename t... |
286 287 |
while ((task = cgroup_iter_next(cgroup, &it))) __thaw_task(task); |
dc52ddc0e container freezer... |
288 |
cgroup_iter_end(cgroup, &it); |
dc52ddc0e container freezer... |
289 290 291 292 293 294 295 296 297 |
} 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... |
298 |
|
dc52ddc0e container freezer... |
299 |
spin_lock_irq(&freezer->lock); |
51308ee59 freezer_cg: simpl... |
300 |
|
2d3cbf8bc cgroup_freezer: u... |
301 |
update_if_frozen(cgroup, freezer); |
51308ee59 freezer_cg: simpl... |
302 303 |
switch (goal_state) { |
81dcf33c2 container freezer... |
304 |
case CGROUP_THAWED: |
a3201227f freezer: make fre... |
305 306 |
if (freezer->state != CGROUP_THAWED) atomic_dec(&system_freezing_cnt); |
22b4e111f cgroup_freezer: p... |
307 |
freezer->state = CGROUP_THAWED; |
51308ee59 freezer_cg: simpl... |
308 |
unfreeze_cgroup(cgroup, freezer); |
dc52ddc0e container freezer... |
309 |
break; |
81dcf33c2 container freezer... |
310 |
case CGROUP_FROZEN: |
a3201227f freezer: make fre... |
311 312 |
if (freezer->state == CGROUP_THAWED) atomic_inc(&system_freezing_cnt); |
22b4e111f cgroup_freezer: p... |
313 |
freezer->state = CGROUP_FREEZING; |
51308ee59 freezer_cg: simpl... |
314 |
retval = try_to_freeze_cgroup(cgroup, freezer); |
dc52ddc0e container freezer... |
315 316 |
break; default: |
51308ee59 freezer_cg: simpl... |
317 |
BUG(); |
dc52ddc0e container freezer... |
318 |
} |
22b4e111f cgroup_freezer: p... |
319 |
|
dc52ddc0e container freezer... |
320 321 322 323 324 325 326 327 328 329 330 |
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... |
331 332 333 334 |
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... |
335 |
else |
3b1b3f6e5 freezer_cg: disab... |
336 |
return -EINVAL; |
dc52ddc0e container freezer... |
337 338 339 340 341 342 343 344 345 346 347 |
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", |
4baf6e332 cgroup: convert a... |
348 |
.flags = CFTYPE_NOT_ON_ROOT, |
dc52ddc0e container freezer... |
349 350 351 |
.read_seq_string = freezer_read, .write_string = freezer_write, }, |
4baf6e332 cgroup: convert a... |
352 |
{ } /* terminate */ |
dc52ddc0e container freezer... |
353 |
}; |
dc52ddc0e container freezer... |
354 355 356 357 |
struct cgroup_subsys freezer_subsys = { .name = "freezer", .create = freezer_create, .destroy = freezer_destroy, |
dc52ddc0e container freezer... |
358 359 |
.subsys_id = freezer_subsys_id, .can_attach = freezer_can_attach, |
dc52ddc0e container freezer... |
360 |
.fork = freezer_fork, |
4baf6e332 cgroup: convert a... |
361 |
.base_cftypes = files, |
dc52ddc0e container freezer... |
362 |
}; |