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