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