diff options
author | Oleg Nesterov <oleg@tv-sign.ru> | 2008-04-30 00:52:51 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-30 08:29:35 -0700 |
commit | 3547ff3aefbe092ca35506c60c02e2d17a4f2199 (patch) | |
tree | 722222124ae06ef45947fae20913bbd71e179c49 /kernel/signal.c | |
parent | 6e65acba7ca8169e38ab55d62d52f29a75fb141f (diff) | |
download | linux-3547ff3aefbe092ca35506c60c02e2d17a4f2199.tar.gz linux-3547ff3aefbe092ca35506c60c02e2d17a4f2199.tar.bz2 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.c | 14 |
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; } |