aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/fork.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2012-10-16 18:03:14 -0400
committerTejun Heo <tj@kernel.org>2012-10-16 18:03:14 -0400
commit5edee61edeaaebafe584f8fb7074c1ef4658596b (patch)
tree23e6ee3581eb0009b3c2a2686c25fdba538219de /kernel/fork.c
parentddffeb8c4d0331609ef2581d84de4d763607bd37 (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.c9
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);
1509bad_fork_cleanup_count: 1502bad_fork_cleanup_count: