diff options
author | Oleg Nesterov <oleg@tv-sign.ru> | 2008-04-30 03:52:51 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-30 11:29:35 -0400 |
commit | 3547ff3aefbe092ca35506c60c02e2d17a4f2199 (patch) | |
tree | 722222124ae06ef45947fae20913bbd71e179c49 /kernel/signal.c | |
parent | 6e65acba7ca8169e38ab55d62d52f29a75fb141f (diff) |
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) | |||
2219 | int error; | 2219 | int error; |
2220 | struct siginfo info; | 2220 | struct siginfo info; |
2221 | struct task_struct *p; | 2221 | struct task_struct *p; |
2222 | unsigned long flags; | ||
2222 | 2223 | ||
2223 | error = -ESRCH; | 2224 | error = -ESRCH; |
2224 | info.si_signo = sig; | 2225 | info.si_signo = sig; |
@@ -2227,21 +2228,24 @@ static int do_tkill(int tgid, int pid, int sig) | |||
2227 | info.si_pid = task_tgid_vnr(current); | 2228 | info.si_pid = task_tgid_vnr(current); |
2228 | info.si_uid = current->uid; | 2229 | info.si_uid = current->uid; |
2229 | 2230 | ||
2230 | read_lock(&tasklist_lock); | 2231 | rcu_read_lock(); |
2231 | p = find_task_by_vpid(pid); | 2232 | p = find_task_by_vpid(pid); |
2232 | if (p && (tgid <= 0 || task_tgid_vnr(p) == tgid)) { | 2233 | if (p && (tgid <= 0 || task_tgid_vnr(p) == tgid)) { |
2233 | error = check_kill_permission(sig, &info, p); | 2234 | error = check_kill_permission(sig, &info, p); |
2234 | /* | 2235 | /* |
2235 | * The null signal is a permissions and process existence | 2236 | * The null signal is a permissions and process existence |
2236 | * probe. No signal is actually delivered. | 2237 | * probe. No signal is actually delivered. |
2238 | * | ||
2239 | * If lock_task_sighand() fails we pretend the task dies | ||
2240 | * after receiving the signal. The window is tiny, and the | ||
2241 | * signal is private anyway. | ||
2237 | */ | 2242 | */ |
2238 | if (!error && sig && p->sighand) { | 2243 | if (!error && sig && lock_task_sighand(p, &flags)) { |
2239 | spin_lock_irq(&p->sighand->siglock); | ||
2240 | error = specific_send_sig_info(sig, &info, p); | 2244 | error = specific_send_sig_info(sig, &info, p); |
2241 | spin_unlock_irq(&p->sighand->siglock); | 2245 | unlock_task_sighand(p, &flags); |
2242 | } | 2246 | } |
2243 | } | 2247 | } |
2244 | read_unlock(&tasklist_lock); | 2248 | rcu_read_unlock(); |
2245 | 2249 | ||
2246 | return error; | 2250 | return error; |
2247 | } | 2251 | } |