Blame view

kernel/cgroup_freezer.c 13.6 KB
dc52ddc0e   Matt Helsley   container freezer...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * cgroup_freezer.c -  control group freezer subsystem
   *
   * Copyright IBM Corporation, 2007
   *
   * Author : Cedric Le Goater <clg@fr.ibm.com>
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of version 2.1 of the GNU Lesser General Public License
   * as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it would be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   */
9984de1a5   Paul Gortmaker   kernel: Map most ...
16
  #include <linux/export.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
17
  #include <linux/slab.h>
dc52ddc0e   Matt Helsley   container freezer...
18
19
20
21
22
  #include <linux/cgroup.h>
  #include <linux/fs.h>
  #include <linux/uaccess.h>
  #include <linux/freezer.h>
  #include <linux/seq_file.h>
ef9fe980c   Tejun Heo   cgroup_freezer: i...
23
24
25
26
27
28
29
  /*
   * A cgroup is freezing if any FREEZING flags are set.  FREEZING_SELF is
   * set if "FROZEN" is written to freezer.state cgroupfs file, and cleared
   * for "THAWED".  FREEZING_PARENT is set if the parent freezer is FREEZING
   * for whatever reason.  IOW, a cgroup has FREEZING_PARENT set if one of
   * its ancestors has FREEZING_SELF set.
   */
d6a2fe134   Tejun Heo   cgroup_freezer: m...
30
  enum freezer_state_flags {
5300a9b34   Tejun Heo   cgroup_freezer: a...
31
  	CGROUP_FREEZER_ONLINE	= (1 << 0), /* freezer is fully online */
a22521806   Tejun Heo   cgroup_freezer: i...
32
33
  	CGROUP_FREEZING_SELF	= (1 << 1), /* this freezer is freezing */
  	CGROUP_FREEZING_PARENT	= (1 << 2), /* the parent freezer is freezing */
d6a2fe134   Tejun Heo   cgroup_freezer: m...
34
  	CGROUP_FROZEN		= (1 << 3), /* this and its descendants frozen */
a22521806   Tejun Heo   cgroup_freezer: i...
35
36
37
  
  	/* mask for all FREEZING flags */
  	CGROUP_FREEZING		= CGROUP_FREEZING_SELF | CGROUP_FREEZING_PARENT,
dc52ddc0e   Matt Helsley   container freezer...
38
39
40
  };
  
  struct freezer {
bcd66c894   Tejun Heo   cgroup_freezer: t...
41
  	struct cgroup_subsys_state	css;
d6a2fe134   Tejun Heo   cgroup_freezer: m...
42
  	unsigned int			state;
bcd66c894   Tejun Heo   cgroup_freezer: t...
43
  	spinlock_t			lock;
dc52ddc0e   Matt Helsley   container freezer...
44
  };
a7c6d554a   Tejun Heo   cgroup: add/updat...
45
46
47
48
  static inline struct freezer *css_freezer(struct cgroup_subsys_state *css)
  {
  	return css ? container_of(css, struct freezer, css) : NULL;
  }
dc52ddc0e   Matt Helsley   container freezer...
49
50
  static inline struct freezer *task_freezer(struct task_struct *task)
  {
073219e99   Tejun Heo   cgroup: clean up ...
51
  	return css_freezer(task_css(task, freezer_cgrp_id));
dc52ddc0e   Matt Helsley   container freezer...
52
  }
ef9fe980c   Tejun Heo   cgroup_freezer: i...
53
54
  static struct freezer *parent_freezer(struct freezer *freezer)
  {
638769869   Tejun Heo   cgroup: add css_p...
55
  	return css_freezer(css_parent(&freezer->css));
ef9fe980c   Tejun Heo   cgroup_freezer: i...
56
  }
22b4e111f   Tejun Heo   cgroup_freezer: p...
57
  bool cgroup_freezing(struct task_struct *task)
dc52ddc0e   Matt Helsley   container freezer...
58
  {
22b4e111f   Tejun Heo   cgroup_freezer: p...
59
  	bool ret;
dc52ddc0e   Matt Helsley   container freezer...
60

22b4e111f   Tejun Heo   cgroup_freezer: p...
61
  	rcu_read_lock();
d6a2fe134   Tejun Heo   cgroup_freezer: m...
62
  	ret = task_freezer(task)->state & CGROUP_FREEZING;
22b4e111f   Tejun Heo   cgroup_freezer: p...
63
64
65
  	rcu_read_unlock();
  
  	return ret;
dc52ddc0e   Matt Helsley   container freezer...
66
67
68
69
70
71
  }
  
  /*
   * cgroups_write_string() limits the size of freezer state strings to
   * CGROUP_LOCAL_BUFFER_SIZE
   */
d6a2fe134   Tejun Heo   cgroup_freezer: m...
72
73
74
75
76
77
78
  static const char *freezer_state_strs(unsigned int state)
  {
  	if (state & CGROUP_FROZEN)
  		return "FROZEN";
  	if (state & CGROUP_FREEZING)
  		return "FREEZING";
  	return "THAWED";
dc52ddc0e   Matt Helsley   container freezer...
79
  };
eb95419b0   Tejun Heo   cgroup: pass arou...
80
81
  static struct cgroup_subsys_state *
  freezer_css_alloc(struct cgroup_subsys_state *parent_css)
dc52ddc0e   Matt Helsley   container freezer...
82
83
84
85
86
87
88
89
  {
  	struct freezer *freezer;
  
  	freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
  	if (!freezer)
  		return ERR_PTR(-ENOMEM);
  
  	spin_lock_init(&freezer->lock);
dc52ddc0e   Matt Helsley   container freezer...
90
91
  	return &freezer->css;
  }
5300a9b34   Tejun Heo   cgroup_freezer: a...
92
  /**
eb95419b0   Tejun Heo   cgroup: pass arou...
93
94
   * freezer_css_online - commit creation of a freezer css
   * @css: css being created
5300a9b34   Tejun Heo   cgroup_freezer: a...
95
   *
eb95419b0   Tejun Heo   cgroup: pass arou...
96
   * We're committing to creation of @css.  Mark it online and inherit
ef9fe980c   Tejun Heo   cgroup_freezer: i...
97
98
   * parent's freezing state while holding both parent's and our
   * freezer->lock.
5300a9b34   Tejun Heo   cgroup_freezer: a...
99
   */
eb95419b0   Tejun Heo   cgroup: pass arou...
100
  static int freezer_css_online(struct cgroup_subsys_state *css)
dc52ddc0e   Matt Helsley   container freezer...
101
  {
eb95419b0   Tejun Heo   cgroup: pass arou...
102
  	struct freezer *freezer = css_freezer(css);
ef9fe980c   Tejun Heo   cgroup_freezer: i...
103
104
105
106
107
  	struct freezer *parent = parent_freezer(freezer);
  
  	/*
  	 * The following double locking and freezing state inheritance
  	 * guarantee that @cgroup can never escape ancestors' freezing
492eb21b9   Tejun Heo   cgroup: make hier...
108
  	 * states.  See css_for_each_descendant_pre() for details.
ef9fe980c   Tejun Heo   cgroup_freezer: i...
109
110
111
112
  	 */
  	if (parent)
  		spin_lock_irq(&parent->lock);
  	spin_lock_nested(&freezer->lock, SINGLE_DEPTH_NESTING);
a3201227f   Tejun Heo   freezer: make fre...
113

5300a9b34   Tejun Heo   cgroup_freezer: a...
114
  	freezer->state |= CGROUP_FREEZER_ONLINE;
ef9fe980c   Tejun Heo   cgroup_freezer: i...
115
116
117
118
119
120
121
122
123
  
  	if (parent && (parent->state & CGROUP_FREEZING)) {
  		freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN;
  		atomic_inc(&system_freezing_cnt);
  	}
  
  	spin_unlock(&freezer->lock);
  	if (parent)
  		spin_unlock_irq(&parent->lock);
b1929db42   Tejun Heo   cgroup: allow ->p...
124
125
  
  	return 0;
5300a9b34   Tejun Heo   cgroup_freezer: a...
126
127
128
  }
  
  /**
eb95419b0   Tejun Heo   cgroup: pass arou...
129
130
   * freezer_css_offline - initiate destruction of a freezer css
   * @css: css being destroyed
5300a9b34   Tejun Heo   cgroup_freezer: a...
131
   *
eb95419b0   Tejun Heo   cgroup: pass arou...
132
133
   * @css is going away.  Mark it dead and decrement system_freezing_count if
   * it was holding one.
5300a9b34   Tejun Heo   cgroup_freezer: a...
134
   */
eb95419b0   Tejun Heo   cgroup: pass arou...
135
  static void freezer_css_offline(struct cgroup_subsys_state *css)
5300a9b34   Tejun Heo   cgroup_freezer: a...
136
  {
eb95419b0   Tejun Heo   cgroup: pass arou...
137
  	struct freezer *freezer = css_freezer(css);
5300a9b34   Tejun Heo   cgroup_freezer: a...
138
139
  
  	spin_lock_irq(&freezer->lock);
d6a2fe134   Tejun Heo   cgroup_freezer: m...
140
  	if (freezer->state & CGROUP_FREEZING)
a3201227f   Tejun Heo   freezer: make fre...
141
  		atomic_dec(&system_freezing_cnt);
5300a9b34   Tejun Heo   cgroup_freezer: a...
142
143
144
145
146
  
  	freezer->state = 0;
  
  	spin_unlock_irq(&freezer->lock);
  }
eb95419b0   Tejun Heo   cgroup: pass arou...
147
  static void freezer_css_free(struct cgroup_subsys_state *css)
5300a9b34   Tejun Heo   cgroup_freezer: a...
148
  {
eb95419b0   Tejun Heo   cgroup: pass arou...
149
  	kfree(css_freezer(css));
dc52ddc0e   Matt Helsley   container freezer...
150
  }
957a4eeaf   Matt Helsley   container freezer...
151
  /*
ead5c4737   Tejun Heo   cgroup_freezer: d...
152
153
154
155
156
157
158
   * Tasks can be migrated into a different freezer anytime regardless of its
   * current state.  freezer_attach() is responsible for making new tasks
   * conform to the current state.
   *
   * Freezer state changes and task migration are synchronized via
   * @freezer->lock.  freezer_attach() makes the new tasks conform to the
   * current state and all following state changes can see the new tasks.
957a4eeaf   Matt Helsley   container freezer...
159
   */
eb95419b0   Tejun Heo   cgroup: pass arou...
160
161
  static void freezer_attach(struct cgroup_subsys_state *new_css,
  			   struct cgroup_taskset *tset)
dc52ddc0e   Matt Helsley   container freezer...
162
  {
eb95419b0   Tejun Heo   cgroup: pass arou...
163
  	struct freezer *freezer = css_freezer(new_css);
bb9d97b6d   Tejun Heo   cgroup: don't use...
164
  	struct task_struct *task;
ef9fe980c   Tejun Heo   cgroup_freezer: i...
165
  	bool clear_frozen = false;
957a4eeaf   Matt Helsley   container freezer...
166

8755ade68   Tejun Heo   cgroup_freezer: a...
167
  	spin_lock_irq(&freezer->lock);
80a6a2cf3   Li Zefan   freezer_cg: remov...
168
  	/*
eb95419b0   Tejun Heo   cgroup: pass arou...
169
  	 * Make the new tasks conform to the current state of @new_css.
8755ade68   Tejun Heo   cgroup_freezer: a...
170
171
172
173
  	 * For simplicity, when migrating any task to a FROZEN cgroup, we
  	 * revert it to FREEZING and let update_if_frozen() determine the
  	 * correct state later.
  	 *
eb95419b0   Tejun Heo   cgroup: pass arou...
174
  	 * Tasks in @tset are on @new_css but may not conform to its
8755ade68   Tejun Heo   cgroup_freezer: a...
175
176
  	 * current state before executing the following - !frozen tasks may
  	 * be visible in a FROZEN cgroup and frozen tasks in a THAWED one.
80a6a2cf3   Li Zefan   freezer_cg: remov...
177
  	 */
924f0d9a2   Tejun Heo   cgroup: drop @ski...
178
  	cgroup_taskset_for_each(task, tset) {
d6a2fe134   Tejun Heo   cgroup_freezer: m...
179
  		if (!(freezer->state & CGROUP_FREEZING)) {
8755ade68   Tejun Heo   cgroup_freezer: a...
180
181
182
  			__thaw_task(task);
  		} else {
  			freeze_task(task);
d6a2fe134   Tejun Heo   cgroup_freezer: m...
183
  			freezer->state &= ~CGROUP_FROZEN;
ef9fe980c   Tejun Heo   cgroup_freezer: i...
184
  			clear_frozen = true;
8755ade68   Tejun Heo   cgroup_freezer: a...
185
186
  		}
  	}
dc52ddc0e   Matt Helsley   container freezer...
187

8755ade68   Tejun Heo   cgroup_freezer: a...
188
  	spin_unlock_irq(&freezer->lock);
ef9fe980c   Tejun Heo   cgroup_freezer: i...
189
190
191
192
193
194
195
196
197
198
199
200
201
202
  
  	/*
  	 * Propagate FROZEN clearing upwards.  We may race with
  	 * update_if_frozen(), but as long as both work bottom-up, either
  	 * update_if_frozen() sees child's FROZEN cleared or we clear the
  	 * parent's FROZEN later.  No parent w/ !FROZEN children can be
  	 * left FROZEN.
  	 */
  	while (clear_frozen && (freezer = parent_freezer(freezer))) {
  		spin_lock_irq(&freezer->lock);
  		freezer->state &= ~CGROUP_FROZEN;
  		clear_frozen = freezer->state & CGROUP_FREEZING;
  		spin_unlock_irq(&freezer->lock);
  	}
f780bdb7c   Ben Blum   cgroups: add per-...
203
  }
a60bed296   Tejun Heo   cgroup_freezer: d...
204
205
206
207
208
209
210
211
212
213
  /**
   * freezer_fork - cgroup post fork callback
   * @task: a task which has just been forked
   *
   * @task has just been created and should conform to the current state of
   * the cgroup_freezer it belongs to.  This function may race against
   * freezer_attach().  Losing to freezer_attach() means that we don't have
   * to do anything as freezer_attach() will put @task into the appropriate
   * state.
   */
761b3ef50   Li Zefan   cgroup: remove cg...
214
  static void freezer_fork(struct task_struct *task)
dc52ddc0e   Matt Helsley   container freezer...
215
216
  {
  	struct freezer *freezer;
8b46f8808   Paul E. McKenney   rcu: Fix RCU lock...
217
  	rcu_read_lock();
dc52ddc0e   Matt Helsley   container freezer...
218
  	freezer = task_freezer(task);
dc52ddc0e   Matt Helsley   container freezer...
219

3b1b3f6e5   Li Zefan   freezer_cg: disab...
220
  	/*
a60bed296   Tejun Heo   cgroup_freezer: d...
221
222
223
224
225
  	 * The root cgroup is non-freezable, so we can skip locking the
  	 * freezer.  This is safe regardless of race with task migration.
  	 * If we didn't race or won, skipping is obviously the right thing
  	 * to do.  If we lost and root is the new cgroup, noop is still the
  	 * right thing to do.
3b1b3f6e5   Li Zefan   freezer_cg: disab...
226
  	 */
638769869   Tejun Heo   cgroup: add css_p...
227
  	if (!parent_freezer(freezer))
5edee61ed   Tejun Heo   cgroup: cgroup_su...
228
  		goto out;
3b1b3f6e5   Li Zefan   freezer_cg: disab...
229

a60bed296   Tejun Heo   cgroup_freezer: d...
230
231
232
233
234
235
236
237
238
  	/*
  	 * Grab @freezer->lock and freeze @task after verifying @task still
  	 * belongs to @freezer and it's freezing.  The former is for the
  	 * case where we have raced against task migration and lost and
  	 * @task is already in a different cgroup which may not be frozen.
  	 * This isn't strictly necessary as freeze_task() is allowed to be
  	 * called spuriously but let's do it anyway for, if nothing else,
  	 * documentation.
  	 */
dc52ddc0e   Matt Helsley   container freezer...
239
  	spin_lock_irq(&freezer->lock);
a60bed296   Tejun Heo   cgroup_freezer: d...
240
  	if (freezer == task_freezer(task) && (freezer->state & CGROUP_FREEZING))
839e3407d   Tejun Heo   freezer: remove u...
241
  		freeze_task(task);
dc52ddc0e   Matt Helsley   container freezer...
242
  	spin_unlock_irq(&freezer->lock);
5edee61ed   Tejun Heo   cgroup: cgroup_su...
243
244
  out:
  	rcu_read_unlock();
dc52ddc0e   Matt Helsley   container freezer...
245
  }
ef9fe980c   Tejun Heo   cgroup_freezer: i...
246
247
  /**
   * update_if_frozen - update whether a cgroup finished freezing
182446d08   Tejun Heo   cgroup: pass arou...
248
   * @css: css of interest
ef9fe980c   Tejun Heo   cgroup_freezer: i...
249
250
251
252
253
254
255
256
   *
   * Once FREEZING is initiated, transition to FROZEN is lazily updated by
   * calling this function.  If the current state is FREEZING but not FROZEN,
   * this function checks whether all tasks of this cgroup and the descendant
   * cgroups finished freezing and, if so, sets FROZEN.
   *
   * The caller is responsible for grabbing RCU read lock and calling
   * update_if_frozen() on all descendants prior to invoking this function.
b4d18311d   Tejun Heo   cgroup_freezer: p...
257
258
   *
   * Task states and freezer state might disagree while tasks are being
182446d08   Tejun Heo   cgroup: pass arou...
259
   * migrated into or out of @css, so we can't verify task states against
ead5c4737   Tejun Heo   cgroup_freezer: d...
260
   * @freezer state here.  See freezer_attach() for details.
dc52ddc0e   Matt Helsley   container freezer...
261
   */
182446d08   Tejun Heo   cgroup: pass arou...
262
  static void update_if_frozen(struct cgroup_subsys_state *css)
dc52ddc0e   Matt Helsley   container freezer...
263
  {
182446d08   Tejun Heo   cgroup: pass arou...
264
  	struct freezer *freezer = css_freezer(css);
492eb21b9   Tejun Heo   cgroup: make hier...
265
  	struct cgroup_subsys_state *pos;
72ec70299   Tejun Heo   cgroup: make task...
266
  	struct css_task_iter it;
dc52ddc0e   Matt Helsley   container freezer...
267
  	struct task_struct *task;
b4d18311d   Tejun Heo   cgroup_freezer: p...
268

ef9fe980c   Tejun Heo   cgroup_freezer: i...
269
270
271
  	WARN_ON_ONCE(!rcu_read_lock_held());
  
  	spin_lock_irq(&freezer->lock);
d6a2fe134   Tejun Heo   cgroup_freezer: m...
272
273
  	if (!(freezer->state & CGROUP_FREEZING) ||
  	    (freezer->state & CGROUP_FROZEN))
ef9fe980c   Tejun Heo   cgroup_freezer: i...
274
275
276
  		goto out_unlock;
  
  	/* are all (live) children frozen? */
492eb21b9   Tejun Heo   cgroup: make hier...
277
278
  	css_for_each_child(pos, css) {
  		struct freezer *child = css_freezer(pos);
dc52ddc0e   Matt Helsley   container freezer...
279

ef9fe980c   Tejun Heo   cgroup_freezer: i...
280
281
282
283
284
285
  		if ((child->state & CGROUP_FREEZER_ONLINE) &&
  		    !(child->state & CGROUP_FROZEN))
  			goto out_unlock;
  	}
  
  	/* are all tasks frozen? */
72ec70299   Tejun Heo   cgroup: make task...
286
  	css_task_iter_start(css, &it);
b4d18311d   Tejun Heo   cgroup_freezer: p...
287

72ec70299   Tejun Heo   cgroup: make task...
288
  	while ((task = css_task_iter_next(&it))) {
3c426d5e1   Tejun Heo   cgroup_freezer: d...
289
  		if (freezing(task)) {
3c426d5e1   Tejun Heo   cgroup_freezer: d...
290
291
292
293
294
295
  			/*
  			 * freezer_should_skip() indicates that the task
  			 * should be skipped when determining freezing
  			 * completion.  Consider it frozen in addition to
  			 * the usual frozen condition.
  			 */
5d8f72b55   Oleg Nesterov   freezer: change p...
296
  			if (!frozen(task) && !freezer_should_skip(task))
ef9fe980c   Tejun Heo   cgroup_freezer: i...
297
  				goto out_iter_end;
3c426d5e1   Tejun Heo   cgroup_freezer: d...
298
  		}
dc52ddc0e   Matt Helsley   container freezer...
299
  	}
d6a2fe134   Tejun Heo   cgroup_freezer: m...
300
  	freezer->state |= CGROUP_FROZEN;
ef9fe980c   Tejun Heo   cgroup_freezer: i...
301
  out_iter_end:
72ec70299   Tejun Heo   cgroup: make task...
302
  	css_task_iter_end(&it);
ef9fe980c   Tejun Heo   cgroup_freezer: i...
303
304
  out_unlock:
  	spin_unlock_irq(&freezer->lock);
dc52ddc0e   Matt Helsley   container freezer...
305
  }
2da8ca822   Tejun Heo   cgroup: replace c...
306
  static int freezer_read(struct seq_file *m, void *v)
dc52ddc0e   Matt Helsley   container freezer...
307
  {
2da8ca822   Tejun Heo   cgroup: replace c...
308
  	struct cgroup_subsys_state *css = seq_css(m), *pos;
dc52ddc0e   Matt Helsley   container freezer...
309

ef9fe980c   Tejun Heo   cgroup_freezer: i...
310
  	rcu_read_lock();
dc52ddc0e   Matt Helsley   container freezer...
311

ef9fe980c   Tejun Heo   cgroup_freezer: i...
312
  	/* update states bottom-up */
492eb21b9   Tejun Heo   cgroup: make hier...
313
314
  	css_for_each_descendant_post(pos, css)
  		update_if_frozen(pos);
ef9fe980c   Tejun Heo   cgroup_freezer: i...
315
316
  
  	rcu_read_unlock();
182446d08   Tejun Heo   cgroup: pass arou...
317
  	seq_puts(m, freezer_state_strs(css_freezer(css)->state));
dc52ddc0e   Matt Helsley   container freezer...
318
319
320
321
  	seq_putc(m, '
  ');
  	return 0;
  }
bcd66c894   Tejun Heo   cgroup_freezer: t...
322
  static void freeze_cgroup(struct freezer *freezer)
dc52ddc0e   Matt Helsley   container freezer...
323
  {
72ec70299   Tejun Heo   cgroup: make task...
324
  	struct css_task_iter it;
dc52ddc0e   Matt Helsley   container freezer...
325
  	struct task_struct *task;
dc52ddc0e   Matt Helsley   container freezer...
326

72ec70299   Tejun Heo   cgroup: make task...
327
328
  	css_task_iter_start(&freezer->css, &it);
  	while ((task = css_task_iter_next(&it)))
51f246ed9   Tejun Heo   cgroup_freezer: m...
329
  		freeze_task(task);
72ec70299   Tejun Heo   cgroup: make task...
330
  	css_task_iter_end(&it);
dc52ddc0e   Matt Helsley   container freezer...
331
  }
bcd66c894   Tejun Heo   cgroup_freezer: t...
332
  static void unfreeze_cgroup(struct freezer *freezer)
dc52ddc0e   Matt Helsley   container freezer...
333
  {
72ec70299   Tejun Heo   cgroup: make task...
334
  	struct css_task_iter it;
dc52ddc0e   Matt Helsley   container freezer...
335
  	struct task_struct *task;
72ec70299   Tejun Heo   cgroup: make task...
336
337
  	css_task_iter_start(&freezer->css, &it);
  	while ((task = css_task_iter_next(&it)))
a5be2d0d1   Tejun Heo   freezer: rename t...
338
  		__thaw_task(task);
72ec70299   Tejun Heo   cgroup: make task...
339
  	css_task_iter_end(&it);
dc52ddc0e   Matt Helsley   container freezer...
340
  }
04a4ec325   Tejun Heo   cgroup_freezer: p...
341
342
343
344
  /**
   * freezer_apply_state - apply state change to a single cgroup_freezer
   * @freezer: freezer to apply state change to
   * @freeze: whether to freeze or unfreeze
a22521806   Tejun Heo   cgroup_freezer: i...
345
346
347
348
   * @state: CGROUP_FREEZING_* flag to set or clear
   *
   * Set or clear @state on @cgroup according to @freeze, and perform
   * freezing or thawing as necessary.
04a4ec325   Tejun Heo   cgroup_freezer: p...
349
   */
a22521806   Tejun Heo   cgroup_freezer: i...
350
351
  static void freezer_apply_state(struct freezer *freezer, bool freeze,
  				unsigned int state)
dc52ddc0e   Matt Helsley   container freezer...
352
  {
ead5c4737   Tejun Heo   cgroup_freezer: d...
353
  	/* also synchronizes against task migration, see freezer_attach() */
04a4ec325   Tejun Heo   cgroup_freezer: p...
354
  	lockdep_assert_held(&freezer->lock);
51308ee59   Li Zefan   freezer_cg: simpl...
355

5300a9b34   Tejun Heo   cgroup_freezer: a...
356
357
  	if (!(freezer->state & CGROUP_FREEZER_ONLINE))
  		return;
04a4ec325   Tejun Heo   cgroup_freezer: p...
358
  	if (freeze) {
d6a2fe134   Tejun Heo   cgroup_freezer: m...
359
  		if (!(freezer->state & CGROUP_FREEZING))
a3201227f   Tejun Heo   freezer: make fre...
360
  			atomic_inc(&system_freezing_cnt);
a22521806   Tejun Heo   cgroup_freezer: i...
361
  		freezer->state |= state;
bcd66c894   Tejun Heo   cgroup_freezer: t...
362
  		freeze_cgroup(freezer);
04a4ec325   Tejun Heo   cgroup_freezer: p...
363
  	} else {
a22521806   Tejun Heo   cgroup_freezer: i...
364
365
366
367
368
369
370
371
372
373
  		bool was_freezing = freezer->state & CGROUP_FREEZING;
  
  		freezer->state &= ~state;
  
  		if (!(freezer->state & CGROUP_FREEZING)) {
  			if (was_freezing)
  				atomic_dec(&system_freezing_cnt);
  			freezer->state &= ~CGROUP_FROZEN;
  			unfreeze_cgroup(freezer);
  		}
dc52ddc0e   Matt Helsley   container freezer...
374
  	}
04a4ec325   Tejun Heo   cgroup_freezer: p...
375
  }
22b4e111f   Tejun Heo   cgroup_freezer: p...
376

04a4ec325   Tejun Heo   cgroup_freezer: p...
377
378
379
380
381
  /**
   * freezer_change_state - change the freezing state of a cgroup_freezer
   * @freezer: freezer of interest
   * @freeze: whether to freeze or thaw
   *
ef9fe980c   Tejun Heo   cgroup_freezer: i...
382
383
   * Freeze or thaw @freezer according to @freeze.  The operations are
   * recursive - all descendants of @freezer will be affected.
04a4ec325   Tejun Heo   cgroup_freezer: p...
384
385
386
   */
  static void freezer_change_state(struct freezer *freezer, bool freeze)
  {
492eb21b9   Tejun Heo   cgroup: make hier...
387
  	struct cgroup_subsys_state *pos;
ef9fe980c   Tejun Heo   cgroup_freezer: i...
388

ef9fe980c   Tejun Heo   cgroup_freezer: i...
389
390
391
392
393
394
  	/*
  	 * Update all its descendants in pre-order traversal.  Each
  	 * descendant will try to inherit its parent's FREEZING state as
  	 * CGROUP_FREEZING_PARENT.
  	 */
  	rcu_read_lock();
492eb21b9   Tejun Heo   cgroup: make hier...
395
396
  	css_for_each_descendant_pre(pos, &freezer->css) {
  		struct freezer *pos_f = css_freezer(pos);
ef9fe980c   Tejun Heo   cgroup_freezer: i...
397
  		struct freezer *parent = parent_freezer(pos_f);
ef9fe980c   Tejun Heo   cgroup_freezer: i...
398
  		spin_lock_irq(&pos_f->lock);
bd8815a6d   Tejun Heo   cgroup: make css_...
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  
  		if (pos_f == freezer) {
  			freezer_apply_state(pos_f, freeze,
  					    CGROUP_FREEZING_SELF);
  		} else {
  			/*
  			 * Our update to @parent->state is already visible
  			 * which is all we need.  No need to lock @parent.
  			 * For more info on synchronization, see
  			 * freezer_post_create().
  			 */
  			freezer_apply_state(pos_f,
  					    parent->state & CGROUP_FREEZING,
  					    CGROUP_FREEZING_PARENT);
  		}
ef9fe980c   Tejun Heo   cgroup_freezer: i...
414
415
416
  		spin_unlock_irq(&pos_f->lock);
  	}
  	rcu_read_unlock();
dc52ddc0e   Matt Helsley   container freezer...
417
  }
182446d08   Tejun Heo   cgroup: pass arou...
418
  static int freezer_write(struct cgroup_subsys_state *css, struct cftype *cft,
4d3bb511b   Tejun Heo   cgroup: drop cons...
419
  			 char *buffer)
dc52ddc0e   Matt Helsley   container freezer...
420
  {
04a4ec325   Tejun Heo   cgroup_freezer: p...
421
  	bool freeze;
dc52ddc0e   Matt Helsley   container freezer...
422

d6a2fe134   Tejun Heo   cgroup_freezer: m...
423
  	if (strcmp(buffer, freezer_state_strs(0)) == 0)
04a4ec325   Tejun Heo   cgroup_freezer: p...
424
  		freeze = false;
d6a2fe134   Tejun Heo   cgroup_freezer: m...
425
  	else if (strcmp(buffer, freezer_state_strs(CGROUP_FROZEN)) == 0)
04a4ec325   Tejun Heo   cgroup_freezer: p...
426
  		freeze = true;
dc52ddc0e   Matt Helsley   container freezer...
427
  	else
3b1b3f6e5   Li Zefan   freezer_cg: disab...
428
  		return -EINVAL;
dc52ddc0e   Matt Helsley   container freezer...
429

182446d08   Tejun Heo   cgroup: pass arou...
430
  	freezer_change_state(css_freezer(css), freeze);
51f246ed9   Tejun Heo   cgroup_freezer: m...
431
  	return 0;
dc52ddc0e   Matt Helsley   container freezer...
432
  }
182446d08   Tejun Heo   cgroup: pass arou...
433
434
  static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css,
  				      struct cftype *cft)
a22521806   Tejun Heo   cgroup_freezer: i...
435
  {
182446d08   Tejun Heo   cgroup: pass arou...
436
  	struct freezer *freezer = css_freezer(css);
a22521806   Tejun Heo   cgroup_freezer: i...
437
438
439
  
  	return (bool)(freezer->state & CGROUP_FREEZING_SELF);
  }
182446d08   Tejun Heo   cgroup: pass arou...
440
441
  static u64 freezer_parent_freezing_read(struct cgroup_subsys_state *css,
  					struct cftype *cft)
a22521806   Tejun Heo   cgroup_freezer: i...
442
  {
182446d08   Tejun Heo   cgroup: pass arou...
443
  	struct freezer *freezer = css_freezer(css);
a22521806   Tejun Heo   cgroup_freezer: i...
444
445
446
  
  	return (bool)(freezer->state & CGROUP_FREEZING_PARENT);
  }
dc52ddc0e   Matt Helsley   container freezer...
447
448
449
  static struct cftype files[] = {
  	{
  		.name = "state",
4baf6e332   Tejun Heo   cgroup: convert a...
450
  		.flags = CFTYPE_NOT_ON_ROOT,
2da8ca822   Tejun Heo   cgroup: replace c...
451
  		.seq_show = freezer_read,
dc52ddc0e   Matt Helsley   container freezer...
452
453
  		.write_string = freezer_write,
  	},
a22521806   Tejun Heo   cgroup_freezer: i...
454
455
456
457
458
459
460
461
462
463
  	{
  		.name = "self_freezing",
  		.flags = CFTYPE_NOT_ON_ROOT,
  		.read_u64 = freezer_self_freezing_read,
  	},
  	{
  		.name = "parent_freezing",
  		.flags = CFTYPE_NOT_ON_ROOT,
  		.read_u64 = freezer_parent_freezing_read,
  	},
4baf6e332   Tejun Heo   cgroup: convert a...
464
  	{ }	/* terminate */
dc52ddc0e   Matt Helsley   container freezer...
465
  };
073219e99   Tejun Heo   cgroup: clean up ...
466
  struct cgroup_subsys freezer_cgrp_subsys = {
92fb97487   Tejun Heo   cgroup: rename ->...
467
468
469
470
  	.css_alloc	= freezer_css_alloc,
  	.css_online	= freezer_css_online,
  	.css_offline	= freezer_css_offline,
  	.css_free	= freezer_css_free,
8755ade68   Tejun Heo   cgroup_freezer: a...
471
  	.attach		= freezer_attach,
dc52ddc0e   Matt Helsley   container freezer...
472
  	.fork		= freezer_fork,
4baf6e332   Tejun Heo   cgroup: convert a...
473
  	.base_cftypes	= files,
dc52ddc0e   Matt Helsley   container freezer...
474
  };