aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/cgroup_freezer.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/cgroup_freezer.c')
-rw-r--r--kernel/cgroup_freezer.c138
1 files changed, 59 insertions, 79 deletions
diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c
index 2bc4a2256444..a79e40f9d700 100644
--- a/kernel/cgroup_freezer.c
+++ b/kernel/cgroup_freezer.c
@@ -21,6 +21,7 @@
21#include <linux/uaccess.h> 21#include <linux/uaccess.h>
22#include <linux/freezer.h> 22#include <linux/freezer.h>
23#include <linux/seq_file.h> 23#include <linux/seq_file.h>
24#include <linux/mutex.h>
24 25
25/* 26/*
26 * A cgroup is freezing if any FREEZING flags are set. FREEZING_SELF is 27 * A cgroup is freezing if any FREEZING flags are set. FREEZING_SELF is
@@ -42,9 +43,10 @@ enum freezer_state_flags {
42struct freezer { 43struct freezer {
43 struct cgroup_subsys_state css; 44 struct cgroup_subsys_state css;
44 unsigned int state; 45 unsigned int state;
45 spinlock_t lock;
46}; 46};
47 47
48static DEFINE_MUTEX(freezer_mutex);
49
48static inline struct freezer *css_freezer(struct cgroup_subsys_state *css) 50static inline struct freezer *css_freezer(struct cgroup_subsys_state *css)
49{ 51{
50 return css ? container_of(css, struct freezer, css) : NULL; 52 return css ? container_of(css, struct freezer, css) : NULL;
@@ -57,7 +59,7 @@ static inline struct freezer *task_freezer(struct task_struct *task)
57 59
58static struct freezer *parent_freezer(struct freezer *freezer) 60static struct freezer *parent_freezer(struct freezer *freezer)
59{ 61{
60 return css_freezer(css_parent(&freezer->css)); 62 return css_freezer(freezer->css.parent);
61} 63}
62 64
63bool cgroup_freezing(struct task_struct *task) 65bool cgroup_freezing(struct task_struct *task)
@@ -71,10 +73,6 @@ bool cgroup_freezing(struct task_struct *task)
71 return ret; 73 return ret;
72} 74}
73 75
74/*
75 * cgroups_write_string() limits the size of freezer state strings to
76 * CGROUP_LOCAL_BUFFER_SIZE
77 */
78static const char *freezer_state_strs(unsigned int state) 76static const char *freezer_state_strs(unsigned int state)
79{ 77{
80 if (state & CGROUP_FROZEN) 78 if (state & CGROUP_FROZEN)
@@ -93,7 +91,6 @@ freezer_css_alloc(struct cgroup_subsys_state *parent_css)
93 if (!freezer) 91 if (!freezer)
94 return ERR_PTR(-ENOMEM); 92 return ERR_PTR(-ENOMEM);
95 93
96 spin_lock_init(&freezer->lock);
97 return &freezer->css; 94 return &freezer->css;
98} 95}
99 96
@@ -110,14 +107,7 @@ static int freezer_css_online(struct cgroup_subsys_state *css)
110 struct freezer *freezer = css_freezer(css); 107 struct freezer *freezer = css_freezer(css);
111 struct freezer *parent = parent_freezer(freezer); 108 struct freezer *parent = parent_freezer(freezer);
112 109
113 /* 110 mutex_lock(&freezer_mutex);
114 * The following double locking and freezing state inheritance
115 * guarantee that @cgroup can never escape ancestors' freezing
116 * states. See css_for_each_descendant_pre() for details.
117 */
118 if (parent)
119 spin_lock_irq(&parent->lock);
120 spin_lock_nested(&freezer->lock, SINGLE_DEPTH_NESTING);
121 111
122 freezer->state |= CGROUP_FREEZER_ONLINE; 112 freezer->state |= CGROUP_FREEZER_ONLINE;
123 113
@@ -126,10 +116,7 @@ static int freezer_css_online(struct cgroup_subsys_state *css)
126 atomic_inc(&system_freezing_cnt); 116 atomic_inc(&system_freezing_cnt);
127 } 117 }
128 118
129 spin_unlock(&freezer->lock); 119 mutex_unlock(&freezer_mutex);
130 if (parent)
131 spin_unlock_irq(&parent->lock);
132
133 return 0; 120 return 0;
134} 121}
135 122
@@ -144,14 +131,14 @@ static void freezer_css_offline(struct cgroup_subsys_state *css)
144{ 131{
145 struct freezer *freezer = css_freezer(css); 132 struct freezer *freezer = css_freezer(css);
146 133
147 spin_lock_irq(&freezer->lock); 134 mutex_lock(&freezer_mutex);
148 135
149 if (freezer->state & CGROUP_FREEZING) 136 if (freezer->state & CGROUP_FREEZING)
150 atomic_dec(&system_freezing_cnt); 137 atomic_dec(&system_freezing_cnt);
151 138
152 freezer->state = 0; 139 freezer->state = 0;
153 140
154 spin_unlock_irq(&freezer->lock); 141 mutex_unlock(&freezer_mutex);
155} 142}
156 143
157static void freezer_css_free(struct cgroup_subsys_state *css) 144static void freezer_css_free(struct cgroup_subsys_state *css)
@@ -175,7 +162,7 @@ static void freezer_attach(struct cgroup_subsys_state *new_css,
175 struct task_struct *task; 162 struct task_struct *task;
176 bool clear_frozen = false; 163 bool clear_frozen = false;
177 164
178 spin_lock_irq(&freezer->lock); 165 mutex_lock(&freezer_mutex);
179 166
180 /* 167 /*
181 * Make the new tasks conform to the current state of @new_css. 168 * Make the new tasks conform to the current state of @new_css.
@@ -197,21 +184,13 @@ static void freezer_attach(struct cgroup_subsys_state *new_css,
197 } 184 }
198 } 185 }
199 186
200 spin_unlock_irq(&freezer->lock); 187 /* propagate FROZEN clearing upwards */
201
202 /*
203 * Propagate FROZEN clearing upwards. We may race with
204 * update_if_frozen(), but as long as both work bottom-up, either
205 * update_if_frozen() sees child's FROZEN cleared or we clear the
206 * parent's FROZEN later. No parent w/ !FROZEN children can be
207 * left FROZEN.
208 */
209 while (clear_frozen && (freezer = parent_freezer(freezer))) { 188 while (clear_frozen && (freezer = parent_freezer(freezer))) {
210 spin_lock_irq(&freezer->lock);
211 freezer->state &= ~CGROUP_FROZEN; 189 freezer->state &= ~CGROUP_FROZEN;
212 clear_frozen = freezer->state & CGROUP_FREEZING; 190 clear_frozen = freezer->state & CGROUP_FREEZING;
213 spin_unlock_irq(&freezer->lock);
214 } 191 }
192
193 mutex_unlock(&freezer_mutex);
215} 194}
216 195
217/** 196/**
@@ -228,9 +207,6 @@ static void freezer_fork(struct task_struct *task)
228{ 207{
229 struct freezer *freezer; 208 struct freezer *freezer;
230 209
231 rcu_read_lock();
232 freezer = task_freezer(task);
233
234 /* 210 /*
235 * The root cgroup is non-freezable, so we can skip locking the 211 * The root cgroup is non-freezable, so we can skip locking the
236 * freezer. This is safe regardless of race with task migration. 212 * freezer. This is safe regardless of race with task migration.
@@ -238,24 +214,18 @@ static void freezer_fork(struct task_struct *task)
238 * to do. If we lost and root is the new cgroup, noop is still the 214 * to do. If we lost and root is the new cgroup, noop is still the
239 * right thing to do. 215 * right thing to do.
240 */ 216 */
241 if (!parent_freezer(freezer)) 217 if (task_css_is_root(task, freezer_cgrp_id))
242 goto out; 218 return;
243 219
244 /* 220 mutex_lock(&freezer_mutex);
245 * Grab @freezer->lock and freeze @task after verifying @task still 221 rcu_read_lock();
246 * belongs to @freezer and it's freezing. The former is for the 222
247 * case where we have raced against task migration and lost and 223 freezer = task_freezer(task);
248 * @task is already in a different cgroup which may not be frozen. 224 if (freezer->state & CGROUP_FREEZING)
249 * This isn't strictly necessary as freeze_task() is allowed to be
250 * called spuriously but let's do it anyway for, if nothing else,
251 * documentation.
252 */
253 spin_lock_irq(&freezer->lock);
254 if (freezer == task_freezer(task) && (freezer->state & CGROUP_FREEZING))
255 freeze_task(task); 225 freeze_task(task);
256 spin_unlock_irq(&freezer->lock); 226
257out:
258 rcu_read_unlock(); 227 rcu_read_unlock();
228 mutex_unlock(&freezer_mutex);
259} 229}
260 230
261/** 231/**
@@ -281,22 +251,24 @@ static void update_if_frozen(struct cgroup_subsys_state *css)
281 struct css_task_iter it; 251 struct css_task_iter it;
282 struct task_struct *task; 252 struct task_struct *task;
283 253
284 WARN_ON_ONCE(!rcu_read_lock_held()); 254 lockdep_assert_held(&freezer_mutex);
285
286 spin_lock_irq(&freezer->lock);
287 255
288 if (!(freezer->state & CGROUP_FREEZING) || 256 if (!(freezer->state & CGROUP_FREEZING) ||
289 (freezer->state & CGROUP_FROZEN)) 257 (freezer->state & CGROUP_FROZEN))
290 goto out_unlock; 258 return;
291 259
292 /* are all (live) children frozen? */ 260 /* are all (live) children frozen? */
261 rcu_read_lock();
293 css_for_each_child(pos, css) { 262 css_for_each_child(pos, css) {
294 struct freezer *child = css_freezer(pos); 263 struct freezer *child = css_freezer(pos);
295 264
296 if ((child->state & CGROUP_FREEZER_ONLINE) && 265 if ((child->state & CGROUP_FREEZER_ONLINE) &&
297 !(child->state & CGROUP_FROZEN)) 266 !(child->state & CGROUP_FROZEN)) {
298 goto out_unlock; 267 rcu_read_unlock();
268 return;
269 }
299 } 270 }
271 rcu_read_unlock();
300 272
301 /* are all tasks frozen? */ 273 /* are all tasks frozen? */
302 css_task_iter_start(css, &it); 274 css_task_iter_start(css, &it);
@@ -317,21 +289,29 @@ static void update_if_frozen(struct cgroup_subsys_state *css)
317 freezer->state |= CGROUP_FROZEN; 289 freezer->state |= CGROUP_FROZEN;
318out_iter_end: 290out_iter_end:
319 css_task_iter_end(&it); 291 css_task_iter_end(&it);
320out_unlock:
321 spin_unlock_irq(&freezer->lock);
322} 292}
323 293
324static int freezer_read(struct seq_file *m, void *v) 294static int freezer_read(struct seq_file *m, void *v)
325{ 295{
326 struct cgroup_subsys_state *css = seq_css(m), *pos; 296 struct cgroup_subsys_state *css = seq_css(m), *pos;
327 297
298 mutex_lock(&freezer_mutex);
328 rcu_read_lock(); 299 rcu_read_lock();
329 300
330 /* update states bottom-up */ 301 /* update states bottom-up */
331 css_for_each_descendant_post(pos, css) 302 css_for_each_descendant_post(pos, css) {
303 if (!css_tryget_online(pos))
304 continue;
305 rcu_read_unlock();
306
332 update_if_frozen(pos); 307 update_if_frozen(pos);
333 308
309 rcu_read_lock();
310 css_put(pos);
311 }
312
334 rcu_read_unlock(); 313 rcu_read_unlock();
314 mutex_unlock(&freezer_mutex);
335 315
336 seq_puts(m, freezer_state_strs(css_freezer(css)->state)); 316 seq_puts(m, freezer_state_strs(css_freezer(css)->state));
337 seq_putc(m, '\n'); 317 seq_putc(m, '\n');
@@ -373,7 +353,7 @@ static void freezer_apply_state(struct freezer *freezer, bool freeze,
373 unsigned int state) 353 unsigned int state)
374{ 354{
375 /* also synchronizes against task migration, see freezer_attach() */ 355 /* also synchronizes against task migration, see freezer_attach() */
376 lockdep_assert_held(&freezer->lock); 356 lockdep_assert_held(&freezer_mutex);
377 357
378 if (!(freezer->state & CGROUP_FREEZER_ONLINE)) 358 if (!(freezer->state & CGROUP_FREEZER_ONLINE))
379 return; 359 return;
@@ -414,47 +394,47 @@ static void freezer_change_state(struct freezer *freezer, bool freeze)
414 * descendant will try to inherit its parent's FREEZING state as 394 * descendant will try to inherit its parent's FREEZING state as
415 * CGROUP_FREEZING_PARENT. 395 * CGROUP_FREEZING_PARENT.
416 */ 396 */
397 mutex_lock(&freezer_mutex);
417 rcu_read_lock(); 398 rcu_read_lock();
418 css_for_each_descendant_pre(pos, &freezer->css) { 399 css_for_each_descendant_pre(pos, &freezer->css) {
419 struct freezer *pos_f = css_freezer(pos); 400 struct freezer *pos_f = css_freezer(pos);
420 struct freezer *parent = parent_freezer(pos_f); 401 struct freezer *parent = parent_freezer(pos_f);
421 402
422 spin_lock_irq(&pos_f->lock); 403 if (!css_tryget_online(pos))
404 continue;
405 rcu_read_unlock();
423 406
424 if (pos_f == freezer) { 407 if (pos_f == freezer)
425 freezer_apply_state(pos_f, freeze, 408 freezer_apply_state(pos_f, freeze,
426 CGROUP_FREEZING_SELF); 409 CGROUP_FREEZING_SELF);
427 } else { 410 else
428 /*
429 * Our update to @parent->state is already visible
430 * which is all we need. No need to lock @parent.
431 * For more info on synchronization, see
432 * freezer_post_create().
433 */
434 freezer_apply_state(pos_f, 411 freezer_apply_state(pos_f,
435 parent->state & CGROUP_FREEZING, 412 parent->state & CGROUP_FREEZING,
436 CGROUP_FREEZING_PARENT); 413 CGROUP_FREEZING_PARENT);
437 }
438 414
439 spin_unlock_irq(&pos_f->lock); 415 rcu_read_lock();
416 css_put(pos);
440 } 417 }
441 rcu_read_unlock(); 418 rcu_read_unlock();
419 mutex_unlock(&freezer_mutex);
442} 420}
443 421
444static int freezer_write(struct cgroup_subsys_state *css, struct cftype *cft, 422static ssize_t freezer_write(struct kernfs_open_file *of,
445 char *buffer) 423 char *buf, size_t nbytes, loff_t off)
446{ 424{
447 bool freeze; 425 bool freeze;
448 426
449 if (strcmp(buffer, freezer_state_strs(0)) == 0) 427 buf = strstrip(buf);
428
429 if (strcmp(buf, freezer_state_strs(0)) == 0)
450 freeze = false; 430 freeze = false;
451 else if (strcmp(buffer, freezer_state_strs(CGROUP_FROZEN)) == 0) 431 else if (strcmp(buf, freezer_state_strs(CGROUP_FROZEN)) == 0)
452 freeze = true; 432 freeze = true;
453 else 433 else
454 return -EINVAL; 434 return -EINVAL;
455 435
456 freezer_change_state(css_freezer(css), freeze); 436 freezer_change_state(css_freezer(of_css(of)), freeze);
457 return 0; 437 return nbytes;
458} 438}
459 439
460static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css, 440static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css,
@@ -478,7 +458,7 @@ static struct cftype files[] = {
478 .name = "state", 458 .name = "state",
479 .flags = CFTYPE_NOT_ON_ROOT, 459 .flags = CFTYPE_NOT_ON_ROOT,
480 .seq_show = freezer_read, 460 .seq_show = freezer_read,
481 .write_string = freezer_write, 461 .write = freezer_write,
482 }, 462 },
483 { 463 {
484 .name = "self_freezing", 464 .name = "self_freezing",