Blame view

kernel/cgroup_freezer.c 9.39 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
129
130
131
132
133
134
135
136
137
138
   *     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...
139
  	freezer->state = CGROUP_THAWED;
dc52ddc0e   Matt Helsley   container freezer...
140
141
142
143
144
145
  	return &freezer->css;
  }
  
  static void freezer_destroy(struct cgroup_subsys *ss,
  			    struct cgroup *cgroup)
  {
a3201227f   Tejun Heo   freezer: make fre...
146
147
148
149
150
  	struct freezer *freezer = cgroup_freezer(cgroup);
  
  	if (freezer->state != CGROUP_THAWED)
  		atomic_dec(&system_freezing_cnt);
  	kfree(freezer);
dc52ddc0e   Matt Helsley   container freezer...
151
  }
884a45d96   Michal Hocko   cgroup_freezer: f...
152
153
154
155
156
157
  /* 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...
158
159
160
161
162
  /*
   * 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...
163
164
  static int freezer_can_attach(struct cgroup_subsys *ss,
  			      struct cgroup *new_cgroup,
2f7ee5691   Tejun Heo   cgroup: introduce...
165
  			      struct cgroup_taskset *tset)
dc52ddc0e   Matt Helsley   container freezer...
166
167
  {
  	struct freezer *freezer;
bb9d97b6d   Tejun Heo   cgroup: don't use...
168
  	struct task_struct *task;
957a4eeaf   Matt Helsley   container freezer...
169

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

0bdba580a   Tomasz Buchert   cgroup_freezer: f...
177
178
  	freezer = cgroup_freezer(new_cgroup);
  	if (freezer->state != CGROUP_THAWED)
957a4eeaf   Matt Helsley   container freezer...
179
  		return -EBUSY;
dc52ddc0e   Matt Helsley   container freezer...
180

f780bdb7c   Ben Blum   cgroups: add per-...
181
182
  	return 0;
  }
dc52ddc0e   Matt Helsley   container freezer...
183
184
185
  static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task)
  {
  	struct freezer *freezer;
687446760   Li Zefan   freezer_cg: remov...
186
187
188
189
  	/*
  	 * 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...
190
191
  	 * function call.  Nevertheless, apply RCU read-side critical
  	 * section to suppress RCU lockdep false positives.
687446760   Li Zefan   freezer_cg: remov...
192
  	 */
8b46f8808   Paul E. McKenney   rcu: Fix RCU lock...
193
  	rcu_read_lock();
dc52ddc0e   Matt Helsley   container freezer...
194
  	freezer = task_freezer(task);
8b46f8808   Paul E. McKenney   rcu: Fix RCU lock...
195
  	rcu_read_unlock();
dc52ddc0e   Matt Helsley   container freezer...
196

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

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

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

dc52ddc0e   Matt Helsley   container freezer...
324
325
326
327
328
329
330
331
332
333
334
  	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...
335
336
337
338
  	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...
339
  	else
3b1b3f6e5   Li Zefan   freezer_cg: disab...
340
  		return -EINVAL;
dc52ddc0e   Matt Helsley   container freezer...
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
  
  	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...
359
360
  	if (!cgroup->parent)
  		return 0;
dc52ddc0e   Matt Helsley   container freezer...
361
362
363
364
365
366
367
368
369
370
  	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,
dc52ddc0e   Matt Helsley   container freezer...
371
  	.fork		= freezer_fork,
dc52ddc0e   Matt Helsley   container freezer...
372
  };