LCOV - code coverage report
Current view: top level - kernel/cgroup - legacy_freezer.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_real_modules_combined.info Lines: 15 112 13.4 %
Date: 2020-09-30 20:25:40 Functions: 3 16 18.8 %
Branches: 5 100 5.0 %

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

Generated by: LCOV version 1.14