at v3.13 487 lines 13 kB view raw
1/* 2 * cgroup_freezer.c - control group freezer subsystem 3 * 4 * Copyright IBM Corporation, 2007 5 * 6 * Author : Cedric Le Goater <clg@fr.ibm.com> 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 25/* 26 * A cgroup is freezing if any FREEZING flags are set. FREEZING_SELF is 27 * set if "FROZEN" is written to freezer.state cgroupfs file, and cleared 28 * for "THAWED". FREEZING_PARENT is set if the parent freezer is FREEZING 29 * for whatever reason. IOW, a cgroup has FREEZING_PARENT set if one of 30 * its ancestors has FREEZING_SELF set. 31 */ 32enum freezer_state_flags { 33 CGROUP_FREEZER_ONLINE = (1 << 0), /* freezer is fully online */ 34 CGROUP_FREEZING_SELF = (1 << 1), /* this freezer is freezing */ 35 CGROUP_FREEZING_PARENT = (1 << 2), /* the parent freezer is freezing */ 36 CGROUP_FROZEN = (1 << 3), /* this and its descendants frozen */ 37 38 /* mask for all FREEZING flags */ 39 CGROUP_FREEZING = CGROUP_FREEZING_SELF | CGROUP_FREEZING_PARENT, 40}; 41 42struct freezer { 43 struct cgroup_subsys_state css; 44 unsigned int state; 45 spinlock_t lock; 46}; 47 48static inline struct freezer *css_freezer(struct cgroup_subsys_state *css) 49{ 50 return css ? container_of(css, struct freezer, css) : NULL; 51} 52 53static inline struct freezer *task_freezer(struct task_struct *task) 54{ 55 return css_freezer(task_css(task, freezer_subsys_id)); 56} 57 58static struct freezer *parent_freezer(struct freezer *freezer) 59{ 60 return css_freezer(css_parent(&freezer->css)); 61} 62 63bool cgroup_freezing(struct task_struct *task) 64{ 65 bool ret; 66 67 rcu_read_lock(); 68 ret = task_freezer(task)->state & CGROUP_FREEZING; 69 rcu_read_unlock(); 70 71 return ret; 72} 73 74/* 75 * cgroups_write_string() limits the size of freezer state strings to 76 * CGROUP_LOCAL_BUFFER_SIZE 77 */ 78static const char *freezer_state_strs(unsigned int state) 79{ 80 if (state & CGROUP_FROZEN) 81 return "FROZEN"; 82 if (state & CGROUP_FREEZING) 83 return "FREEZING"; 84 return "THAWED"; 85}; 86 87struct cgroup_subsys freezer_subsys; 88 89static struct cgroup_subsys_state * 90freezer_css_alloc(struct cgroup_subsys_state *parent_css) 91{ 92 struct freezer *freezer; 93 94 freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL); 95 if (!freezer) 96 return ERR_PTR(-ENOMEM); 97 98 spin_lock_init(&freezer->lock); 99 return &freezer->css; 100} 101 102/** 103 * freezer_css_online - commit creation of a freezer css 104 * @css: css being created 105 * 106 * We're committing to creation of @css. Mark it online and inherit 107 * parent's freezing state while holding both parent's and our 108 * freezer->lock. 109 */ 110static int freezer_css_online(struct cgroup_subsys_state *css) 111{ 112 struct freezer *freezer = css_freezer(css); 113 struct freezer *parent = parent_freezer(freezer); 114 115 /* 116 * The following double locking and freezing state inheritance 117 * guarantee that @cgroup can never escape ancestors' freezing 118 * states. See css_for_each_descendant_pre() for details. 119 */ 120 if (parent) 121 spin_lock_irq(&parent->lock); 122 spin_lock_nested(&freezer->lock, SINGLE_DEPTH_NESTING); 123 124 freezer->state |= CGROUP_FREEZER_ONLINE; 125 126 if (parent && (parent->state & CGROUP_FREEZING)) { 127 freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN; 128 atomic_inc(&system_freezing_cnt); 129 } 130 131 spin_unlock(&freezer->lock); 132 if (parent) 133 spin_unlock_irq(&parent->lock); 134 135 return 0; 136} 137 138/** 139 * freezer_css_offline - initiate destruction of a freezer css 140 * @css: css being destroyed 141 * 142 * @css is going away. Mark it dead and decrement system_freezing_count if 143 * it was holding one. 144 */ 145static void freezer_css_offline(struct cgroup_subsys_state *css) 146{ 147 struct freezer *freezer = css_freezer(css); 148 149 spin_lock_irq(&freezer->lock); 150 151 if (freezer->state & CGROUP_FREEZING) 152 atomic_dec(&system_freezing_cnt); 153 154 freezer->state = 0; 155 156 spin_unlock_irq(&freezer->lock); 157} 158 159static void freezer_css_free(struct cgroup_subsys_state *css) 160{ 161 kfree(css_freezer(css)); 162} 163 164/* 165 * Tasks can be migrated into a different freezer anytime regardless of its 166 * current state. freezer_attach() is responsible for making new tasks 167 * conform to the current state. 168 * 169 * Freezer state changes and task migration are synchronized via 170 * @freezer->lock. freezer_attach() makes the new tasks conform to the 171 * current state and all following state changes can see the new tasks. 172 */ 173static void freezer_attach(struct cgroup_subsys_state *new_css, 174 struct cgroup_taskset *tset) 175{ 176 struct freezer *freezer = css_freezer(new_css); 177 struct task_struct *task; 178 bool clear_frozen = false; 179 180 spin_lock_irq(&freezer->lock); 181 182 /* 183 * Make the new tasks conform to the current state of @new_css. 184 * For simplicity, when migrating any task to a FROZEN cgroup, we 185 * revert it to FREEZING and let update_if_frozen() determine the 186 * correct state later. 187 * 188 * Tasks in @tset are on @new_css but may not conform to its 189 * current state before executing the following - !frozen tasks may 190 * be visible in a FROZEN cgroup and frozen tasks in a THAWED one. 191 */ 192 cgroup_taskset_for_each(task, new_css, tset) { 193 if (!(freezer->state & CGROUP_FREEZING)) { 194 __thaw_task(task); 195 } else { 196 freeze_task(task); 197 freezer->state &= ~CGROUP_FROZEN; 198 clear_frozen = true; 199 } 200 } 201 202 spin_unlock_irq(&freezer->lock); 203 204 /* 205 * Propagate FROZEN clearing upwards. We may race with 206 * update_if_frozen(), but as long as both work bottom-up, either 207 * update_if_frozen() sees child's FROZEN cleared or we clear the 208 * parent's FROZEN later. No parent w/ !FROZEN children can be 209 * left FROZEN. 210 */ 211 while (clear_frozen && (freezer = parent_freezer(freezer))) { 212 spin_lock_irq(&freezer->lock); 213 freezer->state &= ~CGROUP_FROZEN; 214 clear_frozen = freezer->state & CGROUP_FREEZING; 215 spin_unlock_irq(&freezer->lock); 216 } 217} 218 219static void freezer_fork(struct task_struct *task) 220{ 221 struct freezer *freezer; 222 223 rcu_read_lock(); 224 freezer = task_freezer(task); 225 226 /* 227 * The root cgroup is non-freezable, so we can skip the 228 * following check. 229 */ 230 if (!parent_freezer(freezer)) 231 goto out; 232 233 spin_lock_irq(&freezer->lock); 234 if (freezer->state & CGROUP_FREEZING) 235 freeze_task(task); 236 spin_unlock_irq(&freezer->lock); 237out: 238 rcu_read_unlock(); 239} 240 241/** 242 * update_if_frozen - update whether a cgroup finished freezing 243 * @css: css of interest 244 * 245 * Once FREEZING is initiated, transition to FROZEN is lazily updated by 246 * calling this function. If the current state is FREEZING but not FROZEN, 247 * this function checks whether all tasks of this cgroup and the descendant 248 * cgroups finished freezing and, if so, sets FROZEN. 249 * 250 * The caller is responsible for grabbing RCU read lock and calling 251 * update_if_frozen() on all descendants prior to invoking this function. 252 * 253 * Task states and freezer state might disagree while tasks are being 254 * migrated into or out of @css, so we can't verify task states against 255 * @freezer state here. See freezer_attach() for details. 256 */ 257static void update_if_frozen(struct cgroup_subsys_state *css) 258{ 259 struct freezer *freezer = css_freezer(css); 260 struct cgroup_subsys_state *pos; 261 struct css_task_iter it; 262 struct task_struct *task; 263 264 WARN_ON_ONCE(!rcu_read_lock_held()); 265 266 spin_lock_irq(&freezer->lock); 267 268 if (!(freezer->state & CGROUP_FREEZING) || 269 (freezer->state & CGROUP_FROZEN)) 270 goto out_unlock; 271 272 /* are all (live) children frozen? */ 273 css_for_each_child(pos, css) { 274 struct freezer *child = css_freezer(pos); 275 276 if ((child->state & CGROUP_FREEZER_ONLINE) && 277 !(child->state & CGROUP_FROZEN)) 278 goto out_unlock; 279 } 280 281 /* are all tasks frozen? */ 282 css_task_iter_start(css, &it); 283 284 while ((task = css_task_iter_next(&it))) { 285 if (freezing(task)) { 286 /* 287 * freezer_should_skip() indicates that the task 288 * should be skipped when determining freezing 289 * completion. Consider it frozen in addition to 290 * the usual frozen condition. 291 */ 292 if (!frozen(task) && !freezer_should_skip(task)) 293 goto out_iter_end; 294 } 295 } 296 297 freezer->state |= CGROUP_FROZEN; 298out_iter_end: 299 css_task_iter_end(&it); 300out_unlock: 301 spin_unlock_irq(&freezer->lock); 302} 303 304static int freezer_read(struct cgroup_subsys_state *css, struct cftype *cft, 305 struct seq_file *m) 306{ 307 struct cgroup_subsys_state *pos; 308 309 rcu_read_lock(); 310 311 /* update states bottom-up */ 312 css_for_each_descendant_post(pos, css) 313 update_if_frozen(pos); 314 315 rcu_read_unlock(); 316 317 seq_puts(m, freezer_state_strs(css_freezer(css)->state)); 318 seq_putc(m, '\n'); 319 return 0; 320} 321 322static void freeze_cgroup(struct freezer *freezer) 323{ 324 struct css_task_iter it; 325 struct task_struct *task; 326 327 css_task_iter_start(&freezer->css, &it); 328 while ((task = css_task_iter_next(&it))) 329 freeze_task(task); 330 css_task_iter_end(&it); 331} 332 333static void unfreeze_cgroup(struct freezer *freezer) 334{ 335 struct css_task_iter it; 336 struct task_struct *task; 337 338 css_task_iter_start(&freezer->css, &it); 339 while ((task = css_task_iter_next(&it))) 340 __thaw_task(task); 341 css_task_iter_end(&it); 342} 343 344/** 345 * freezer_apply_state - apply state change to a single cgroup_freezer 346 * @freezer: freezer to apply state change to 347 * @freeze: whether to freeze or unfreeze 348 * @state: CGROUP_FREEZING_* flag to set or clear 349 * 350 * Set or clear @state on @cgroup according to @freeze, and perform 351 * freezing or thawing as necessary. 352 */ 353static void freezer_apply_state(struct freezer *freezer, bool freeze, 354 unsigned int state) 355{ 356 /* also synchronizes against task migration, see freezer_attach() */ 357 lockdep_assert_held(&freezer->lock); 358 359 if (!(freezer->state & CGROUP_FREEZER_ONLINE)) 360 return; 361 362 if (freeze) { 363 if (!(freezer->state & CGROUP_FREEZING)) 364 atomic_inc(&system_freezing_cnt); 365 freezer->state |= state; 366 freeze_cgroup(freezer); 367 } else { 368 bool was_freezing = freezer->state & CGROUP_FREEZING; 369 370 freezer->state &= ~state; 371 372 if (!(freezer->state & CGROUP_FREEZING)) { 373 if (was_freezing) 374 atomic_dec(&system_freezing_cnt); 375 freezer->state &= ~CGROUP_FROZEN; 376 unfreeze_cgroup(freezer); 377 } 378 } 379} 380 381/** 382 * freezer_change_state - change the freezing state of a cgroup_freezer 383 * @freezer: freezer of interest 384 * @freeze: whether to freeze or thaw 385 * 386 * Freeze or thaw @freezer according to @freeze. The operations are 387 * recursive - all descendants of @freezer will be affected. 388 */ 389static void freezer_change_state(struct freezer *freezer, bool freeze) 390{ 391 struct cgroup_subsys_state *pos; 392 393 /* 394 * Update all its descendants in pre-order traversal. Each 395 * descendant will try to inherit its parent's FREEZING state as 396 * CGROUP_FREEZING_PARENT. 397 */ 398 rcu_read_lock(); 399 css_for_each_descendant_pre(pos, &freezer->css) { 400 struct freezer *pos_f = css_freezer(pos); 401 struct freezer *parent = parent_freezer(pos_f); 402 403 spin_lock_irq(&pos_f->lock); 404 405 if (pos_f == freezer) { 406 freezer_apply_state(pos_f, freeze, 407 CGROUP_FREEZING_SELF); 408 } else { 409 /* 410 * Our update to @parent->state is already visible 411 * which is all we need. No need to lock @parent. 412 * For more info on synchronization, see 413 * freezer_post_create(). 414 */ 415 freezer_apply_state(pos_f, 416 parent->state & CGROUP_FREEZING, 417 CGROUP_FREEZING_PARENT); 418 } 419 420 spin_unlock_irq(&pos_f->lock); 421 } 422 rcu_read_unlock(); 423} 424 425static int freezer_write(struct cgroup_subsys_state *css, struct cftype *cft, 426 const char *buffer) 427{ 428 bool freeze; 429 430 if (strcmp(buffer, freezer_state_strs(0)) == 0) 431 freeze = false; 432 else if (strcmp(buffer, freezer_state_strs(CGROUP_FROZEN)) == 0) 433 freeze = true; 434 else 435 return -EINVAL; 436 437 freezer_change_state(css_freezer(css), freeze); 438 return 0; 439} 440 441static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css, 442 struct cftype *cft) 443{ 444 struct freezer *freezer = css_freezer(css); 445 446 return (bool)(freezer->state & CGROUP_FREEZING_SELF); 447} 448 449static u64 freezer_parent_freezing_read(struct cgroup_subsys_state *css, 450 struct cftype *cft) 451{ 452 struct freezer *freezer = css_freezer(css); 453 454 return (bool)(freezer->state & CGROUP_FREEZING_PARENT); 455} 456 457static struct cftype files[] = { 458 { 459 .name = "state", 460 .flags = CFTYPE_NOT_ON_ROOT, 461 .read_seq_string = freezer_read, 462 .write_string = freezer_write, 463 }, 464 { 465 .name = "self_freezing", 466 .flags = CFTYPE_NOT_ON_ROOT, 467 .read_u64 = freezer_self_freezing_read, 468 }, 469 { 470 .name = "parent_freezing", 471 .flags = CFTYPE_NOT_ON_ROOT, 472 .read_u64 = freezer_parent_freezing_read, 473 }, 474 { } /* terminate */ 475}; 476 477struct cgroup_subsys freezer_subsys = { 478 .name = "freezer", 479 .css_alloc = freezer_css_alloc, 480 .css_online = freezer_css_online, 481 .css_offline = freezer_css_offline, 482 .css_free = freezer_css_free, 483 .subsys_id = freezer_subsys_id, 484 .attach = freezer_attach, 485 .fork = freezer_fork, 486 .base_cftypes = files, 487};