aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2007-05-23 16:57:44 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-23 23:14:12 -0400
commit7bb44adef39ad3bda2be40bb34686bc56bd563a5 (patch)
tree1e9eecbd03877c7ac8b00a8cb1d4ae54ebec3bb1
parent3c6df2a9177c010bf811c361149069e4bfd9bb11 (diff)
recalc_sigpending_tsk fixes
Steve Hawkes discovered a problem where recalc_sigpending_tsk was called in do_sigaction but no signal_wake_up call was made, preventing later signals from waking up blocked threads with TIF_SIGPENDING already set. In fact, the few other calls to recalc_sigpending_tsk outside the signals code are also subject to this problem in other race conditions. This change makes recalc_sigpending_tsk private to the signals code. It changes the outside calls, as well as do_sigaction, to use the new recalc_sigpending_and_wake instead. Signed-off-by: Roland McGrath <roland@redhat.com> Cc: <Steve.Hawkes@motorola.com> Cc: Oleg Nesterov <oleg@tv-sign.ru> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/sched.h12
-rw-r--r--kernel/exit.c7
-rw-r--r--kernel/power/process.c2
-rw-r--r--kernel/signal.c24
4 files changed, 28 insertions, 17 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 870b75e348ab..d58e74b98367 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1616,11 +1616,13 @@ static inline int lock_need_resched(spinlock_t *lock)
1616 return 0; 1616 return 0;
1617} 1617}
1618 1618
1619/* Reevaluate whether the task has signals pending delivery. 1619/*
1620 This is required every time the blocked sigset_t changes. 1620 * Reevaluate whether the task has signals pending delivery.
1621 callers must hold sighand->siglock. */ 1621 * Wake the task if so.
1622 1622 * This is required every time the blocked sigset_t changes.
1623extern FASTCALL(void recalc_sigpending_tsk(struct task_struct *t)); 1623 * callers must hold sighand->siglock.
1624 */
1625extern void recalc_sigpending_and_wake(struct task_struct *t);
1624extern void recalc_sigpending(void); 1626extern void recalc_sigpending(void);
1625 1627
1626extern void signal_wake_up(struct task_struct *t, int resume_stopped); 1628extern void signal_wake_up(struct task_struct *t, int resume_stopped);
diff --git a/kernel/exit.c b/kernel/exit.c
index c6d14b8008dd..5b888c24e43e 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -762,11 +762,8 @@ static void exit_notify(struct task_struct *tsk)
762 read_lock(&tasklist_lock); 762 read_lock(&tasklist_lock);
763 spin_lock_irq(&tsk->sighand->siglock); 763 spin_lock_irq(&tsk->sighand->siglock);
764 for (t = next_thread(tsk); t != tsk; t = next_thread(t)) 764 for (t = next_thread(tsk); t != tsk; t = next_thread(t))
765 if (!signal_pending(t) && !(t->flags & PF_EXITING)) { 765 if (!signal_pending(t) && !(t->flags & PF_EXITING))
766 recalc_sigpending_tsk(t); 766 recalc_sigpending_and_wake(t);
767 if (signal_pending(t))
768 signal_wake_up(t, 0);
769 }
770 spin_unlock_irq(&tsk->sighand->siglock); 767 spin_unlock_irq(&tsk->sighand->siglock);
771 read_unlock(&tasklist_lock); 768 read_unlock(&tasklist_lock);
772 } 769 }
diff --git a/kernel/power/process.c b/kernel/power/process.c
index d31d638ab4c0..e0233d8422b9 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -101,7 +101,7 @@ static void cancel_freezing(struct task_struct *p)
101 pr_debug(" clean up: %s\n", p->comm); 101 pr_debug(" clean up: %s\n", p->comm);
102 do_not_freeze(p); 102 do_not_freeze(p);
103 spin_lock_irqsave(&p->sighand->siglock, flags); 103 spin_lock_irqsave(&p->sighand->siglock, flags);
104 recalc_sigpending_tsk(p); 104 recalc_sigpending_and_wake(p);
105 spin_unlock_irqrestore(&p->sighand->siglock, flags); 105 spin_unlock_irqrestore(&p->sighand->siglock, flags);
106 } 106 }
107} 107}
diff --git a/kernel/signal.c b/kernel/signal.c
index 364fc95bf97c..acdfc0549c6f 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -96,15 +96,27 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked)
96 96
97#define PENDING(p,b) has_pending_signals(&(p)->signal, (b)) 97#define PENDING(p,b) has_pending_signals(&(p)->signal, (b))
98 98
99fastcall void recalc_sigpending_tsk(struct task_struct *t) 99static int recalc_sigpending_tsk(struct task_struct *t)
100{ 100{
101 if (t->signal->group_stop_count > 0 || 101 if (t->signal->group_stop_count > 0 ||
102 (freezing(t)) || 102 (freezing(t)) ||
103 PENDING(&t->pending, &t->blocked) || 103 PENDING(&t->pending, &t->blocked) ||
104 PENDING(&t->signal->shared_pending, &t->blocked)) 104 PENDING(&t->signal->shared_pending, &t->blocked)) {
105 set_tsk_thread_flag(t, TIF_SIGPENDING); 105 set_tsk_thread_flag(t, TIF_SIGPENDING);
106 else 106 return 1;
107 clear_tsk_thread_flag(t, TIF_SIGPENDING); 107 }
108 clear_tsk_thread_flag(t, TIF_SIGPENDING);
109 return 0;
110}
111
112/*
113 * After recalculating TIF_SIGPENDING, we need to make sure the task wakes up.
114 * This is superfluous when called on current, the wakeup is a harmless no-op.
115 */
116void recalc_sigpending_and_wake(struct task_struct *t)
117{
118 if (recalc_sigpending_tsk(t))
119 signal_wake_up(t, 0);
108} 120}
109 121
110void recalc_sigpending(void) 122void recalc_sigpending(void)
@@ -744,7 +756,7 @@ force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
744 action->sa.sa_handler = SIG_DFL; 756 action->sa.sa_handler = SIG_DFL;
745 if (blocked) { 757 if (blocked) {
746 sigdelset(&t->blocked, sig); 758 sigdelset(&t->blocked, sig);
747 recalc_sigpending_tsk(t); 759 recalc_sigpending_and_wake(t);
748 } 760 }
749 } 761 }
750 ret = specific_send_sig_info(sig, info, t); 762 ret = specific_send_sig_info(sig, info, t);
@@ -2273,7 +2285,7 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
2273 rm_from_queue_full(&mask, &t->signal->shared_pending); 2285 rm_from_queue_full(&mask, &t->signal->shared_pending);
2274 do { 2286 do {
2275 rm_from_queue_full(&mask, &t->pending); 2287 rm_from_queue_full(&mask, &t->pending);
2276 recalc_sigpending_tsk(t); 2288 recalc_sigpending_and_wake(t);
2277 t = next_thread(t); 2289 t = next_thread(t);
2278 } while (t != current); 2290 } while (t != current);
2279 } 2291 }