Blame view

kernel/cgroup_freezer.c 9.78 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);
  }
5a7aadfe2   Matt Helsley   Freezer: Fix bugg...
50
  int cgroup_freezing_or_frozen(struct task_struct *task)
dc52ddc0e   Matt Helsley   container freezer...
51
52
53
54
55
56
  {
  	struct freezer *freezer;
  	enum freezer_state state;
  
  	task_lock(task);
  	freezer = task_freezer(task);
5a7aadfe2   Matt Helsley   Freezer: Fix bugg...
57
58
59
60
  	if (!freezer->css.cgroup->parent)
  		state = CGROUP_THAWED; /* root cgroup can't be frozen */
  	else
  		state = freezer->state;
dc52ddc0e   Matt Helsley   container freezer...
61
  	task_unlock(task);
5a7aadfe2   Matt Helsley   Freezer: Fix bugg...
62
  	return (state == CGROUP_FREEZING) || (state == CGROUP_FROZEN);
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
159
  /* 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));
  }
dc52ddc0e   Matt Helsley   container freezer...
160

957a4eeaf   Matt Helsley   container freezer...
161
162
163
164
165
  /*
   * 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...
166
167
  static int freezer_can_attach(struct cgroup_subsys *ss,
  			      struct cgroup *new_cgroup,
be367d099   Ben Blum   cgroups: let ss->...
168
  			      struct task_struct *task, bool threadgroup)
dc52ddc0e   Matt Helsley   container freezer...
169
170
  {
  	struct freezer *freezer;
957a4eeaf   Matt Helsley   container freezer...
171

80a6a2cf3   Li Zefan   freezer_cg: remov...
172
173
174
175
176
177
  	/*
  	 * Anything frozen can't move or be moved to/from.
  	 *
  	 * Since orig_freezer->state == FROZEN means that @task has been
  	 * frozen, so it's sufficient to check the latter condition.
  	 */
957a4eeaf   Matt Helsley   container freezer...
178
179
180
  
  	if (is_task_frozen_enough(task))
  		return -EBUSY;
dc52ddc0e   Matt Helsley   container freezer...
181

dc52ddc0e   Matt Helsley   container freezer...
182
  	freezer = cgroup_freezer(new_cgroup);
81dcf33c2   Matt Helsley   container freezer...
183
  	if (freezer->state == CGROUP_FROZEN)
957a4eeaf   Matt Helsley   container freezer...
184
  		return -EBUSY;
be367d099   Ben Blum   cgroups: let ss->...
185
186
187
188
189
190
191
192
193
194
195
196
  	if (threadgroup) {
  		struct task_struct *c;
  
  		rcu_read_lock();
  		list_for_each_entry_rcu(c, &task->thread_group, thread_group) {
  			if (is_task_frozen_enough(c)) {
  				rcu_read_unlock();
  				return -EBUSY;
  			}
  		}
  		rcu_read_unlock();
  	}
80a6a2cf3   Li Zefan   freezer_cg: remov...
197
  	return 0;
dc52ddc0e   Matt Helsley   container freezer...
198
199
200
201
202
  }
  
  static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task)
  {
  	struct freezer *freezer;
687446760   Li Zefan   freezer_cg: remov...
203
204
205
206
  	/*
  	 * 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...
207
208
  	 * function call.  Nevertheless, apply RCU read-side critical
  	 * section to suppress RCU lockdep false positives.
687446760   Li Zefan   freezer_cg: remov...
209
  	 */
8b46f8808   Paul E. McKenney   rcu: Fix RCU lock...
210
  	rcu_read_lock();
dc52ddc0e   Matt Helsley   container freezer...
211
  	freezer = task_freezer(task);
8b46f8808   Paul E. McKenney   rcu: Fix RCU lock...
212
  	rcu_read_unlock();
dc52ddc0e   Matt Helsley   container freezer...
213

3b1b3f6e5   Li Zefan   freezer_cg: disab...
214
215
216
217
218
219
  	/*
  	 * The root cgroup is non-freezable, so we can skip the
  	 * following check.
  	 */
  	if (!freezer->css.cgroup->parent)
  		return;
dc52ddc0e   Matt Helsley   container freezer...
220
  	spin_lock_irq(&freezer->lock);
7ccb97437   Li Zefan   freezer_cg: fix i...
221
  	BUG_ON(freezer->state == CGROUP_FROZEN);
81dcf33c2   Matt Helsley   container freezer...
222
223
  	/* Locking avoids race with FREEZING -> THAWED transitions. */
  	if (freezer->state == CGROUP_FREEZING)
dc52ddc0e   Matt Helsley   container freezer...
224
225
226
227
228
229
230
  		freeze_task(task, true);
  	spin_unlock_irq(&freezer->lock);
  }
  
  /*
   * caller must hold freezer->lock
   */
1aece3483   Matt Helsley   container freezer...
231
232
  static void update_freezer_state(struct cgroup *cgroup,
  				 struct freezer *freezer)
dc52ddc0e   Matt Helsley   container freezer...
233
234
235
236
237
238
239
240
  {
  	struct cgroup_iter it;
  	struct task_struct *task;
  	unsigned int nfrozen = 0, ntotal = 0;
  
  	cgroup_iter_start(cgroup, &it);
  	while ((task = cgroup_iter_next(cgroup, &it))) {
  		ntotal++;
957a4eeaf   Matt Helsley   container freezer...
241
  		if (is_task_frozen_enough(task))
dc52ddc0e   Matt Helsley   container freezer...
242
243
244
245
246
247
248
249
250
  			nfrozen++;
  	}
  
  	/*
  	 * Transition to FROZEN when no new tasks can be added ensures
  	 * that we never exist in the FROZEN state while there are unfrozen
  	 * tasks.
  	 */
  	if (nfrozen == ntotal)
81dcf33c2   Matt Helsley   container freezer...
251
  		freezer->state = CGROUP_FROZEN;
1aece3483   Matt Helsley   container freezer...
252
253
254
255
  	else if (nfrozen > 0)
  		freezer->state = CGROUP_FREEZING;
  	else
  		freezer->state = CGROUP_THAWED;
dc52ddc0e   Matt Helsley   container freezer...
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
  	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...
271
  	if (state == CGROUP_FREEZING) {
dc52ddc0e   Matt Helsley   container freezer...
272
273
  		/* We change from FREEZING to FROZEN lazily if the cgroup was
  		 * only partially frozen when we exitted write. */
1aece3483   Matt Helsley   container freezer...
274
  		update_freezer_state(cgroup, freezer);
dc52ddc0e   Matt Helsley   container freezer...
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
  		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...
291
  	freezer->state = CGROUP_FREEZING;
dc52ddc0e   Matt Helsley   container freezer...
292
293
294
295
  	cgroup_iter_start(cgroup, &it);
  	while ((task = cgroup_iter_next(cgroup, &it))) {
  		if (!freeze_task(task, true))
  			continue;
957a4eeaf   Matt Helsley   container freezer...
296
  		if (is_task_frozen_enough(task))
dc52ddc0e   Matt Helsley   container freezer...
297
298
299
300
301
302
303
304
  			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...
305
  static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
dc52ddc0e   Matt Helsley   container freezer...
306
307
308
309
310
311
  {
  	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...
312
  		thaw_process(task);
dc52ddc0e   Matt Helsley   container freezer...
313
314
  	}
  	cgroup_iter_end(cgroup, &it);
dc52ddc0e   Matt Helsley   container freezer...
315

00c2e63c3   Li Zefan   freezer_cg: use t...
316
  	freezer->state = CGROUP_THAWED;
dc52ddc0e   Matt Helsley   container freezer...
317
318
319
320
321
322
323
324
325
  }
  
  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...
326

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

1aece3483   Matt Helsley   container freezer...
329
  	update_freezer_state(cgroup, freezer);
dc52ddc0e   Matt Helsley   container freezer...
330
331
  	if (goal_state == freezer->state)
  		goto out;
51308ee59   Li Zefan   freezer_cg: simpl...
332
333
  
  	switch (goal_state) {
81dcf33c2   Matt Helsley   container freezer...
334
  	case CGROUP_THAWED:
51308ee59   Li Zefan   freezer_cg: simpl...
335
  		unfreeze_cgroup(cgroup, freezer);
dc52ddc0e   Matt Helsley   container freezer...
336
  		break;
81dcf33c2   Matt Helsley   container freezer...
337
  	case CGROUP_FROZEN:
51308ee59   Li Zefan   freezer_cg: simpl...
338
  		retval = try_to_freeze_cgroup(cgroup, freezer);
dc52ddc0e   Matt Helsley   container freezer...
339
340
  		break;
  	default:
51308ee59   Li Zefan   freezer_cg: simpl...
341
  		BUG();
dc52ddc0e   Matt Helsley   container freezer...
342
343
344
345
346
347
348
349
350
351
352
353
354
  	}
  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...
355
356
357
358
  	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...
359
  	else
3b1b3f6e5   Li Zefan   freezer_cg: disab...
360
  		return -EINVAL;
dc52ddc0e   Matt Helsley   container freezer...
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
  
  	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...
379
380
  	if (!cgroup->parent)
  		return 0;
dc52ddc0e   Matt Helsley   container freezer...
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  	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,
  	.attach		= NULL,
  	.fork		= freezer_fork,
  	.exit		= NULL,
  };