aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@tv-sign.ru>2008-02-08 07:19:12 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-08 12:22:27 -0500
commitd12619b5ff5664623524aef796514d1946ea3b4a (patch)
tree23140ca0148f9154440d3056e2040d8df468a24b /kernel
parent430c623121ea88ca80595c99fdc63b7f8a803ae5 (diff)
fix group stop with exit race
do_signal_stop() counts all sub-thread and sets ->group_stop_count accordingly. Every thread should decrement ->group_stop_count and stop, the last one should notify the parent. However a sub-thread can exit before it notices the signal_pending(), or it may be somewhere in do_exit() already. In that case the group stop never finishes properly. Note: this is a minimal fix, we can add some optimizations later. Say we can return quickly if thread_group_empty(). Also, we can move some signal related code from exit_notify() to exit_signals(). Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru> Acked-by: Davide Libenzi <davidel@xmailserver.org> Cc: Ingo Molnar <mingo@elte.hu> Cc: Roland McGrath <roland@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/exit.c2
-rw-r--r--kernel/signal.c27
2 files changed, 27 insertions, 2 deletions
diff --git a/kernel/exit.c b/kernel/exit.c
index d7815f570882..8f3bf53a5b4d 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -947,7 +947,7 @@ fastcall NORET_TYPE void do_exit(long code)
947 schedule(); 947 schedule();
948 } 948 }
949 949
950 tsk->flags |= PF_EXITING; 950 exit_signals(tsk); /* sets PF_EXITING */
951 /* 951 /*
952 * tsk->flags are checked in the futex code to protect against 952 * tsk->flags are checked in the futex code to protect against
953 * an exiting task cleaning up the robust pi futexes. 953 * an exiting task cleaning up the robust pi futexes.
diff --git a/kernel/signal.c b/kernel/signal.c
index 3fca710a5cd7..209eec11eef5 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1739,7 +1739,7 @@ static int do_signal_stop(int signr)
1739 * stop is always done with the siglock held, 1739 * stop is always done with the siglock held,
1740 * so this check has no races. 1740 * so this check has no races.
1741 */ 1741 */
1742 if (!t->exit_state && 1742 if (!(t->flags & PF_EXITING) &&
1743 !task_is_stopped_or_traced(t)) { 1743 !task_is_stopped_or_traced(t)) {
1744 stop_count++; 1744 stop_count++;
1745 signal_wake_up(t, 0); 1745 signal_wake_up(t, 0);
@@ -1900,6 +1900,31 @@ relock:
1900 return signr; 1900 return signr;
1901} 1901}
1902 1902
1903void exit_signals(struct task_struct *tsk)
1904{
1905 int group_stop = 0;
1906
1907 spin_lock_irq(&tsk->sighand->siglock);
1908 if (unlikely(tsk->signal->group_stop_count) &&
1909 !--tsk->signal->group_stop_count) {
1910 tsk->signal->flags = SIGNAL_STOP_STOPPED;
1911 group_stop = 1;
1912 }
1913
1914 /*
1915 * From now this task is not visible for group-wide signals,
1916 * see wants_signal(), do_signal_stop().
1917 */
1918 tsk->flags |= PF_EXITING;
1919 spin_unlock_irq(&tsk->sighand->siglock);
1920
1921 if (unlikely(group_stop)) {
1922 read_lock(&tasklist_lock);
1923 do_notify_parent_cldstop(tsk, CLD_STOPPED);
1924 read_unlock(&tasklist_lock);
1925 }
1926}
1927
1903EXPORT_SYMBOL(recalc_sigpending); 1928EXPORT_SYMBOL(recalc_sigpending);
1904EXPORT_SYMBOL_GPL(dequeue_signal); 1929EXPORT_SYMBOL_GPL(dequeue_signal);
1905EXPORT_SYMBOL(flush_signals); 1930EXPORT_SYMBOL(flush_signals);