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