13d0407baSopenharmony_ci/* 23d0407baSopenharmony_ci * cgroup_freezer.c - control group freezer subsystem 33d0407baSopenharmony_ci * 43d0407baSopenharmony_ci * Copyright IBM Corporation, 2007 53d0407baSopenharmony_ci * 63d0407baSopenharmony_ci * Author : Cedric Le Goater <clg@fr.ibm.com> 73d0407baSopenharmony_ci * 83d0407baSopenharmony_ci * This program is free software; you can redistribute it and/or modify it 93d0407baSopenharmony_ci * under the terms of version 2.1 of the GNU Lesser General Public License 103d0407baSopenharmony_ci * as published by the Free Software Foundation. 113d0407baSopenharmony_ci * 123d0407baSopenharmony_ci * This program is distributed in the hope that it would be useful, but 133d0407baSopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 143d0407baSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 153d0407baSopenharmony_ci */ 163d0407baSopenharmony_ci 173d0407baSopenharmony_ci#include <linux/export.h> 183d0407baSopenharmony_ci#include <linux/slab.h> 193d0407baSopenharmony_ci#include <linux/cgroup.h> 203d0407baSopenharmony_ci#include <linux/fs.h> 213d0407baSopenharmony_ci#include <linux/uaccess.h> 223d0407baSopenharmony_ci#include <linux/freezer.h> 233d0407baSopenharmony_ci#include <linux/seq_file.h> 243d0407baSopenharmony_ci#include <linux/mutex.h> 253d0407baSopenharmony_ci 263d0407baSopenharmony_ci/* 273d0407baSopenharmony_ci * A cgroup is freezing if any FREEZING flags are set. FREEZING_SELF is 283d0407baSopenharmony_ci * set if "FROZEN" is written to freezer.state cgroupfs file, and cleared 293d0407baSopenharmony_ci * for "THAWED". FREEZING_PARENT is set if the parent freezer is FREEZING 303d0407baSopenharmony_ci * for whatever reason. IOW, a cgroup has FREEZING_PARENT set if one of 313d0407baSopenharmony_ci * its ancestors has FREEZING_SELF set. 323d0407baSopenharmony_ci */ 333d0407baSopenharmony_cienum freezer_state_flags { 343d0407baSopenharmony_ci CGROUP_FREEZER_ONLINE = (1 << 0), /* freezer is fully online */ 353d0407baSopenharmony_ci CGROUP_FREEZING_SELF = (1 << 1), /* this freezer is freezing */ 363d0407baSopenharmony_ci CGROUP_FREEZING_PARENT = (1 << 2), /* the parent freezer is freezing */ 373d0407baSopenharmony_ci CGROUP_FROZEN = (1 << 3), /* this and its descendants frozen */ 383d0407baSopenharmony_ci 393d0407baSopenharmony_ci /* mask for all FREEZING flags */ 403d0407baSopenharmony_ci CGROUP_FREEZING = CGROUP_FREEZING_SELF | CGROUP_FREEZING_PARENT, 413d0407baSopenharmony_ci}; 423d0407baSopenharmony_ci 433d0407baSopenharmony_cistruct freezer { 443d0407baSopenharmony_ci struct cgroup_subsys_state css; 453d0407baSopenharmony_ci unsigned int state; 463d0407baSopenharmony_ci}; 473d0407baSopenharmony_ci 483d0407baSopenharmony_cistatic DEFINE_MUTEX(freezer_mutex); 493d0407baSopenharmony_ci 503d0407baSopenharmony_cistatic inline struct freezer *css_freezer(struct cgroup_subsys_state *css) 513d0407baSopenharmony_ci{ 523d0407baSopenharmony_ci return css ? container_of(css, struct freezer, css) : NULL; 533d0407baSopenharmony_ci} 543d0407baSopenharmony_ci 553d0407baSopenharmony_cistatic inline struct freezer *task_freezer(struct task_struct *task) 563d0407baSopenharmony_ci{ 573d0407baSopenharmony_ci return css_freezer(task_css(task, freezer_cgrp_id)); 583d0407baSopenharmony_ci} 593d0407baSopenharmony_ci 603d0407baSopenharmony_cistatic struct freezer *parent_freezer(struct freezer *freezer) 613d0407baSopenharmony_ci{ 623d0407baSopenharmony_ci return css_freezer(freezer->css.parent); 633d0407baSopenharmony_ci} 643d0407baSopenharmony_ci 653d0407baSopenharmony_cibool cgroup_freezing(struct task_struct *task) 663d0407baSopenharmony_ci{ 673d0407baSopenharmony_ci bool ret; 683d0407baSopenharmony_ci 693d0407baSopenharmony_ci rcu_read_lock(); 703d0407baSopenharmony_ci ret = task_freezer(task)->state & CGROUP_FREEZING; 713d0407baSopenharmony_ci rcu_read_unlock(); 723d0407baSopenharmony_ci 733d0407baSopenharmony_ci return ret; 743d0407baSopenharmony_ci} 753d0407baSopenharmony_ci 763d0407baSopenharmony_cistatic const char *freezer_state_strs(unsigned int state) 773d0407baSopenharmony_ci{ 783d0407baSopenharmony_ci if (state & CGROUP_FROZEN) { 793d0407baSopenharmony_ci return "FROZEN"; 803d0407baSopenharmony_ci } 813d0407baSopenharmony_ci if (state & CGROUP_FREEZING) { 823d0407baSopenharmony_ci return "FREEZING"; 833d0407baSopenharmony_ci } 843d0407baSopenharmony_ci return "THAWED"; 853d0407baSopenharmony_ci}; 863d0407baSopenharmony_ci 873d0407baSopenharmony_cistatic struct cgroup_subsys_state *freezer_css_alloc(struct cgroup_subsys_state *parent_css) 883d0407baSopenharmony_ci{ 893d0407baSopenharmony_ci struct freezer *freezer; 903d0407baSopenharmony_ci 913d0407baSopenharmony_ci freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL); 923d0407baSopenharmony_ci if (!freezer) { 933d0407baSopenharmony_ci return ERR_PTR(-ENOMEM); 943d0407baSopenharmony_ci } 953d0407baSopenharmony_ci 963d0407baSopenharmony_ci return &freezer->css; 973d0407baSopenharmony_ci} 983d0407baSopenharmony_ci 993d0407baSopenharmony_ci/** 1003d0407baSopenharmony_ci * freezer_css_online - commit creation of a freezer css 1013d0407baSopenharmony_ci * @css: css being created 1023d0407baSopenharmony_ci * 1033d0407baSopenharmony_ci * We're committing to creation of @css. Mark it online and inherit 1043d0407baSopenharmony_ci * parent's freezing state while holding both parent's and our 1053d0407baSopenharmony_ci * freezer->lock. 1063d0407baSopenharmony_ci */ 1073d0407baSopenharmony_cistatic int freezer_css_online(struct cgroup_subsys_state *css) 1083d0407baSopenharmony_ci{ 1093d0407baSopenharmony_ci struct freezer *freezer = css_freezer(css); 1103d0407baSopenharmony_ci struct freezer *parent = parent_freezer(freezer); 1113d0407baSopenharmony_ci 1123d0407baSopenharmony_ci mutex_lock(&freezer_mutex); 1133d0407baSopenharmony_ci 1143d0407baSopenharmony_ci freezer->state |= CGROUP_FREEZER_ONLINE; 1153d0407baSopenharmony_ci 1163d0407baSopenharmony_ci if (parent && (parent->state & CGROUP_FREEZING)) { 1173d0407baSopenharmony_ci freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN; 1183d0407baSopenharmony_ci atomic_inc(&system_freezing_cnt); 1193d0407baSopenharmony_ci } 1203d0407baSopenharmony_ci 1213d0407baSopenharmony_ci mutex_unlock(&freezer_mutex); 1223d0407baSopenharmony_ci return 0; 1233d0407baSopenharmony_ci} 1243d0407baSopenharmony_ci 1253d0407baSopenharmony_ci/** 1263d0407baSopenharmony_ci * freezer_css_offline - initiate destruction of a freezer css 1273d0407baSopenharmony_ci * @css: css being destroyed 1283d0407baSopenharmony_ci * 1293d0407baSopenharmony_ci * @css is going away. Mark it dead and decrement system_freezing_count if 1303d0407baSopenharmony_ci * it was holding one. 1313d0407baSopenharmony_ci */ 1323d0407baSopenharmony_cistatic void freezer_css_offline(struct cgroup_subsys_state *css) 1333d0407baSopenharmony_ci{ 1343d0407baSopenharmony_ci struct freezer *freezer = css_freezer(css); 1353d0407baSopenharmony_ci 1363d0407baSopenharmony_ci mutex_lock(&freezer_mutex); 1373d0407baSopenharmony_ci 1383d0407baSopenharmony_ci if (freezer->state & CGROUP_FREEZING) { 1393d0407baSopenharmony_ci atomic_dec(&system_freezing_cnt); 1403d0407baSopenharmony_ci } 1413d0407baSopenharmony_ci 1423d0407baSopenharmony_ci freezer->state = 0; 1433d0407baSopenharmony_ci 1443d0407baSopenharmony_ci mutex_unlock(&freezer_mutex); 1453d0407baSopenharmony_ci} 1463d0407baSopenharmony_ci 1473d0407baSopenharmony_cistatic void freezer_css_free(struct cgroup_subsys_state *css) 1483d0407baSopenharmony_ci{ 1493d0407baSopenharmony_ci kfree(css_freezer(css)); 1503d0407baSopenharmony_ci} 1513d0407baSopenharmony_ci 1523d0407baSopenharmony_ci/* 1533d0407baSopenharmony_ci * Tasks can be migrated into a different freezer anytime regardless of its 1543d0407baSopenharmony_ci * current state. freezer_attach() is responsible for making new tasks 1553d0407baSopenharmony_ci * conform to the current state. 1563d0407baSopenharmony_ci * 1573d0407baSopenharmony_ci * Freezer state changes and task migration are synchronized via 1583d0407baSopenharmony_ci * @freezer->lock. freezer_attach() makes the new tasks conform to the 1593d0407baSopenharmony_ci * current state and all following state changes can see the new tasks. 1603d0407baSopenharmony_ci */ 1613d0407baSopenharmony_cistatic void freezer_attach(struct cgroup_taskset *tset) 1623d0407baSopenharmony_ci{ 1633d0407baSopenharmony_ci struct task_struct *task; 1643d0407baSopenharmony_ci struct cgroup_subsys_state *new_css; 1653d0407baSopenharmony_ci 1663d0407baSopenharmony_ci mutex_lock(&freezer_mutex); 1673d0407baSopenharmony_ci 1683d0407baSopenharmony_ci /* 1693d0407baSopenharmony_ci * Make the new tasks conform to the current state of @new_css. 1703d0407baSopenharmony_ci * For simplicity, when migrating any task to a FROZEN cgroup, we 1713d0407baSopenharmony_ci * revert it to FREEZING and let update_if_frozen() determine the 1723d0407baSopenharmony_ci * correct state later. 1733d0407baSopenharmony_ci * 1743d0407baSopenharmony_ci * Tasks in @tset are on @new_css but may not conform to its 1753d0407baSopenharmony_ci * current state before executing the following - !frozen tasks may 1763d0407baSopenharmony_ci * be visible in a FROZEN cgroup and frozen tasks in a THAWED one. 1773d0407baSopenharmony_ci */ 1783d0407baSopenharmony_ci cgroup_taskset_for_each(task, new_css, tset) 1793d0407baSopenharmony_ci { 1803d0407baSopenharmony_ci struct freezer *freezer = css_freezer(new_css); 1813d0407baSopenharmony_ci 1823d0407baSopenharmony_ci if (!(freezer->state & CGROUP_FREEZING)) { 1833d0407baSopenharmony_ci __thaw_task(task); 1843d0407baSopenharmony_ci } else { 1853d0407baSopenharmony_ci freeze_task(task); 1863d0407baSopenharmony_ci /* clear FROZEN and propagate upwards */ 1873d0407baSopenharmony_ci while (freezer && (freezer->state & CGROUP_FROZEN)) { 1883d0407baSopenharmony_ci freezer->state &= ~CGROUP_FROZEN; 1893d0407baSopenharmony_ci freezer = parent_freezer(freezer); 1903d0407baSopenharmony_ci } 1913d0407baSopenharmony_ci } 1923d0407baSopenharmony_ci } 1933d0407baSopenharmony_ci 1943d0407baSopenharmony_ci mutex_unlock(&freezer_mutex); 1953d0407baSopenharmony_ci} 1963d0407baSopenharmony_ci 1973d0407baSopenharmony_ci/** 1983d0407baSopenharmony_ci * freezer_fork - cgroup post fork callback 1993d0407baSopenharmony_ci * @task: a task which has just been forked 2003d0407baSopenharmony_ci * 2013d0407baSopenharmony_ci * @task has just been created and should conform to the current state of 2023d0407baSopenharmony_ci * the cgroup_freezer it belongs to. This function may race against 2033d0407baSopenharmony_ci * freezer_attach(). Losing to freezer_attach() means that we don't have 2043d0407baSopenharmony_ci * to do anything as freezer_attach() will put @task into the appropriate 2053d0407baSopenharmony_ci * state. 2063d0407baSopenharmony_ci */ 2073d0407baSopenharmony_cistatic void freezer_fork(struct task_struct *task) 2083d0407baSopenharmony_ci{ 2093d0407baSopenharmony_ci struct freezer *freezer; 2103d0407baSopenharmony_ci 2113d0407baSopenharmony_ci /* 2123d0407baSopenharmony_ci * The root cgroup is non-freezable, so we can skip locking the 2133d0407baSopenharmony_ci * freezer. This is safe regardless of race with task migration. 2143d0407baSopenharmony_ci * If we didn't race or won, skipping is obviously the right thing 2153d0407baSopenharmony_ci * to do. If we lost and root is the new cgroup, noop is still the 2163d0407baSopenharmony_ci * right thing to do. 2173d0407baSopenharmony_ci */ 2183d0407baSopenharmony_ci if (task_css_is_root(task, freezer_cgrp_id)) { 2193d0407baSopenharmony_ci return; 2203d0407baSopenharmony_ci } 2213d0407baSopenharmony_ci 2223d0407baSopenharmony_ci mutex_lock(&freezer_mutex); 2233d0407baSopenharmony_ci rcu_read_lock(); 2243d0407baSopenharmony_ci 2253d0407baSopenharmony_ci freezer = task_freezer(task); 2263d0407baSopenharmony_ci if (freezer->state & CGROUP_FREEZING) { 2273d0407baSopenharmony_ci freeze_task(task); 2283d0407baSopenharmony_ci } 2293d0407baSopenharmony_ci 2303d0407baSopenharmony_ci rcu_read_unlock(); 2313d0407baSopenharmony_ci mutex_unlock(&freezer_mutex); 2323d0407baSopenharmony_ci} 2333d0407baSopenharmony_ci 2343d0407baSopenharmony_ci/** 2353d0407baSopenharmony_ci * update_if_frozen - update whether a cgroup finished freezing 2363d0407baSopenharmony_ci * @css: css of interest 2373d0407baSopenharmony_ci * 2383d0407baSopenharmony_ci * Once FREEZING is initiated, transition to FROZEN is lazily updated by 2393d0407baSopenharmony_ci * calling this function. If the current state is FREEZING but not FROZEN, 2403d0407baSopenharmony_ci * this function checks whether all tasks of this cgroup and the descendant 2413d0407baSopenharmony_ci * cgroups finished freezing and, if so, sets FROZEN. 2423d0407baSopenharmony_ci * 2433d0407baSopenharmony_ci * The caller is responsible for grabbing RCU read lock and calling 2443d0407baSopenharmony_ci * update_if_frozen() on all descendants prior to invoking this function. 2453d0407baSopenharmony_ci * 2463d0407baSopenharmony_ci * Task states and freezer state might disagree while tasks are being 2473d0407baSopenharmony_ci * migrated into or out of @css, so we can't verify task states against 2483d0407baSopenharmony_ci * @freezer state here. See freezer_attach() for details. 2493d0407baSopenharmony_ci */ 2503d0407baSopenharmony_cistatic void update_if_frozen(struct cgroup_subsys_state *css) 2513d0407baSopenharmony_ci{ 2523d0407baSopenharmony_ci struct freezer *freezer = css_freezer(css); 2533d0407baSopenharmony_ci struct cgroup_subsys_state *pos; 2543d0407baSopenharmony_ci struct css_task_iter it; 2553d0407baSopenharmony_ci struct task_struct *task; 2563d0407baSopenharmony_ci 2573d0407baSopenharmony_ci lockdep_assert_held(&freezer_mutex); 2583d0407baSopenharmony_ci 2593d0407baSopenharmony_ci if (!(freezer->state & CGROUP_FREEZING) || (freezer->state & CGROUP_FROZEN)) { 2603d0407baSopenharmony_ci return; 2613d0407baSopenharmony_ci } 2623d0407baSopenharmony_ci 2633d0407baSopenharmony_ci /* are all (live) children frozen? */ 2643d0407baSopenharmony_ci rcu_read_lock(); 2653d0407baSopenharmony_ci css_for_each_child(pos, css) 2663d0407baSopenharmony_ci { 2673d0407baSopenharmony_ci struct freezer *child = css_freezer(pos); 2683d0407baSopenharmony_ci 2693d0407baSopenharmony_ci if ((child->state & CGROUP_FREEZER_ONLINE) && !(child->state & CGROUP_FROZEN)) { 2703d0407baSopenharmony_ci rcu_read_unlock(); 2713d0407baSopenharmony_ci return; 2723d0407baSopenharmony_ci } 2733d0407baSopenharmony_ci } 2743d0407baSopenharmony_ci rcu_read_unlock(); 2753d0407baSopenharmony_ci 2763d0407baSopenharmony_ci /* are all tasks frozen? */ 2773d0407baSopenharmony_ci css_task_iter_start(css, 0, &it); 2783d0407baSopenharmony_ci 2793d0407baSopenharmony_ci while ((task = css_task_iter_next(&it))) { 2803d0407baSopenharmony_ci if (freezing(task)) { 2813d0407baSopenharmony_ci /* 2823d0407baSopenharmony_ci * freezer_should_skip() indicates that the task 2833d0407baSopenharmony_ci * should be skipped when determining freezing 2843d0407baSopenharmony_ci * completion. Consider it frozen in addition to 2853d0407baSopenharmony_ci * the usual frozen condition. 2863d0407baSopenharmony_ci */ 2873d0407baSopenharmony_ci if (!frozen(task) && !freezer_should_skip(task)) { 2883d0407baSopenharmony_ci goto out_iter_end; 2893d0407baSopenharmony_ci } 2903d0407baSopenharmony_ci } 2913d0407baSopenharmony_ci } 2923d0407baSopenharmony_ci 2933d0407baSopenharmony_ci freezer->state |= CGROUP_FROZEN; 2943d0407baSopenharmony_ciout_iter_end: 2953d0407baSopenharmony_ci css_task_iter_end(&it); 2963d0407baSopenharmony_ci} 2973d0407baSopenharmony_ci 2983d0407baSopenharmony_cistatic int freezer_read(struct seq_file *m, void *v) 2993d0407baSopenharmony_ci{ 3003d0407baSopenharmony_ci struct cgroup_subsys_state *css = seq_css(m), *pos; 3013d0407baSopenharmony_ci 3023d0407baSopenharmony_ci mutex_lock(&freezer_mutex); 3033d0407baSopenharmony_ci rcu_read_lock(); 3043d0407baSopenharmony_ci 3053d0407baSopenharmony_ci /* update states bottom-up */ 3063d0407baSopenharmony_ci css_for_each_descendant_post(pos, css) 3073d0407baSopenharmony_ci { 3083d0407baSopenharmony_ci if (!css_tryget_online(pos)) { 3093d0407baSopenharmony_ci continue; 3103d0407baSopenharmony_ci } 3113d0407baSopenharmony_ci rcu_read_unlock(); 3123d0407baSopenharmony_ci 3133d0407baSopenharmony_ci update_if_frozen(pos); 3143d0407baSopenharmony_ci 3153d0407baSopenharmony_ci rcu_read_lock(); 3163d0407baSopenharmony_ci css_put(pos); 3173d0407baSopenharmony_ci } 3183d0407baSopenharmony_ci 3193d0407baSopenharmony_ci rcu_read_unlock(); 3203d0407baSopenharmony_ci mutex_unlock(&freezer_mutex); 3213d0407baSopenharmony_ci 3223d0407baSopenharmony_ci seq_puts(m, freezer_state_strs(css_freezer(css)->state)); 3233d0407baSopenharmony_ci seq_putc(m, '\n'); 3243d0407baSopenharmony_ci return 0; 3253d0407baSopenharmony_ci} 3263d0407baSopenharmony_ci 3273d0407baSopenharmony_cistatic void freeze_cgroup(struct freezer *freezer) 3283d0407baSopenharmony_ci{ 3293d0407baSopenharmony_ci struct css_task_iter it; 3303d0407baSopenharmony_ci struct task_struct *task; 3313d0407baSopenharmony_ci 3323d0407baSopenharmony_ci css_task_iter_start(&freezer->css, 0, &it); 3333d0407baSopenharmony_ci while ((task = css_task_iter_next(&it))) { 3343d0407baSopenharmony_ci freeze_task(task); 3353d0407baSopenharmony_ci } 3363d0407baSopenharmony_ci css_task_iter_end(&it); 3373d0407baSopenharmony_ci} 3383d0407baSopenharmony_ci 3393d0407baSopenharmony_cistatic void unfreeze_cgroup(struct freezer *freezer) 3403d0407baSopenharmony_ci{ 3413d0407baSopenharmony_ci struct css_task_iter it; 3423d0407baSopenharmony_ci struct task_struct *task; 3433d0407baSopenharmony_ci 3443d0407baSopenharmony_ci css_task_iter_start(&freezer->css, 0, &it); 3453d0407baSopenharmony_ci while ((task = css_task_iter_next(&it))) { 3463d0407baSopenharmony_ci __thaw_task(task); 3473d0407baSopenharmony_ci } 3483d0407baSopenharmony_ci css_task_iter_end(&it); 3493d0407baSopenharmony_ci} 3503d0407baSopenharmony_ci 3513d0407baSopenharmony_ci/** 3523d0407baSopenharmony_ci * freezer_apply_state - apply state change to a single cgroup_freezer 3533d0407baSopenharmony_ci * @freezer: freezer to apply state change to 3543d0407baSopenharmony_ci * @freeze: whether to freeze or unfreeze 3553d0407baSopenharmony_ci * @state: CGROUP_FREEZING_* flag to set or clear 3563d0407baSopenharmony_ci * 3573d0407baSopenharmony_ci * Set or clear @state on @cgroup according to @freeze, and perform 3583d0407baSopenharmony_ci * freezing or thawing as necessary. 3593d0407baSopenharmony_ci */ 3603d0407baSopenharmony_cistatic void freezer_apply_state(struct freezer *freezer, bool freeze, unsigned int state) 3613d0407baSopenharmony_ci{ 3623d0407baSopenharmony_ci /* also synchronizes against task migration, see freezer_attach() */ 3633d0407baSopenharmony_ci lockdep_assert_held(&freezer_mutex); 3643d0407baSopenharmony_ci 3653d0407baSopenharmony_ci if (!(freezer->state & CGROUP_FREEZER_ONLINE)) { 3663d0407baSopenharmony_ci return; 3673d0407baSopenharmony_ci } 3683d0407baSopenharmony_ci 3693d0407baSopenharmony_ci if (freeze) { 3703d0407baSopenharmony_ci if (!(freezer->state & CGROUP_FREEZING)) { 3713d0407baSopenharmony_ci atomic_inc(&system_freezing_cnt); 3723d0407baSopenharmony_ci } 3733d0407baSopenharmony_ci freezer->state |= state; 3743d0407baSopenharmony_ci freeze_cgroup(freezer); 3753d0407baSopenharmony_ci } else { 3763d0407baSopenharmony_ci bool was_freezing = freezer->state & CGROUP_FREEZING; 3773d0407baSopenharmony_ci 3783d0407baSopenharmony_ci freezer->state &= ~state; 3793d0407baSopenharmony_ci 3803d0407baSopenharmony_ci if (!(freezer->state & CGROUP_FREEZING)) { 3813d0407baSopenharmony_ci if (was_freezing) { 3823d0407baSopenharmony_ci atomic_dec(&system_freezing_cnt); 3833d0407baSopenharmony_ci } 3843d0407baSopenharmony_ci freezer->state &= ~CGROUP_FROZEN; 3853d0407baSopenharmony_ci unfreeze_cgroup(freezer); 3863d0407baSopenharmony_ci } 3873d0407baSopenharmony_ci } 3883d0407baSopenharmony_ci} 3893d0407baSopenharmony_ci 3903d0407baSopenharmony_ci/** 3913d0407baSopenharmony_ci * freezer_change_state - change the freezing state of a cgroup_freezer 3923d0407baSopenharmony_ci * @freezer: freezer of interest 3933d0407baSopenharmony_ci * @freeze: whether to freeze or thaw 3943d0407baSopenharmony_ci * 3953d0407baSopenharmony_ci * Freeze or thaw @freezer according to @freeze. The operations are 3963d0407baSopenharmony_ci * recursive - all descendants of @freezer will be affected. 3973d0407baSopenharmony_ci */ 3983d0407baSopenharmony_cistatic void freezer_change_state(struct freezer *freezer, bool freeze) 3993d0407baSopenharmony_ci{ 4003d0407baSopenharmony_ci struct cgroup_subsys_state *pos; 4013d0407baSopenharmony_ci 4023d0407baSopenharmony_ci /* 4033d0407baSopenharmony_ci * Update all its descendants in pre-order traversal. Each 4043d0407baSopenharmony_ci * descendant will try to inherit its parent's FREEZING state as 4053d0407baSopenharmony_ci * CGROUP_FREEZING_PARENT. 4063d0407baSopenharmony_ci */ 4073d0407baSopenharmony_ci mutex_lock(&freezer_mutex); 4083d0407baSopenharmony_ci rcu_read_lock(); 4093d0407baSopenharmony_ci css_for_each_descendant_pre(pos, &freezer->css) 4103d0407baSopenharmony_ci { 4113d0407baSopenharmony_ci struct freezer *pos_f = css_freezer(pos); 4123d0407baSopenharmony_ci struct freezer *parent = parent_freezer(pos_f); 4133d0407baSopenharmony_ci 4143d0407baSopenharmony_ci if (!css_tryget_online(pos)) { 4153d0407baSopenharmony_ci continue; 4163d0407baSopenharmony_ci } 4173d0407baSopenharmony_ci rcu_read_unlock(); 4183d0407baSopenharmony_ci 4193d0407baSopenharmony_ci if (pos_f == freezer) { 4203d0407baSopenharmony_ci freezer_apply_state(pos_f, freeze, CGROUP_FREEZING_SELF); 4213d0407baSopenharmony_ci } else { 4223d0407baSopenharmony_ci freezer_apply_state(pos_f, parent->state & CGROUP_FREEZING, CGROUP_FREEZING_PARENT); 4233d0407baSopenharmony_ci } 4243d0407baSopenharmony_ci 4253d0407baSopenharmony_ci rcu_read_lock(); 4263d0407baSopenharmony_ci css_put(pos); 4273d0407baSopenharmony_ci } 4283d0407baSopenharmony_ci rcu_read_unlock(); 4293d0407baSopenharmony_ci mutex_unlock(&freezer_mutex); 4303d0407baSopenharmony_ci} 4313d0407baSopenharmony_ci 4323d0407baSopenharmony_cistatic ssize_t freezer_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) 4333d0407baSopenharmony_ci{ 4343d0407baSopenharmony_ci bool freeze; 4353d0407baSopenharmony_ci 4363d0407baSopenharmony_ci buf = strstrip(buf); 4373d0407baSopenharmony_ci if (strcmp(buf, freezer_state_strs(0)) == 0) { 4383d0407baSopenharmony_ci freeze = false; 4393d0407baSopenharmony_ci } else if (strcmp(buf, freezer_state_strs(CGROUP_FROZEN)) == 0) { 4403d0407baSopenharmony_ci freeze = true; 4413d0407baSopenharmony_ci } else { 4423d0407baSopenharmony_ci return -EINVAL; 4433d0407baSopenharmony_ci } 4443d0407baSopenharmony_ci 4453d0407baSopenharmony_ci freezer_change_state(css_freezer(of_css(of)), freeze); 4463d0407baSopenharmony_ci return nbytes; 4473d0407baSopenharmony_ci} 4483d0407baSopenharmony_ci 4493d0407baSopenharmony_cistatic u64 freezer_self_freezing_read(struct cgroup_subsys_state *css, struct cftype *cft) 4503d0407baSopenharmony_ci{ 4513d0407baSopenharmony_ci struct freezer *freezer = css_freezer(css); 4523d0407baSopenharmony_ci 4533d0407baSopenharmony_ci return (bool)(freezer->state & CGROUP_FREEZING_SELF); 4543d0407baSopenharmony_ci} 4553d0407baSopenharmony_ci 4563d0407baSopenharmony_cistatic u64 freezer_parent_freezing_read(struct cgroup_subsys_state *css, struct cftype *cft) 4573d0407baSopenharmony_ci{ 4583d0407baSopenharmony_ci struct freezer *freezer = css_freezer(css); 4593d0407baSopenharmony_ci 4603d0407baSopenharmony_ci return (bool)(freezer->state & CGROUP_FREEZING_PARENT); 4613d0407baSopenharmony_ci} 4623d0407baSopenharmony_ci 4633d0407baSopenharmony_cistatic struct cftype files[] = { 4643d0407baSopenharmony_ci { 4653d0407baSopenharmony_ci .name = "state", 4663d0407baSopenharmony_ci .flags = CFTYPE_NOT_ON_ROOT, 4673d0407baSopenharmony_ci .seq_show = freezer_read, 4683d0407baSopenharmony_ci .write = freezer_write, 4693d0407baSopenharmony_ci }, 4703d0407baSopenharmony_ci { 4713d0407baSopenharmony_ci .name = "self_freezing", 4723d0407baSopenharmony_ci .flags = CFTYPE_NOT_ON_ROOT, 4733d0407baSopenharmony_ci .read_u64 = freezer_self_freezing_read, 4743d0407baSopenharmony_ci }, 4753d0407baSopenharmony_ci { 4763d0407baSopenharmony_ci .name = "parent_freezing", 4773d0407baSopenharmony_ci .flags = CFTYPE_NOT_ON_ROOT, 4783d0407baSopenharmony_ci .read_u64 = freezer_parent_freezing_read, 4793d0407baSopenharmony_ci }, 4803d0407baSopenharmony_ci {} /* terminate */ 4813d0407baSopenharmony_ci}; 4823d0407baSopenharmony_ci 4833d0407baSopenharmony_cistruct cgroup_subsys freezer_cgrp_subsys = { 4843d0407baSopenharmony_ci .css_alloc = freezer_css_alloc, 4853d0407baSopenharmony_ci .css_online = freezer_css_online, 4863d0407baSopenharmony_ci .css_offline = freezer_css_offline, 4873d0407baSopenharmony_ci .css_free = freezer_css_free, 4883d0407baSopenharmony_ci .attach = freezer_attach, 4893d0407baSopenharmony_ci .fork = freezer_fork, 4903d0407baSopenharmony_ci .legacy_cftypes = files, 4913d0407baSopenharmony_ci}; 4923d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(freezer_cgrp_subsys); 493