diff options
author | Oleg Nesterov <oleg@tv-sign.ru> | 2008-04-30 03:52:44 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-30 11:29:34 -0400 |
commit | e442055193e4584218006e616c9bdce0c5e9ae5c (patch) | |
tree | 5e444253953c2f48c0a194bf8c5799f03bb2e266 /kernel/signal.c | |
parent | 3b5e9e53c6f31b5a5a0f5c43707503c62bdefa46 (diff) |
signals: re-assign CLD_CONTINUED notification from the sender to reciever
Based on discussion with Jiri and Roland.
In short: currently handle_stop_signal(SIGCONT, p) sends the notification to
p->parent, with this patch p itself notifies its parent when it becomes
running.
handle_stop_signal(SIGCONT) has to drop ->siglock temporary in order to notify
the parent with do_notify_parent_cldstop(). This leads to multiple problems:
- as Jiri Kosina pointed out, the stopped task can resume without
actually seeing SIGCONT which may have a handler.
- we race with another sig_kernel_stop() signal which may come in
that window.
- we race with sig_fatal() signals which may set SIGNAL_GROUP_EXIT
in that window.
- we can't avoid taking tasklist_lock() while sending SIGCONT.
With this patch handle_stop_signal() just sets the new SIGNAL_CLD_CONTINUED
flag in p->signal->flags and returns. The notification is sent by the first
task which returns from finish_stop() (there should be at least one) or any
other signalled thread from get_signal_to_deliver().
This is a user-visible change. Say, currently kill(SIGCONT, stopped_child)
can't return without seeing SIGCHLD, with this patch SIGCHLD can be delayed
unpredictably. Another difference is that if the child is ptraced by another
process, CLD_CONTINUED may be delivered to ->real_parent after ptrace_detach()
while currently it always goes to the tracer which doesn't actually need this
notification. Hopefully not a problem.
The patch asks for the futher obvious cleanups, I'll send them separately.
Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru>
Cc: Roland McGrath <roland@redhat.com>
Cc: Jiri Kosina <jkosina@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/signal.c')
-rw-r--r-- | kernel/signal.c | 29 |
1 files changed, 19 insertions, 10 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index 91d57f89f5a5..115c04f3f143 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -603,10 +603,8 @@ static void handle_stop_signal(int sig, struct task_struct *p) | |||
603 | * the SIGCHLD was pending on entry to this kill. | 603 | * the SIGCHLD was pending on entry to this kill. |
604 | */ | 604 | */ |
605 | p->signal->group_stop_count = 0; | 605 | p->signal->group_stop_count = 0; |
606 | p->signal->flags = SIGNAL_STOP_CONTINUED; | 606 | p->signal->flags = SIGNAL_STOP_CONTINUED | |
607 | spin_unlock(&p->sighand->siglock); | 607 | SIGNAL_CLD_STOPPED; |
608 | do_notify_parent_cldstop(p, CLD_STOPPED); | ||
609 | spin_lock(&p->sighand->siglock); | ||
610 | } | 608 | } |
611 | rm_from_queue(SIG_KERNEL_STOP_MASK, &p->signal->shared_pending); | 609 | rm_from_queue(SIG_KERNEL_STOP_MASK, &p->signal->shared_pending); |
612 | t = p; | 610 | t = p; |
@@ -643,25 +641,23 @@ static void handle_stop_signal(int sig, struct task_struct *p) | |||
643 | * We were in fact stopped, and are now continued. | 641 | * We were in fact stopped, and are now continued. |
644 | * Notify the parent with CLD_CONTINUED. | 642 | * Notify the parent with CLD_CONTINUED. |
645 | */ | 643 | */ |
646 | p->signal->flags = SIGNAL_STOP_CONTINUED; | 644 | p->signal->flags = SIGNAL_STOP_CONTINUED | |
645 | SIGNAL_CLD_CONTINUED; | ||
647 | p->signal->group_exit_code = 0; | 646 | p->signal->group_exit_code = 0; |
648 | spin_unlock(&p->sighand->siglock); | ||
649 | do_notify_parent_cldstop(p, CLD_CONTINUED); | ||
650 | spin_lock(&p->sighand->siglock); | ||
651 | } else { | 647 | } else { |
652 | /* | 648 | /* |
653 | * We are not stopped, but there could be a stop | 649 | * We are not stopped, but there could be a stop |
654 | * signal in the middle of being processed after | 650 | * signal in the middle of being processed after |
655 | * being removed from the queue. Clear that too. | 651 | * being removed from the queue. Clear that too. |
656 | */ | 652 | */ |
657 | p->signal->flags = 0; | 653 | p->signal->flags &= ~SIGNAL_STOP_DEQUEUED; |
658 | } | 654 | } |
659 | } else if (sig == SIGKILL) { | 655 | } else if (sig == SIGKILL) { |
660 | /* | 656 | /* |
661 | * Make sure that any pending stop signal already dequeued | 657 | * Make sure that any pending stop signal already dequeued |
662 | * is undone by the wakeup for SIGKILL. | 658 | * is undone by the wakeup for SIGKILL. |
663 | */ | 659 | */ |
664 | p->signal->flags = 0; | 660 | p->signal->flags &= ~SIGNAL_STOP_DEQUEUED; |
665 | } | 661 | } |
666 | } | 662 | } |
667 | 663 | ||
@@ -1784,6 +1780,19 @@ relock: | |||
1784 | try_to_freeze(); | 1780 | try_to_freeze(); |
1785 | 1781 | ||
1786 | spin_lock_irq(¤t->sighand->siglock); | 1782 | spin_lock_irq(¤t->sighand->siglock); |
1783 | |||
1784 | if (unlikely(current->signal->flags & SIGNAL_CLD_MASK)) { | ||
1785 | int why = (current->signal->flags & SIGNAL_STOP_CONTINUED) | ||
1786 | ? CLD_CONTINUED : CLD_STOPPED; | ||
1787 | current->signal->flags &= ~SIGNAL_CLD_MASK; | ||
1788 | spin_unlock_irq(¤t->sighand->siglock); | ||
1789 | |||
1790 | read_lock(&tasklist_lock); | ||
1791 | do_notify_parent_cldstop(current->group_leader, why); | ||
1792 | read_unlock(&tasklist_lock); | ||
1793 | goto relock; | ||
1794 | } | ||
1795 | |||
1787 | for (;;) { | 1796 | for (;;) { |
1788 | struct k_sigaction *ka; | 1797 | struct k_sigaction *ka; |
1789 | 1798 | ||