aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/cgroup.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2012-10-16 15:03:14 -0700
committerBen Hutchings <ben@decadent.org.uk>2013-01-03 03:32:56 +0000
commitbd8320996b15f292e80501b4cf5de133546d1cbf (patch)
tree239e6e16b0ad7e4694bfba4c5af6399273cb3e60 /kernel/cgroup.c
parent511c73bcf8e747cc95925a311dfeb630989db5a6 (diff)
downloadkernel_samsung_smdk4412-bd8320996b15f292e80501b4cf5de133546d1cbf.zip
kernel_samsung_smdk4412-bd8320996b15f292e80501b4cf5de133546d1cbf.tar.gz
kernel_samsung_smdk4412-bd8320996b15f292e80501b4cf5de133546d1cbf.tar.bz2
cgroup: cgroup_subsys->fork() should be called after the task is added to css_set
commit 5edee61edeaaebafe584f8fb7074c1ef4658596b upstream. 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> [bwh: Backported to 3.2: - Adjust context - Iterate over first CGROUP_BUILTIN_SUBSYS_COUNT elements of subsys - cgroup_subsys::fork takes cgroup_subsys pointer as first parameter] Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Diffstat (limited to 'kernel/cgroup.c')
-rw-r--r--kernel/cgroup.c50
1 files changed, 21 insertions, 29 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 6337535..a51ba74 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -4508,41 +4508,19 @@ void cgroup_fork(struct task_struct *child)
}
/**
- * cgroup_fork_callbacks - run fork callbacks
- * @child: the new task
- *
- * Called on a new task very soon before adding it to the
- * tasklist. No need to take any locks since no-one can
- * be operating on this task.
- */
-void cgroup_fork_callbacks(struct task_struct *child)
-{
- if (need_forkexit_callback) {
- int i;
- /*
- * forkexit callbacks are only supported for builtin
- * subsystems, and the builtin section of the subsys array is
- * immutable, so we don't need to lock the subsys array here.
- */
- for (i = 0; i < CGROUP_BUILTIN_SUBSYS_COUNT; i++) {
- struct cgroup_subsys *ss = subsys[i];
- if (ss->fork)
- ss->fork(ss, child);
- }
- }
-}
-
-/**
* cgroup_post_fork - called on a new task after adding it to the task list
* @child: the task in question
*
- * Adds the task to the list running through its css_set if necessary.
- * Has to be after the task is visible on the task list in case we race
- * with the first call to cgroup_iter_start() - to guarantee that the
- * new task ends up on its list.
+ * Adds the task to the list running through its css_set if necessary and
+ * call the subsystem fork() callbacks. Has to be after the task is
+ * visible on the task list in case we race with the first call to
+ * cgroup_iter_start() - to guarantee that the new task ends up on its
+ * list.
*/
void cgroup_post_fork(struct task_struct *child)
{
+ int i;
+
if (use_task_css_set_links) {
write_lock(&css_set_lock);
task_lock(child);
@@ -4551,7 +4529,21 @@ void cgroup_post_fork(struct task_struct *child)
task_unlock(child);
write_unlock(&css_set_lock);
}
+
+ /*
+ * Call ss->fork(). This must happen after @child is linked on
+ * css_set; otherwise, @child might change state between ->fork()
+ * and addition to css_set.
+ */
+ if (need_forkexit_callback) {
+ for (i = 0; i < CGROUP_BUILTIN_SUBSYS_COUNT; i++) {
+ struct cgroup_subsys *ss = subsys[i];
+ if (ss->fork)
+ ss->fork(ss, child);
+ }
+ }
}
+
/**
* cgroup_exit - detach cgroup from exiting task
* @tsk: pointer to task_struct of exiting process