Blame view

kernel/cgroup_freezer.c 9.36 KB
dc52ddc0e   Matt Helsley   container freezer...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  /*
   * 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.
   */
  
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
18
  #include <linux/slab.h>
dc52ddc0e   Matt Helsley   container freezer...
19
20
21
22
23
24
25
  #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...
26
27
28
  	CGROUP_THAWED = 0,
  	CGROUP_FREEZING,
  	CGROUP_FROZEN,
dc52ddc0e   Matt Helsley   container freezer...
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  };
  
  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);
  }
d5de4ddb1   Tomasz Buchert   cgroup_freezer: u...
50
  static inline int __cgroup_freezing_or_frozen(struct task_struct *task)
dc52ddc0e   Matt Helsley   container freezer...
51
  {
d5de4ddb1   Tomasz Buchert   cgroup_freezer: u...
52
53
54
  	enum freezer_state state = task_freezer(task)->state;
  	return (state == CGROUP_FREEZING) || (state == CGROUP_FROZEN);
  }
dc52ddc0e   Matt Helsley   container freezer...
55

d5de4ddb1   Tomasz Buchert   cgroup_freezer: u...
56
57
58
  int cgroup_freezing_or_frozen(struct task_struct *task)
  {
  	int result;
dc52ddc0e   Matt Helsley   container freezer...
59
  	task_lock(task);
d5de4ddb1   Tomasz Buchert   cgroup_freezer: u...
60
  	result = __cgroup_freezing_or_frozen(task);
dc52ddc0e   Matt Helsley   container freezer...
61
  	task_unlock(task);
d5de4ddb1   Tomasz Buchert   cgroup_freezer: u...
62
  	return result;
dc52ddc0e   Matt Helsley   container freezer...
63
64
65
66
67
68
69
  }
  
  /*
   * 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...
70
  	"THAWED",
dc52ddc0e   Matt Helsley   container freezer...
71
72
73
74
75
76
77
78
79
  	"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...
80
81
82
83
   * (THAWED) --FROZEN--> (FREEZING) --FROZEN--> (FROZEN)
   *    ^ ^                    |                     |
   *    | \_______THAWED_______/                     |
   *    \__________________________THAWED____________/
dc52ddc0e   Matt Helsley   container freezer...
84
85
86
87
88
89
   */
  
  struct cgroup_subsys freezer_subsys;
  
  /* Locks taken and their ordering
   * ------------------------------
dc52ddc0e   Matt Helsley   container freezer...
90
   * cgroup_mutex (AKA cgroup_lock)
dc52ddc0e   Matt Helsley   container freezer...
91
   * freezer->lock
8f77578cc   Matt Helsley   Freezer / cgroup ...
92
93
   * css_set_lock
   * task->alloc_lock (AKA task_lock)
dc52ddc0e   Matt Helsley   container freezer...
94
95
96
97
98
99
100
   * 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 ...
101
102
   * freezer_can_attach():
   * cgroup_mutex (held by caller of can_attach)
dc52ddc0e   Matt Helsley   container freezer...
103
   *
8f77578cc   Matt Helsley   Freezer / cgroup ...
104
   * cgroup_freezing_or_frozen():
dc52ddc0e   Matt Helsley   container freezer...
105
106
107
   * task->alloc_lock (to get task's cgroup)
   *
   * freezer_fork() (preserving fork() performance means can't take cgroup_mutex):
dc52ddc0e   Matt Helsley   container freezer...
108
109
110
111
112
113
   * freezer->lock
   *  sighand->siglock (if the cgroup is freezing)
   *
   * freezer_read():
   * cgroup_mutex
   *  freezer->lock
8f77578cc   Matt Helsley   Freezer / cgroup ...
114
115
   *   write_lock css_set_lock (cgroup iterator start)
   *    task->alloc_lock
dc52ddc0e   Matt Helsley   container freezer...
116
117
118
119
120
   *   read_lock css_set_lock (cgroup iterator start)
   *
   * freezer_write() (freeze):
   * cgroup_mutex
   *  freezer->lock
8f77578cc   Matt Helsley   Freezer / cgroup ...
121
122
   *   write_lock css_set_lock (cgroup iterator start)
   *    task->alloc_lock
dc52ddc0e   Matt Helsley   container freezer...
123
   *   read_lock css_set_lock (cgroup iterator start)
8f77578cc   Matt Helsley   Freezer / cgroup ...
124
   *    sighand->siglock (fake signal delivery inside freeze_task())
dc52ddc0e   Matt Helsley   container freezer...
125
126
127
128
   *
   * freezer_write() (unfreeze):
   * cgroup_mutex
   *  freezer->lock
8f77578cc   Matt Helsley   Freezer / cgroup ...
129
130
   *   write_lock css_set_lock (cgroup iterator start)
   *    task->alloc_lock
dc52ddc0e   Matt Helsley   container freezer...
131
   *   read_lock css_set_lock (cgroup iterator start)
8f77578cc   Matt Helsley   Freezer / cgroup ...
132
   *    task->alloc_lock (inside thaw_process(), prevents race with refrigerator())
dc52ddc0e   Matt Helsley   container freezer...
133
134
135
136
137
138
139
140
141
142
143
144
   *     sighand->siglock
   */
  static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss,
  						  struct cgroup *cgroup)
  {
  	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...
145
  	freezer->state = CGROUP_THAWED;
dc52ddc0e   Matt Helsley   container freezer...
146
147
148
149
150
151
152
153
  	return &freezer->css;
  }
  
  static void freezer_destroy(struct cgroup_subsys *ss,
  			    struct cgroup *cgroup)
  {
  	kfree(cgroup_freezer(cgroup));
  }
957a4eeaf   Matt Helsley   container freezer...
154
155
156
157
158
  /*
   * 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.
   */
dc52ddc0e   Matt Helsley   container freezer...
159
160
  static int freezer_can_attach(struct cgroup_subsys *ss,
  			      struct cgroup *new_cgroup,
f780bdb7c   Ben Blum   cgroups: add per-...
161
  			      struct task_struct *task)
dc52ddc0e   Matt Helsley   container freezer...
162
163
  {
  	struct freezer *freezer;
957a4eeaf   Matt Helsley   container freezer...
164

80a6a2cf3   Li Zefan   freezer_cg: remov...
165
166
  	/*
  	 * Anything frozen can't move or be moved to/from.
80a6a2cf3   Li Zefan   freezer_cg: remov...
167
  	 */
957a4eeaf   Matt Helsley   container freezer...
168

0bdba580a   Tomasz Buchert   cgroup_freezer: f...
169
170
  	freezer = cgroup_freezer(new_cgroup);
  	if (freezer->state != CGROUP_THAWED)
957a4eeaf   Matt Helsley   container freezer...
171
  		return -EBUSY;
dc52ddc0e   Matt Helsley   container freezer...
172

f780bdb7c   Ben Blum   cgroups: add per-...
173
174
175
176
177
  	return 0;
  }
  
  static int freezer_can_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
  {
0bdba580a   Tomasz Buchert   cgroup_freezer: f...
178
  	rcu_read_lock();
f780bdb7c   Ben Blum   cgroups: add per-...
179
  	if (__cgroup_freezing_or_frozen(tsk)) {
0bdba580a   Tomasz Buchert   cgroup_freezer: f...
180
  		rcu_read_unlock();
957a4eeaf   Matt Helsley   container freezer...
181
  		return -EBUSY;
0bdba580a   Tomasz Buchert   cgroup_freezer: f...
182
183
  	}
  	rcu_read_unlock();
80a6a2cf3   Li Zefan   freezer_cg: remov...
184
  	return 0;
dc52ddc0e   Matt Helsley   container freezer...
185
186
187
188
189
  }
  
  static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task)
  {
  	struct freezer *freezer;
687446760   Li Zefan   freezer_cg: remov...
190
191
192
193
  	/*
  	 * 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...
194
195
  	 * function call.  Nevertheless, apply RCU read-side critical
  	 * section to suppress RCU lockdep false positives.
687446760   Li Zefan   freezer_cg: remov...
196
  	 */
8b46f8808   Paul E. McKenney   rcu: Fix RCU lock...
197
  	rcu_read_lock();
dc52ddc0e   Matt Helsley   container freezer...
198
  	freezer = task_freezer(task);
8b46f8808   Paul E. McKenney   rcu: Fix RCU lock...
199
  	rcu_read_unlock();
dc52ddc0e   Matt Helsley   container freezer...
200

3b1b3f6e5   Li Zefan   freezer_cg: disab...
201
202
203
204
205
206
  	/*
  	 * The root cgroup is non-freezable, so we can skip the
  	 * following check.
  	 */
  	if (!freezer->css.cgroup->parent)
  		return;
dc52ddc0e   Matt Helsley   container freezer...
207
  	spin_lock_irq(&freezer->lock);
7ccb97437   Li Zefan   freezer_cg: fix i...
208
  	BUG_ON(freezer->state == CGROUP_FROZEN);
81dcf33c2   Matt Helsley   container freezer...
209
210
  	/* Locking avoids race with FREEZING -> THAWED transitions. */
  	if (freezer->state == CGROUP_FREEZING)
dc52ddc0e   Matt Helsley   container freezer...
211
212
213
214
215
216
217
  		freeze_task(task, true);
  	spin_unlock_irq(&freezer->lock);
  }
  
  /*
   * caller must hold freezer->lock
   */
2d3cbf8bc   Tomasz Buchert   cgroup_freezer: u...
218
  static void update_if_frozen(struct cgroup *cgroup,
1aece3483   Matt Helsley   container freezer...
219
  				 struct freezer *freezer)
dc52ddc0e   Matt Helsley   container freezer...
220
221
222
223
  {
  	struct cgroup_iter it;
  	struct task_struct *task;
  	unsigned int nfrozen = 0, ntotal = 0;
2d3cbf8bc   Tomasz Buchert   cgroup_freezer: u...
224
  	enum freezer_state old_state = freezer->state;
dc52ddc0e   Matt Helsley   container freezer...
225
226
227
228
  
  	cgroup_iter_start(cgroup, &it);
  	while ((task = cgroup_iter_next(cgroup, &it))) {
  		ntotal++;
2d3cbf8bc   Tomasz Buchert   cgroup_freezer: u...
229
  		if (frozen(task))
dc52ddc0e   Matt Helsley   container freezer...
230
231
  			nfrozen++;
  	}
2d3cbf8bc   Tomasz Buchert   cgroup_freezer: u...
232
233
234
235
236
237
238
239
  	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...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
  	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...
255
  	if (state == CGROUP_FREEZING) {
dc52ddc0e   Matt Helsley   container freezer...
256
257
  		/* We change from FREEZING to FROZEN lazily if the cgroup was
  		 * only partially frozen when we exitted write. */
2d3cbf8bc   Tomasz Buchert   cgroup_freezer: u...
258
  		update_if_frozen(cgroup, freezer);
dc52ddc0e   Matt Helsley   container freezer...
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
  		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;
81dcf33c2   Matt Helsley   container freezer...
275
  	freezer->state = CGROUP_FREEZING;
dc52ddc0e   Matt Helsley   container freezer...
276
277
278
279
  	cgroup_iter_start(cgroup, &it);
  	while ((task = cgroup_iter_next(cgroup, &it))) {
  		if (!freeze_task(task, true))
  			continue;
2d3cbf8bc   Tomasz Buchert   cgroup_freezer: u...
280
  		if (frozen(task))
dc52ddc0e   Matt Helsley   container freezer...
281
282
283
284
285
286
287
288
  			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...
289
  static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
dc52ddc0e   Matt Helsley   container freezer...
290
291
292
293
294
295
  {
  	struct cgroup_iter it;
  	struct task_struct *task;
  
  	cgroup_iter_start(cgroup, &it);
  	while ((task = cgroup_iter_next(cgroup, &it))) {
00c2e63c3   Li Zefan   freezer_cg: use t...
296
  		thaw_process(task);
dc52ddc0e   Matt Helsley   container freezer...
297
298
  	}
  	cgroup_iter_end(cgroup, &it);
dc52ddc0e   Matt Helsley   container freezer...
299

00c2e63c3   Li Zefan   freezer_cg: use t...
300
  	freezer->state = CGROUP_THAWED;
dc52ddc0e   Matt Helsley   container freezer...
301
302
303
304
305
306
307
308
309
  }
  
  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...
310

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

2d3cbf8bc   Tomasz Buchert   cgroup_freezer: u...
313
  	update_if_frozen(cgroup, freezer);
dc52ddc0e   Matt Helsley   container freezer...
314
315
  	if (goal_state == freezer->state)
  		goto out;
51308ee59   Li Zefan   freezer_cg: simpl...
316
317
  
  	switch (goal_state) {
81dcf33c2   Matt Helsley   container freezer...
318
  	case CGROUP_THAWED:
51308ee59   Li Zefan   freezer_cg: simpl...
319
  		unfreeze_cgroup(cgroup, freezer);
dc52ddc0e   Matt Helsley   container freezer...
320
  		break;
81dcf33c2   Matt Helsley   container freezer...
321
  	case CGROUP_FROZEN:
51308ee59   Li Zefan   freezer_cg: simpl...
322
  		retval = try_to_freeze_cgroup(cgroup, freezer);
dc52ddc0e   Matt Helsley   container freezer...
323
324
  		break;
  	default:
51308ee59   Li Zefan   freezer_cg: simpl...
325
  		BUG();
dc52ddc0e   Matt Helsley   container freezer...
326
327
328
329
330
331
332
333
334
335
336
337
338
  	}
  out:
  	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...
339
340
341
342
  	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...
343
  	else
3b1b3f6e5   Li Zefan   freezer_cg: disab...
344
  		return -EINVAL;
dc52ddc0e   Matt Helsley   container freezer...
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
  
  	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",
  		.read_seq_string = freezer_read,
  		.write_string = freezer_write,
  	},
  };
  
  static int freezer_populate(struct cgroup_subsys *ss, struct cgroup *cgroup)
  {
3b1b3f6e5   Li Zefan   freezer_cg: disab...
363
364
  	if (!cgroup->parent)
  		return 0;
dc52ddc0e   Matt Helsley   container freezer...
365
366
367
368
369
370
371
372
373
374
  	return cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files));
  }
  
  struct cgroup_subsys freezer_subsys = {
  	.name		= "freezer",
  	.create		= freezer_create,
  	.destroy	= freezer_destroy,
  	.populate	= freezer_populate,
  	.subsys_id	= freezer_subsys_id,
  	.can_attach	= freezer_can_attach,
f780bdb7c   Ben Blum   cgroups: add per-...
375
376
377
  	.can_attach_task = freezer_can_attach_task,
  	.pre_attach	= NULL,
  	.attach_task	= NULL,
dc52ddc0e   Matt Helsley   container freezer...
378
379
380
381
  	.attach		= NULL,
  	.fork		= freezer_fork,
  	.exit		= NULL,
  };