diff options
author | Tejun Heo <tj@kernel.org> | 2012-10-16 18:03:14 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2012-10-16 18:03:14 -0400 |
commit | 5edee61edeaaebafe584f8fb7074c1ef4658596b (patch) | |
tree | 23e6ee3581eb0009b3c2a2686c25fdba538219de /kernel/fork.c | |
parent | ddffeb8c4d0331609ef2581d84de4d763607bd37 (diff) |
cgroup: cgroup_subsys->fork() should be called after the task is added to css_set
cgroup core has a bug which violates a basic rule about event
notifications - when a new entity needs to be added, you add that to
the notification list first and then make the new entity conform to
the current state. If done in the reverse order, an event happening
inbetween will be lost.
cgroup_subsys->fork() is invoked way before the new task is added to
the css_set. Currently, cgroup_freezer is the only user of ->fork()
and uses it to make new tasks conform to the current state of the
freezer. If FROZEN state is requested while fork is in progress
between cgroup_fork_callbacks() and cgroup_post_fork(), the child
could escape freezing - the cgroup isn't frozen when ->fork() is
called and the freezer couldn't see the new task on the css_set.
This patch moves cgroup_subsys->fork() invocation to
cgroup_post_fork() after the new task is added to the css_set.
cgroup_fork_callbacks() is removed.
Because now a task may be migrated during cgroup_subsys->fork(),
freezer_fork() is updated so that it adheres to the usual RCU locking
and the rather pointless comment on why locking can be different there
is removed (if it doesn't make anything simpler, why even bother?).
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Rafael J. Wysocki <rjw@sisk.pl>
Cc: stable@vger.kernel.org
Diffstat (limited to 'kernel/fork.c')
-rw-r--r-- | kernel/fork.c | 9 |
1 files changed, 1 insertions, 8 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index 8b20ab7d3aa2..acc4cb62f32f 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1135,7 +1135,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1135 | { | 1135 | { |
1136 | int retval; | 1136 | int retval; |
1137 | struct task_struct *p; | 1137 | struct task_struct *p; |
1138 | int cgroup_callbacks_done = 0; | ||
1139 | 1138 | ||
1140 | if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS)) | 1139 | if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS)) |
1141 | return ERR_PTR(-EINVAL); | 1140 | return ERR_PTR(-EINVAL); |
@@ -1393,12 +1392,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1393 | INIT_LIST_HEAD(&p->thread_group); | 1392 | INIT_LIST_HEAD(&p->thread_group); |
1394 | p->task_works = NULL; | 1393 | p->task_works = NULL; |
1395 | 1394 | ||
1396 | /* Now that the task is set up, run cgroup callbacks if | ||
1397 | * necessary. We need to run them before the task is visible | ||
1398 | * on the tasklist. */ | ||
1399 | cgroup_fork_callbacks(p); | ||
1400 | cgroup_callbacks_done = 1; | ||
1401 | |||
1402 | /* Need tasklist lock for parent etc handling! */ | 1395 | /* Need tasklist lock for parent etc handling! */ |
1403 | write_lock_irq(&tasklist_lock); | 1396 | write_lock_irq(&tasklist_lock); |
1404 | 1397 | ||
@@ -1503,7 +1496,7 @@ bad_fork_cleanup_cgroup: | |||
1503 | #endif | 1496 | #endif |
1504 | if (clone_flags & CLONE_THREAD) | 1497 | if (clone_flags & CLONE_THREAD) |
1505 | threadgroup_change_end(current); | 1498 | threadgroup_change_end(current); |
1506 | cgroup_exit(p, cgroup_callbacks_done); | 1499 | cgroup_exit(p, 0); |
1507 | delayacct_tsk_free(p); | 1500 | delayacct_tsk_free(p); |
1508 | module_put(task_thread_info(p)->exec_domain->module); | 1501 | module_put(task_thread_info(p)->exec_domain->module); |
1509 | bad_fork_cleanup_count: | 1502 | bad_fork_cleanup_count: |