aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2011-12-24 23:49:45 -0800
committerZiyan <jaraidaniel@gmail.com>2016-01-08 10:43:05 +0100
commit798fc122bcec00eb7e1841e5353246fba2a7f259 (patch)
treeac47e2724a1b96d7daf667303c2758baa52fed44 /kernel
parent2c38a3c551c2e6d4aa62974a56819d66382e0d27 (diff)
downloadkernel_samsung_tuna-798fc122bcec00eb7e1841e5353246fba2a7f259.zip
kernel_samsung_tuna-798fc122bcec00eb7e1841e5353246fba2a7f259.tar.gz
kernel_samsung_tuna-798fc122bcec00eb7e1841e5353246fba2a7f259.tar.bz2
cgroup: always lock threadgroup during migration
From: Tejun Heo <tj@kernel.org> Update cgroup to take advantage of the fack that threadgroup_lock() guarantees stable threadgroup. * Lock threadgroup even if the target is a single task. This guarantees that when the target tasks stay stable during migration regardless of the target type. * Remove PF_EXITING early exit optimization from attach_task_by_pid() and check it in cgroup_task_migrate() instead. The optimization was for rather cold path to begin with and PF_EXITING state can be trusted throughout migration by checking it after locking threadgroup. * Don't add PF_EXITING tasks to target task array in cgroup_attach_proc(). This ensures that task migration is performed only for live tasks. * Remove -ESRCH failure path from cgroup_task_migrate(). With the above changes, it's guaranteed to be called only for live tasks. After the changes, only live tasks are migrated and they're guaranteed to stay alive until migration is complete. This removes problems caused by exec and exit racing against cgroup migration including symmetry among cgroup attach methods and different cgroup methods racing each other. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Paul Menage <menage@google.com> Cc: Li Zefan <lizf@cn.fujitsu.com>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/cgroup.c38
1 files changed, 18 insertions, 20 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index d684776..1b15cf2 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -1752,7 +1752,7 @@ EXPORT_SYMBOL_GPL(cgroup_path);
*
* 'guarantee' is set if the caller promises that a new css_set for the task
* will already exist. If not set, this function might sleep, and can fail with
- * -ENOMEM. Otherwise, it can only fail with -ESRCH.
+ * -ENOMEM. Must be called with cgroup_mutex and threadgroup locked.
*/
static int cgroup_task_migrate(struct cgroup *cgrp, struct cgroup *oldcgrp,
struct task_struct *tsk, bool guarantee)
@@ -1790,13 +1790,9 @@ static int cgroup_task_migrate(struct cgroup *cgrp, struct cgroup *oldcgrp,
}
put_css_set(oldcg);
- /* if PF_EXITING is set, the tsk->cgroups pointer is no longer safe. */
+ /* @tsk can't exit as its threadgroup is locked */
task_lock(tsk);
- if (tsk->flags & PF_EXITING) {
- task_unlock(tsk);
- put_css_set(newcg);
- return -ESRCH;
- }
+ WARN_ON_ONCE(tsk->flags & PF_EXITING);
rcu_assign_pointer(tsk->cgroups, newcg);
task_unlock(tsk);
@@ -1821,8 +1817,8 @@ static int cgroup_task_migrate(struct cgroup *cgrp, struct cgroup *oldcgrp,
* @cgrp: the cgroup the task is attaching to
* @tsk: the task to be attached
*
- * Call holding cgroup_mutex. May take task_lock of
- * the task 'tsk' during call.
+ * Call with cgroup_mutex and threadgroup locked. May take task_lock of
+ * @tsk during call.
*/
int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
{
@@ -1832,6 +1828,10 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
struct cgroupfs_root *root = cgrp->root;
struct css_set *cg;
+ /* @tsk either already exited or can't exit until the end */
+ if (tsk->flags & PF_EXITING)
+ return -ESRCH;
+
/* Nothing to do if the task is already in that cgroup */
oldcgrp = task_cgroup_from_root(tsk, root);
if (cgrp == oldcgrp)
@@ -2058,6 +2058,10 @@ int cgroup_attach_proc(struct cgroup *cgrp, struct task_struct *leader)
tsk = leader;
i = 0;
do {
+ /* @tsk either already exited or can't exit until the end */
+ if (tsk->flags & PF_EXITING)
+ continue;
+
/* as per above, nr_threads may decrease, but not increase. */
BUG_ON(i >= group_size);
get_task_struct(tsk);
@@ -2151,7 +2155,7 @@ int cgroup_attach_proc(struct cgroup *cgrp, struct task_struct *leader)
}
/* if the thread is PF_EXITING, it can just get skipped. */
retval = cgroup_task_migrate(cgrp, oldcgrp, tsk, true);
- BUG_ON(retval != 0 && retval != -ESRCH);
+ BUG_ON(retval != 0);
}
/* nothing is sensitive to fork() after this point. */
@@ -2220,8 +2224,8 @@ static int cgroup_allow_attach(struct cgroup *cgrp, struct task_struct *tsk)
/*
* Find the task_struct of the task to attach by vpid and pass it along to the
- * function to attach either it or all tasks in its threadgroup. Will take
- * cgroup_mutex; may take task_lock of task.
+ * function to attach either it or all tasks in its threadgroup. Will lock
+ * cgroup_mutex and threadgroup; may take task_lock of task.
*/
static int attach_task_by_pid(struct cgroup *cgrp, u64 pid, bool threadgroup)
{
@@ -2244,10 +2248,6 @@ static int attach_task_by_pid(struct cgroup *cgrp, u64 pid, bool threadgroup)
* detect it later.
*/
tsk = tsk->group_leader;
- } else if (tsk->flags & PF_EXITING) {
- /* optimization for the single-task-only case */
- rcu_read_unlock();
- return -ESRCH;
}
/*
@@ -2278,8 +2278,7 @@ static int attach_task_by_pid(struct cgroup *cgrp, u64 pid, bool threadgroup)
get_task_struct(tsk);
}
- if (threadgroup)
- threadgroup_lock(tsk);
+ threadgroup_lock(tsk);
ret = -ENODEV;
if (cgroup_lock_live_group(cgrp)) {
if (threadgroup)
@@ -2288,8 +2287,7 @@ static int attach_task_by_pid(struct cgroup *cgrp, u64 pid, bool threadgroup)
ret = cgroup_attach_task(cgrp, tsk);
cgroup_unlock();
}
- if (threadgroup)
- threadgroup_unlock(tsk);
+ threadgroup_unlock(tsk);
put_task_struct(tsk);
return ret;
}