summaryrefslogtreecommitdiffstats
path: root/kernel/signal.c
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@tv-sign.ru>2008-04-30 09:52:51 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-30 17:29:35 +0200
commit3547ff3aefbe092ca35506c60c02e2d17a4f2199 (patch)
tree722222124ae06ef45947fae20913bbd71e179c49 /kernel/signal.c
parentsignals: move handle_stop_signal() into send_signal() (diff)
downloadlinux-3547ff3aefbe092ca35506c60c02e2d17a4f2199.tar.xz
linux-3547ff3aefbe092ca35506c60c02e2d17a4f2199.zip
signals: do_tkill: don't use tasklist_lock
Convert do_tkill() to use rcu_read_lock() + lock_task_sighand() to avoid taking tasklist lock. Note that we don't return an error if lock_task_sighand() fails, we pretend the task dies after receiving the signal. Otherwise, we should fight with the nasty races with mt-exec without having any advantage. Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru> 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/signal.c')
-rw-r--r--kernel/signal.c14
1 files changed, 9 insertions, 5 deletions
diff --git a/kernel/signal.c b/kernel/signal.c
index b3dedf1f9323..13371d17358d 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2219,6 +2219,7 @@ static int do_tkill(int tgid, int pid, int sig)
int error;
struct siginfo info;
struct task_struct *p;
+ unsigned long flags;
error = -ESRCH;
info.si_signo = sig;
@@ -2227,21 +2228,24 @@ static int do_tkill(int tgid, int pid, int sig)
info.si_pid = task_tgid_vnr(current);
info.si_uid = current->uid;
- read_lock(&tasklist_lock);
+ rcu_read_lock();
p = find_task_by_vpid(pid);
if (p && (tgid <= 0 || task_tgid_vnr(p) == tgid)) {
error = check_kill_permission(sig, &info, p);
/*
* The null signal is a permissions and process existence
* probe. No signal is actually delivered.
+ *
+ * If lock_task_sighand() fails we pretend the task dies
+ * after receiving the signal. The window is tiny, and the
+ * signal is private anyway.
*/
- if (!error && sig && p->sighand) {
- spin_lock_irq(&p->sighand->siglock);
+ if (!error && sig && lock_task_sighand(p, &flags)) {
error = specific_send_sig_info(sig, &info, p);
- spin_unlock_irq(&p->sighand->siglock);
+ unlock_task_sighand(p, &flags);
}
}
- read_unlock(&tasklist_lock);
+ rcu_read_unlock();
return error;
}