diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/signal.c | 45 |
1 files changed, 19 insertions, 26 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index ce53ab19c21d..dee8cc927a63 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -585,33 +585,16 @@ static void handle_stop_signal(int sig, struct task_struct *p) t = next_thread(t); } while (t != p); } else if (sig == SIGCONT) { + unsigned int why; /* * Remove all stop signals from all queues, * and wake all threads. */ - if (unlikely(p->signal->group_stop_count > 0)) { - /* - * There was a group stop in progress. We'll - * pretend it finished before we got here. We are - * obliged to report it to the parent: if the - * SIGSTOP happened "after" this SIGCONT, then it - * would have cleared this pending SIGCONT. If it - * happened "before" this SIGCONT, then the parent - * got the SIGCHLD about the stop finishing before - * the continue happened. We do the notification - * now, and it's as if the stop had finished and - * the SIGCHLD was pending on entry to this kill. - */ - p->signal->group_stop_count = 0; - p->signal->flags = SIGNAL_STOP_CONTINUED | - SIGNAL_CLD_STOPPED; - } rm_from_queue(SIG_KERNEL_STOP_MASK, &p->signal->shared_pending); t = p; do { unsigned int state; rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending); - /* * If there is a handler for SIGCONT, we must make * sure that no thread returns to user mode before @@ -621,7 +604,7 @@ static void handle_stop_signal(int sig, struct task_struct *p) * running the handler. With the TIF_SIGPENDING * flag set, the thread will pause and acquire the * siglock that we hold now and until we've queued - * the pending signal. + * the pending signal. * * Wake up the stopped thread _after_ setting * TIF_SIGPENDING @@ -636,13 +619,23 @@ static void handle_stop_signal(int sig, struct task_struct *p) t = next_thread(t); } while (t != p); - if (p->signal->flags & SIGNAL_STOP_STOPPED) { - /* - * We were in fact stopped, and are now continued. - * Notify the parent with CLD_CONTINUED. - */ - p->signal->flags = SIGNAL_STOP_CONTINUED | - SIGNAL_CLD_CONTINUED; + /* + * Notify the parent with CLD_CONTINUED if we were stopped. + * + * If we were in the middle of a group stop, we pretend it + * was already finished, and then continued. Since SIGCHLD + * doesn't queue we report only CLD_STOPPED, as if the next + * CLD_CONTINUED was dropped. + */ + why = 0; + if (p->signal->flags & SIGNAL_STOP_STOPPED) + why |= SIGNAL_CLD_CONTINUED; + else if (p->signal->group_stop_count) + why |= SIGNAL_CLD_STOPPED; + + if (why) { + p->signal->flags = why | SIGNAL_STOP_CONTINUED; + p->signal->group_stop_count = 0; p->signal->group_exit_code = 0; } else { /* |