diff options
author | Oleg Nesterov <oleg@redhat.com> | 2012-10-26 13:46:06 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2012-10-26 17:27:49 -0400 |
commit | 5d8f72b55c275677865de670fa147ed318191d81 (patch) | |
tree | 58f4f571440f412861a232ed0c5753771e5a6e58 /kernel/power | |
parent | ead5c473712eb26db792b18a4dc98fdb312883fe (diff) |
freezer: change ptrace_stop/do_signal_stop to use freezable_schedule()
try_to_freeze_tasks() and cgroup_freezer rely on scheduler locks
to ensure that a task doing STOPPED/TRACED -> RUNNING transition
can't escape freezing. This mostly works, but ptrace_stop() does
not necessarily call schedule(), it can change task->state back to
RUNNING and check freezing() without any lock/barrier in between.
We could add the necessary barrier, but this patch changes
ptrace_stop() and do_signal_stop() to use freezable_schedule().
This fixes the race, freezer_count() and freezer_should_skip()
carefully avoid the race.
And this simplifies the code, try_to_freeze_tasks/update_if_frozen
no longer need to use task_is_stopped_or_traced() checks with the
non trivial assumptions. We can rely on the mechanism which was
specially designed to mark the sleeping task as "frozen enough".
v2: As Tejun pointed out, we can also change get_signal_to_deliver()
and move try_to_freeze() up before 'relock' label.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'kernel/power')
-rw-r--r-- | kernel/power/process.c | 13 |
1 files changed, 1 insertions, 12 deletions
diff --git a/kernel/power/process.c b/kernel/power/process.c index 87da817f9e1..d5a258b60c6 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c | |||
@@ -48,18 +48,7 @@ static int try_to_freeze_tasks(bool user_only) | |||
48 | if (p == current || !freeze_task(p)) | 48 | if (p == current || !freeze_task(p)) |
49 | continue; | 49 | continue; |
50 | 50 | ||
51 | /* | 51 | if (!freezer_should_skip(p)) |
52 | * Now that we've done set_freeze_flag, don't | ||
53 | * perturb a task in TASK_STOPPED or TASK_TRACED. | ||
54 | * It is "frozen enough". If the task does wake | ||
55 | * up, it will immediately call try_to_freeze. | ||
56 | * | ||
57 | * Because freeze_task() goes through p's scheduler lock, it's | ||
58 | * guaranteed that TASK_STOPPED/TRACED -> TASK_RUNNING | ||
59 | * transition can't race with task state testing here. | ||
60 | */ | ||
61 | if (!task_is_stopped_or_traced(p) && | ||
62 | !freezer_should_skip(p)) | ||
63 | todo++; | 52 | todo++; |
64 | } while_each_thread(g, p); | 53 | } while_each_thread(g, p); |
65 | read_unlock(&tasklist_lock); | 54 | read_unlock(&tasklist_lock); |