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>
This commit is contained in:
Oleg Nesterov 2008-04-30 00:52:51 -07:00 committed by Linus Torvalds
parent 6e65acba7c
commit 3547ff3aef

View File

@ -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;
}