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