aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@tv-sign.ru>2006-03-28 19:11:13 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-28 21:36:42 -0500
commitf63ee72e0fb82e504a0489490babc7612c7cd6c2 (patch)
tree85167f8016d1f746135bf1777646a58c44376af1
parentaa1757f90bea3f598b6e5d04d922a6a60200f1da (diff)
[PATCH] introduce lock_task_sighand() helper
Add lock_task_sighand() helper and converts group_send_sig_info() to use it. Hopefully we will have more users soon. This patch also removes '!sighand->count' and '!p->usage' checks, I think they both are bogus, racy and unneeded (but probably it makes sense to restore them as BUG_ON()s). ->sighand is cleared and it's ->count is decremented in release_task() with sighand->siglock held, so it is a bug to have '!p->usage || !->count' after we already locked and verified it is the same. On the other hand, an already dead task without ->sighand can have a non-zero ->usage due to ptrace, for example. If we read the stale value of ->sighand we must see the change after spin_lock(), because that change was done while holding that same old ->sighand.siglock. Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--include/linux/sched.h9
-rw-r--r--kernel/signal.c38
2 files changed, 33 insertions, 14 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h
index bbcfc873bd98..ca1fd31aae97 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1225,6 +1225,15 @@ static inline void task_unlock(struct task_struct *p)
1225 spin_unlock(&p->alloc_lock); 1225 spin_unlock(&p->alloc_lock);
1226} 1226}
1227 1227
1228extern struct sighand_struct *lock_task_sighand(struct task_struct *tsk,
1229 unsigned long *flags);
1230
1231static inline void unlock_task_sighand(struct task_struct *tsk,
1232 unsigned long *flags)
1233{
1234 spin_unlock_irqrestore(&tsk->sighand->siglock, *flags);
1235}
1236
1228#ifndef __HAVE_THREAD_FUNCTIONS 1237#ifndef __HAVE_THREAD_FUNCTIONS
1229 1238
1230#define task_thread_info(task) (task)->thread_info 1239#define task_thread_info(task) (task)->thread_info
diff --git a/kernel/signal.c b/kernel/signal.c
index b0b1ca9daa33..819fa49aa70a 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1120,27 +1120,37 @@ void zap_other_threads(struct task_struct *p)
1120/* 1120/*
1121 * Must be called under rcu_read_lock() or with tasklist_lock read-held. 1121 * Must be called under rcu_read_lock() or with tasklist_lock read-held.
1122 */ 1122 */
1123struct sighand_struct *lock_task_sighand(struct task_struct *tsk, unsigned long *flags)
1124{
1125 struct sighand_struct *sighand;
1126
1127 for (;;) {
1128 sighand = rcu_dereference(tsk->sighand);
1129 if (unlikely(sighand == NULL))
1130 break;
1131
1132 spin_lock_irqsave(&sighand->siglock, *flags);
1133 if (likely(sighand == tsk->sighand))
1134 break;
1135 spin_unlock_irqrestore(&sighand->siglock, *flags);
1136 }
1137
1138 return sighand;
1139}
1140
1123int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) 1141int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
1124{ 1142{
1125 unsigned long flags; 1143 unsigned long flags;
1126 struct sighand_struct *sp;
1127 int ret; 1144 int ret;
1128 1145
1129retry:
1130 ret = check_kill_permission(sig, info, p); 1146 ret = check_kill_permission(sig, info, p);
1131 if (!ret && sig && (sp = rcu_dereference(p->sighand))) { 1147
1132 spin_lock_irqsave(&sp->siglock, flags); 1148 if (!ret && sig) {
1133 if (p->sighand != sp) { 1149 ret = -ESRCH;
1134 spin_unlock_irqrestore(&sp->siglock, flags); 1150 if (lock_task_sighand(p, &flags)) {
1135 goto retry; 1151 ret = __group_send_sig_info(sig, info, p);
1136 } 1152 unlock_task_sighand(p, &flags);
1137 if ((atomic_read(&sp->count) == 0) ||
1138 (atomic_read(&p->usage) == 0)) {
1139 spin_unlock_irqrestore(&sp->siglock, flags);
1140 return -ESRCH;
1141 } 1153 }
1142 ret = __group_send_sig_info(sig, info, p);
1143 spin_unlock_irqrestore(&sp->siglock, flags);
1144 } 1154 }
1145 1155
1146 return ret; 1156 return ret;