Blame view

kernel/cgroup_freezer.c 9.14 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
23
24
  #include <linux/cgroup.h>
  #include <linux/fs.h>
  #include <linux/uaccess.h>
  #include <linux/freezer.h>
  #include <linux/seq_file.h>
  
  enum freezer_state {
81dcf33c2   Matt Helsley   container freezer...
25
26
27
  	CGROUP_THAWED = 0,
  	CGROUP_FREEZING,
  	CGROUP_FROZEN,
dc52ddc0e   Matt Helsley   container freezer...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  };
  
  struct freezer {
  	struct cgroup_subsys_state css;
  	enum freezer_state state;
  	spinlock_t lock; /* protects _writes_ to state */
  };
  
  static inline struct freezer *cgroup_freezer(
  		struct cgroup *cgroup)
  {
  	return container_of(
  		cgroup_subsys_state(cgroup, freezer_subsys_id),
  		struct freezer, css);
  }
  
  static inline struct freezer *task_freezer(struct task_struct *task)
  {
  	return container_of(task_subsys_state(task, freezer_subsys_id),
  			    struct freezer, css);
  }
22b4e111f   Tejun Heo   cgroup_freezer: p...
49
  bool cgroup_freezing(struct task_struct *task)
dc52ddc0e   Matt Helsley   container freezer...
50
  {
22b4e111f   Tejun Heo   cgroup_freezer: p...
51
52
  	enum freezer_state state;
  	bool ret;
dc52ddc0e   Matt Helsley   container freezer...
53

22b4e111f   Tejun Heo   cgroup_freezer: p...
54
55
56
57
58
59
  	rcu_read_lock();
  	state = task_freezer(task)->state;
  	ret = state == CGROUP_FREEZING || state == CGROUP_FROZEN;
  	rcu_read_unlock();
  
  	return ret;
dc52ddc0e   Matt Helsley   container freezer...
60
61
62
63
64
65
66
  }
  
  /*
   * cgroups_write_string() limits the size of freezer state strings to
   * CGROUP_LOCAL_BUFFER_SIZE
   */
  static const char *freezer_state_strs[] = {
81dcf33c2   Matt Helsley   container freezer...
67
  	"THAWED",
dc52ddc0e   Matt Helsley   container freezer...
68
69
70
71
72
73
74
75
76
  	"FREEZING",
  	"FROZEN",
  };
  
  /*
   * State diagram
   * Transitions are caused by userspace writes to the freezer.state file.
   * The values in parenthesis are state labels. The rest are edge labels.
   *
81dcf33c2   Matt Helsley   container freezer...
77
78
79
80
   * (THAWED) --FROZEN--> (FREEZING) --FROZEN--> (FROZEN)
   *    ^ ^                    |                     |
   *    | \_______THAWED_______/                     |
   *    \__________________________THAWED____________/
dc52ddc0e   Matt Helsley   container freezer...
81
82
83
84
85
86
   */
  
  struct cgroup_subsys freezer_subsys;
  
  /* Locks taken and their ordering
   * ------------------------------
dc52ddc0e   Matt Helsley   container freezer...
87
   * cgroup_mutex (AKA cgroup_lock)
dc52ddc0e   Matt Helsley   container freezer...
88
   * freezer->lock
8f77578cc   Matt Helsley   Freezer / cgroup ...
89
90
   * css_set_lock
   * task->alloc_lock (AKA task_lock)
dc52ddc0e   Matt Helsley   container freezer...
91
92
93
94
95
96
97
   * task->sighand->siglock
   *
   * cgroup code forces css_set_lock to be taken before task->alloc_lock
   *
   * freezer_create(), freezer_destroy():
   * cgroup_mutex [ by cgroup core ]
   *
8f77578cc   Matt Helsley   Freezer / cgroup ...
98
99
   * freezer_can_attach():
   * cgroup_mutex (held by caller of can_attach)
dc52ddc0e   Matt Helsley   container freezer...
100
   *
dc52ddc0e   Matt Helsley   container freezer...
101
   * freezer_fork() (preserving fork() performance means can't take cgroup_mutex):
dc52ddc0e   Matt Helsley   container freezer...
102
103
104
105
106
107
   * freezer->lock
   *  sighand->siglock (if the cgroup is freezing)
   *
   * freezer_read():
   * cgroup_mutex
   *  freezer->lock
8f77578cc   Matt Helsley   Freezer / cgroup ...
108
109
   *   write_lock css_set_lock (cgroup iterator start)
   *    task->alloc_lock
dc52ddc0e   Matt Helsley   container freezer...
110
111
112
113
114
   *   read_lock css_set_lock (cgroup iterator start)
   *
   * freezer_write() (freeze):
   * cgroup_mutex
   *  freezer->lock
8f77578cc   Matt Helsley   Freezer / cgroup ...
115
116
   *   write_lock css_set_lock (cgroup iterator start)
   *    task->alloc_lock
dc52ddc0e   Matt Helsley   container freezer...
117
   *   read_lock css_set_lock (cgroup iterator start)
8f77578cc   Matt Helsley   Freezer / cgroup ...
118
   *    sighand->siglock (fake signal delivery inside freeze_task())
dc52ddc0e   Matt Helsley   container freezer...
119
120
121
122
   *
   * freezer_write() (unfreeze):
   * cgroup_mutex
   *  freezer->lock
8f77578cc   Matt Helsley   Freezer / cgroup ...
123
124
   *   write_lock css_set_lock (cgroup iterator start)
   *    task->alloc_lock
dc52ddc0e   Matt Helsley   container freezer...
125
   *   read_lock css_set_lock (cgroup iterator start)
a5be2d0d1   Tejun Heo   freezer: rename t...
126
   *    task->alloc_lock (inside __thaw_task(), prevents race with refrigerator())
dc52ddc0e   Matt Helsley   container freezer...
127
128
   *     sighand->siglock
   */
761b3ef50   Li Zefan   cgroup: remove cg...
129
  static struct cgroup_subsys_state *freezer_create(struct cgroup *cgroup)
dc52ddc0e   Matt Helsley   container freezer...
130
131
132
133
134
135
136
137
  {
  	struct freezer *freezer;
  
  	freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
  	if (!freezer)
  		return ERR_PTR(-ENOMEM);
  
  	spin_lock_init(&freezer->lock);
81dcf33c2   Matt Helsley   container freezer...
138
  	freezer->state = CGROUP_THAWED;
dc52ddc0e   Matt Helsley   container freezer...
139
140
  	return &freezer->css;
  }
761b3ef50   Li Zefan   cgroup: remove cg...
141
  static void freezer_destroy(struct cgroup *cgroup)
dc52ddc0e   Matt Helsley   container freezer...
142
  {
a3201227f   Tejun Heo   freezer: make fre...
143
144
145
146
147
  	struct freezer *freezer = cgroup_freezer(cgroup);
  
  	if (freezer->state != CGROUP_THAWED)
  		atomic_dec(&system_freezing_cnt);
  	kfree(freezer);
dc52ddc0e   Matt Helsley   container freezer...
148
  }
884a45d96   Michal Hocko   cgroup_freezer: f...
149
150
151
152
153
154
  /* task is frozen or will freeze immediately when next it gets woken */
  static bool is_task_frozen_enough(struct task_struct *task)
  {
  	return frozen(task) ||
  		(task_is_stopped_or_traced(task) && freezing(task));
  }
957a4eeaf   Matt Helsley   container freezer...
155
156
157
158
159
  /*
   * The call to cgroup_lock() in the freezer.state write method prevents
   * a write to that file racing against an attach, and hence the
   * can_attach() result will remain valid until the attach completes.
   */
761b3ef50   Li Zefan   cgroup: remove cg...
160
  static int freezer_can_attach(struct cgroup *new_cgroup,
2f7ee5691   Tejun Heo   cgroup: introduce...
161
  			      struct cgroup_taskset *tset)
dc52ddc0e   Matt Helsley   container freezer...
162
163
  {
  	struct freezer *freezer;
bb9d97b6d   Tejun Heo   cgroup: don't use...
164
  	struct task_struct *task;
957a4eeaf   Matt Helsley   container freezer...
165

80a6a2cf3   Li Zefan   freezer_cg: remov...
166
167
  	/*
  	 * Anything frozen can't move or be moved to/from.
80a6a2cf3   Li Zefan   freezer_cg: remov...
168
  	 */
bb9d97b6d   Tejun Heo   cgroup: don't use...
169
170
171
  	cgroup_taskset_for_each(task, new_cgroup, tset)
  		if (cgroup_freezing(task))
  			return -EBUSY;
957a4eeaf   Matt Helsley   container freezer...
172

0bdba580a   Tomasz Buchert   cgroup_freezer: f...
173
174
  	freezer = cgroup_freezer(new_cgroup);
  	if (freezer->state != CGROUP_THAWED)
957a4eeaf   Matt Helsley   container freezer...
175
  		return -EBUSY;
dc52ddc0e   Matt Helsley   container freezer...
176

f780bdb7c   Ben Blum   cgroups: add per-...
177
178
  	return 0;
  }
761b3ef50   Li Zefan   cgroup: remove cg...
179
  static void freezer_fork(struct task_struct *task)
dc52ddc0e   Matt Helsley   container freezer...
180
181
  {
  	struct freezer *freezer;
687446760   Li Zefan   freezer_cg: remov...
182
183
184
185
  	/*
  	 * No lock is needed, since the task isn't on tasklist yet,
  	 * so it can't be moved to another cgroup, which means the
  	 * freezer won't be removed and will be valid during this
8b46f8808   Paul E. McKenney   rcu: Fix RCU lock...
186
187
  	 * function call.  Nevertheless, apply RCU read-side critical
  	 * section to suppress RCU lockdep false positives.
687446760   Li Zefan   freezer_cg: remov...
188
  	 */
8b46f8808   Paul E. McKenney   rcu: Fix RCU lock...
189
  	rcu_read_lock();
dc52ddc0e   Matt Helsley   container freezer...
190
  	freezer = task_freezer(task);
8b46f8808   Paul E. McKenney   rcu: Fix RCU lock...
191
  	rcu_read_unlock();
dc52ddc0e   Matt Helsley   container freezer...
192

3b1b3f6e5   Li Zefan   freezer_cg: disab...
193
194
195
196
197
198
  	/*
  	 * The root cgroup is non-freezable, so we can skip the
  	 * following check.
  	 */
  	if (!freezer->css.cgroup->parent)
  		return;
dc52ddc0e   Matt Helsley   container freezer...
199
  	spin_lock_irq(&freezer->lock);
7ccb97437   Li Zefan   freezer_cg: fix i...
200
  	BUG_ON(freezer->state == CGROUP_FROZEN);
81dcf33c2   Matt Helsley   container freezer...
201
202
  	/* Locking avoids race with FREEZING -> THAWED transitions. */
  	if (freezer->state == CGROUP_FREEZING)
839e3407d   Tejun Heo   freezer: remove u...
203
  		freeze_task(task);
dc52ddc0e   Matt Helsley   container freezer...
204
205
206
207
208
209
  	spin_unlock_irq(&freezer->lock);
  }
  
  /*
   * caller must hold freezer->lock
   */
2d3cbf8bc   Tomasz Buchert   cgroup_freezer: u...
210
  static void update_if_frozen(struct cgroup *cgroup,
1aece3483   Matt Helsley   container freezer...
211
  				 struct freezer *freezer)
dc52ddc0e   Matt Helsley   container freezer...
212
213
214
215
  {
  	struct cgroup_iter it;
  	struct task_struct *task;
  	unsigned int nfrozen = 0, ntotal = 0;
2d3cbf8bc   Tomasz Buchert   cgroup_freezer: u...
216
  	enum freezer_state old_state = freezer->state;
dc52ddc0e   Matt Helsley   container freezer...
217
218
219
220
  
  	cgroup_iter_start(cgroup, &it);
  	while ((task = cgroup_iter_next(cgroup, &it))) {
  		ntotal++;
b00f4dc5f   Rafael J. Wysocki   Merge branch 'mas...
221
  		if (freezing(task) && is_task_frozen_enough(task))
dc52ddc0e   Matt Helsley   container freezer...
222
223
  			nfrozen++;
  	}
2d3cbf8bc   Tomasz Buchert   cgroup_freezer: u...
224
225
226
227
228
229
230
231
  	if (old_state == CGROUP_THAWED) {
  		BUG_ON(nfrozen > 0);
  	} else if (old_state == CGROUP_FREEZING) {
  		if (nfrozen == ntotal)
  			freezer->state = CGROUP_FROZEN;
  	} else { /* old_state == CGROUP_FROZEN */
  		BUG_ON(nfrozen != ntotal);
  	}
dc52ddc0e   Matt Helsley   container freezer...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  	cgroup_iter_end(cgroup, &it);
  }
  
  static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
  			struct seq_file *m)
  {
  	struct freezer *freezer;
  	enum freezer_state state;
  
  	if (!cgroup_lock_live_group(cgroup))
  		return -ENODEV;
  
  	freezer = cgroup_freezer(cgroup);
  	spin_lock_irq(&freezer->lock);
  	state = freezer->state;
81dcf33c2   Matt Helsley   container freezer...
247
  	if (state == CGROUP_FREEZING) {
dc52ddc0e   Matt Helsley   container freezer...
248
249
  		/* We change from FREEZING to FROZEN lazily if the cgroup was
  		 * only partially frozen when we exitted write. */
2d3cbf8bc   Tomasz Buchert   cgroup_freezer: u...
250
  		update_if_frozen(cgroup, freezer);
dc52ddc0e   Matt Helsley   container freezer...
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
  		state = freezer->state;
  	}
  	spin_unlock_irq(&freezer->lock);
  	cgroup_unlock();
  
  	seq_puts(m, freezer_state_strs[state]);
  	seq_putc(m, '
  ');
  	return 0;
  }
  
  static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
  {
  	struct cgroup_iter it;
  	struct task_struct *task;
  	unsigned int num_cant_freeze_now = 0;
dc52ddc0e   Matt Helsley   container freezer...
267
268
  	cgroup_iter_start(cgroup, &it);
  	while ((task = cgroup_iter_next(cgroup, &it))) {
839e3407d   Tejun Heo   freezer: remove u...
269
  		if (!freeze_task(task))
dc52ddc0e   Matt Helsley   container freezer...
270
  			continue;
884a45d96   Michal Hocko   cgroup_freezer: f...
271
  		if (is_task_frozen_enough(task))
dc52ddc0e   Matt Helsley   container freezer...
272
273
274
275
276
277
278
279
  			continue;
  		if (!freezing(task) && !freezer_should_skip(task))
  			num_cant_freeze_now++;
  	}
  	cgroup_iter_end(cgroup, &it);
  
  	return num_cant_freeze_now ? -EBUSY : 0;
  }
00c2e63c3   Li Zefan   freezer_cg: use t...
280
  static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
dc52ddc0e   Matt Helsley   container freezer...
281
282
283
284
285
  {
  	struct cgroup_iter it;
  	struct task_struct *task;
  
  	cgroup_iter_start(cgroup, &it);
a5be2d0d1   Tejun Heo   freezer: rename t...
286
287
  	while ((task = cgroup_iter_next(cgroup, &it)))
  		__thaw_task(task);
dc52ddc0e   Matt Helsley   container freezer...
288
  	cgroup_iter_end(cgroup, &it);
dc52ddc0e   Matt Helsley   container freezer...
289
290
291
292
293
294
295
296
297
  }
  
  static int freezer_change_state(struct cgroup *cgroup,
  				enum freezer_state goal_state)
  {
  	struct freezer *freezer;
  	int retval = 0;
  
  	freezer = cgroup_freezer(cgroup);
51308ee59   Li Zefan   freezer_cg: simpl...
298

dc52ddc0e   Matt Helsley   container freezer...
299
  	spin_lock_irq(&freezer->lock);
51308ee59   Li Zefan   freezer_cg: simpl...
300

2d3cbf8bc   Tomasz Buchert   cgroup_freezer: u...
301
  	update_if_frozen(cgroup, freezer);
51308ee59   Li Zefan   freezer_cg: simpl...
302
303
  
  	switch (goal_state) {
81dcf33c2   Matt Helsley   container freezer...
304
  	case CGROUP_THAWED:
a3201227f   Tejun Heo   freezer: make fre...
305
306
  		if (freezer->state != CGROUP_THAWED)
  			atomic_dec(&system_freezing_cnt);
22b4e111f   Tejun Heo   cgroup_freezer: p...
307
  		freezer->state = CGROUP_THAWED;
51308ee59   Li Zefan   freezer_cg: simpl...
308
  		unfreeze_cgroup(cgroup, freezer);
dc52ddc0e   Matt Helsley   container freezer...
309
  		break;
81dcf33c2   Matt Helsley   container freezer...
310
  	case CGROUP_FROZEN:
a3201227f   Tejun Heo   freezer: make fre...
311
312
  		if (freezer->state == CGROUP_THAWED)
  			atomic_inc(&system_freezing_cnt);
22b4e111f   Tejun Heo   cgroup_freezer: p...
313
  		freezer->state = CGROUP_FREEZING;
51308ee59   Li Zefan   freezer_cg: simpl...
314
  		retval = try_to_freeze_cgroup(cgroup, freezer);
dc52ddc0e   Matt Helsley   container freezer...
315
316
  		break;
  	default:
51308ee59   Li Zefan   freezer_cg: simpl...
317
  		BUG();
dc52ddc0e   Matt Helsley   container freezer...
318
  	}
22b4e111f   Tejun Heo   cgroup_freezer: p...
319

dc52ddc0e   Matt Helsley   container freezer...
320
321
322
323
324
325
326
327
328
329
330
  	spin_unlock_irq(&freezer->lock);
  
  	return retval;
  }
  
  static int freezer_write(struct cgroup *cgroup,
  			 struct cftype *cft,
  			 const char *buffer)
  {
  	int retval;
  	enum freezer_state goal_state;
81dcf33c2   Matt Helsley   container freezer...
331
332
333
334
  	if (strcmp(buffer, freezer_state_strs[CGROUP_THAWED]) == 0)
  		goal_state = CGROUP_THAWED;
  	else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0)
  		goal_state = CGROUP_FROZEN;
dc52ddc0e   Matt Helsley   container freezer...
335
  	else
3b1b3f6e5   Li Zefan   freezer_cg: disab...
336
  		return -EINVAL;
dc52ddc0e   Matt Helsley   container freezer...
337
338
339
340
341
342
343
344
345
346
347
  
  	if (!cgroup_lock_live_group(cgroup))
  		return -ENODEV;
  	retval = freezer_change_state(cgroup, goal_state);
  	cgroup_unlock();
  	return retval;
  }
  
  static struct cftype files[] = {
  	{
  		.name = "state",
4baf6e332   Tejun Heo   cgroup: convert a...
348
  		.flags = CFTYPE_NOT_ON_ROOT,
dc52ddc0e   Matt Helsley   container freezer...
349
350
351
  		.read_seq_string = freezer_read,
  		.write_string = freezer_write,
  	},
4baf6e332   Tejun Heo   cgroup: convert a...
352
  	{ }	/* terminate */
dc52ddc0e   Matt Helsley   container freezer...
353
  };
dc52ddc0e   Matt Helsley   container freezer...
354
355
356
357
  struct cgroup_subsys freezer_subsys = {
  	.name		= "freezer",
  	.create		= freezer_create,
  	.destroy	= freezer_destroy,
dc52ddc0e   Matt Helsley   container freezer...
358
359
  	.subsys_id	= freezer_subsys_id,
  	.can_attach	= freezer_can_attach,
dc52ddc0e   Matt Helsley   container freezer...
360
  	.fork		= freezer_fork,
4baf6e332   Tejun Heo   cgroup: convert a...
361
  	.base_cftypes	= files,
dc52ddc0e   Matt Helsley   container freezer...
362
  };