Commit 35fbf14c authored by Oleg Nesterov's avatar Oleg Nesterov Committed by James Toy

do_signal_stop() can call tracehook_notify_jctl() before decrementing

->group_stop_count and setting TASK_STOPPED/SIGNAL_STOP_STOPPED.

This way the tracing hooks can drop and reacquire the siglock freely and
do any blocking hooks without potential SIGCONT races.

With this patch TASK_STOPPED/SIGNAL_STOP_STOPPED is set only when we know
for sure we are going to schedule() after unlock(siglock).
Signed-off-by: default avatarOleg Nesterov <oleg@redhat.com>
Acked-by: default avatarRoland McGrath <roland@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 547805d2
...@@ -1664,16 +1664,9 @@ void ptrace_notify(int exit_code) ...@@ -1664,16 +1664,9 @@ void ptrace_notify(int exit_code)
static int do_signal_stop(int signr) static int do_signal_stop(int signr)
{ {
struct signal_struct *sig = current->signal; struct signal_struct *sig = current->signal;
int stop_count;
int notify; int notify;
if (sig->group_stop_count > 0) { if (!sig->group_stop_count) {
/*
* There is a group stop in progress. We don't need to
* start another one.
*/
stop_count = --sig->group_stop_count;
} else {
struct task_struct *t; struct task_struct *t;
if (!likely(sig->flags & SIGNAL_STOP_DEQUEUED) || if (!likely(sig->flags & SIGNAL_STOP_DEQUEUED) ||
...@@ -1685,7 +1678,7 @@ static int do_signal_stop(int signr) ...@@ -1685,7 +1678,7 @@ static int do_signal_stop(int signr)
*/ */
sig->group_exit_code = signr; sig->group_exit_code = signr;
stop_count = 0; sig->group_stop_count = 1;
for (t = next_thread(current); t != current; t = next_thread(t)) for (t = next_thread(current); t != current; t = next_thread(t))
/* /*
* Setting state to TASK_STOPPED for a group * Setting state to TASK_STOPPED for a group
...@@ -1694,25 +1687,28 @@ static int do_signal_stop(int signr) ...@@ -1694,25 +1687,28 @@ static int do_signal_stop(int signr)
*/ */
if (!(t->flags & PF_EXITING) && if (!(t->flags & PF_EXITING) &&
!task_is_stopped_or_traced(t)) { !task_is_stopped_or_traced(t)) {
stop_count++; sig->group_stop_count++;
signal_wake_up(t, 0); signal_wake_up(t, 0);
} }
sig->group_stop_count = stop_count;
} }
if (stop_count == 0)
sig->flags = SIGNAL_STOP_STOPPED;
current->exit_code = sig->group_exit_code;
__set_current_state(TASK_STOPPED);
/* /*
* If there are no other threads in the group, or if there is * If there are no other threads in the group, or if there is
* a group stop in progress and we are the last to stop, * a group stop in progress and we are the last to stop, report
* report to the parent. When ptraced, every thread reports itself. * to the parent. When ptraced, every thread reports itself.
*/ */
notify = tracehook_notify_jctl(stop_count == 0 ? CLD_STOPPED : 0, notify = sig->group_stop_count == 1 ? CLD_STOPPED : 0;
CLD_STOPPED); notify = tracehook_notify_jctl(notify, CLD_STOPPED);
/*
* tracehook_notify_jctl() can drop and reacquire siglock, so
* we keep ->group_stop_count != 0 before the call. If SIGCONT
* or SIGKILL comes in between ->group_stop_count == 0.
*/
if (sig->group_stop_count) {
if (!--sig->group_stop_count)
sig->flags = SIGNAL_STOP_STOPPED;
current->exit_code = sig->group_exit_code;
__set_current_state(TASK_STOPPED);
}
spin_unlock_irq(&current->sighand->siglock); spin_unlock_irq(&current->sighand->siglock);
if (notify) { if (notify) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment