Skip to content

Commit aa226ff

Browse files
committed
cgroup: make sure a parent css isn't offlined before its children
There are three subsystem callbacks in css shutdown path - css_offline(), css_released() and css_free(). Except for css_released(), cgroup core didn't guarantee the order of invocation. css_offline() or css_free() could be called on a parent css before its children. This behavior is unexpected and led to bugs in cpu and memory controller. This patch updates offline path so that a parent css is never offlined before its children. Each css keeps online_cnt which reaches zero iff itself and all its children are offline and offline_css() is invoked only after online_cnt reaches zero. This fixes the memory controller bug and allows the fix for cpu controller. Signed-off-by: Tejun Heo <[email protected]> Reported-and-tested-by: Christian Borntraeger <[email protected]> Reported-by: Brian Christiansen <[email protected]> Link: http://lkml.kernel.org/g/[email protected] Link: http://lkml.kernel.org/g/CAKB58ikDkzc8REt31WBkD99+hxNzjK4+FBmhkgS+NVrC9vjMSg@mail.gmail.com Cc: Heiko Carstens <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: [email protected]
1 parent e93ad19 commit aa226ff

File tree

2 files changed

+23
-5
lines changed

2 files changed

+23
-5
lines changed

include/linux/cgroup-defs.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ struct cgroup_subsys_state {
127127
*/
128128
u64 serial_nr;
129129

130+
/*
131+
* Incremented by online self and children. Used to guarantee that
132+
* parents are not offlined before their children.
133+
*/
134+
atomic_t online_cnt;
135+
130136
/* percpu_ref killing and RCU release */
131137
struct rcu_head rcu_head;
132138
struct work_struct destroy_work;

kernel/cgroup.c

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4760,6 +4760,7 @@ static void init_and_link_css(struct cgroup_subsys_state *css,
47604760
INIT_LIST_HEAD(&css->sibling);
47614761
INIT_LIST_HEAD(&css->children);
47624762
css->serial_nr = css_serial_nr_next++;
4763+
atomic_set(&css->online_cnt, 0);
47634764

47644765
if (cgroup_parent(cgrp)) {
47654766
css->parent = cgroup_css(cgroup_parent(cgrp), ss);
@@ -4782,6 +4783,10 @@ static int online_css(struct cgroup_subsys_state *css)
47824783
if (!ret) {
47834784
css->flags |= CSS_ONLINE;
47844785
rcu_assign_pointer(css->cgroup->subsys[ss->id], css);
4786+
4787+
atomic_inc(&css->online_cnt);
4788+
if (css->parent)
4789+
atomic_inc(&css->parent->online_cnt);
47854790
}
47864791
return ret;
47874792
}
@@ -5019,10 +5024,15 @@ static void css_killed_work_fn(struct work_struct *work)
50195024
container_of(work, struct cgroup_subsys_state, destroy_work);
50205025

50215026
mutex_lock(&cgroup_mutex);
5022-
offline_css(css);
5023-
mutex_unlock(&cgroup_mutex);
50245027

5025-
css_put(css);
5028+
do {
5029+
offline_css(css);
5030+
css_put(css);
5031+
/* @css can't go away while we're holding cgroup_mutex */
5032+
css = css->parent;
5033+
} while (css && atomic_dec_and_test(&css->online_cnt));
5034+
5035+
mutex_unlock(&cgroup_mutex);
50265036
}
50275037

50285038
/* css kill confirmation processing requires process context, bounce */
@@ -5031,8 +5041,10 @@ static void css_killed_ref_fn(struct percpu_ref *ref)
50315041
struct cgroup_subsys_state *css =
50325042
container_of(ref, struct cgroup_subsys_state, refcnt);
50335043

5034-
INIT_WORK(&css->destroy_work, css_killed_work_fn);
5035-
queue_work(cgroup_destroy_wq, &css->destroy_work);
5044+
if (atomic_dec_and_test(&css->online_cnt)) {
5045+
INIT_WORK(&css->destroy_work, css_killed_work_fn);
5046+
queue_work(cgroup_destroy_wq, &css->destroy_work);
5047+
}
50365048
}
50375049

50385050
/**

0 commit comments

Comments
 (0)