X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=kernel%2Fsignal.c;h=cf9bb8d626eebd719d285b76428765880d301556;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=4d39765b401cde15971e66ba971e11329cf1079b;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/kernel/signal.c b/kernel/signal.c index 4d39765b4..cf9bb8d62 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -152,11 +153,6 @@ static kmem_cache_t *sigqueue_cachep; (!T(signr, SIG_KERNEL_IGNORE_MASK|SIG_KERNEL_STOP_MASK) && \ (t)->sighand->action[(signr)-1].sa.sa_handler == SIG_DFL) -#define sig_avoid_stop_race() \ - (sigtestsetmask(¤t->pending.signal, M(SIGCONT) | M(SIGKILL)) || \ - sigtestsetmask(¤t->signal->shared_pending.signal, \ - M(SIGCONT) | M(SIGKILL))) - static int sig_ignored(struct task_struct *t, int sig) { void __user * handler; @@ -264,18 +260,18 @@ next_signal(struct sigpending *pending, sigset_t *mask) return sig; } -static struct sigqueue *__sigqueue_alloc(void) +static struct sigqueue *__sigqueue_alloc(struct task_struct *t, int flags) { struct sigqueue *q = NULL; - if (atomic_read(¤t->user->sigpending) < - current->rlim[RLIMIT_SIGPENDING].rlim_cur) - q = kmem_cache_alloc(sigqueue_cachep, GFP_ATOMIC); + if (atomic_read(&t->user->sigpending) < + t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur) + q = kmem_cache_alloc(sigqueue_cachep, flags); if (q) { INIT_LIST_HEAD(&q->list); q->flags = 0; q->lock = NULL; - q->user = get_uid(current->user); + q->user = get_uid(t->user); atomic_inc(&q->user->sigpending); } return(q); @@ -379,8 +375,8 @@ void __exit_signal(struct task_struct *tsk) * We won't ever get here for the group leader, since it * will have been the last reference on the signal_struct. */ - sig->utime += tsk->utime; - sig->stime += tsk->stime; + sig->utime = cputime_add(sig->utime, tsk->utime); + sig->stime = cputime_add(sig->stime, tsk->stime); sig->min_flt += tsk->min_flt; sig->maj_flt += tsk->maj_flt; sig->nvcsw += tsk->nvcsw; @@ -550,6 +546,21 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info) if (!signr) signr = __dequeue_signal(&tsk->signal->shared_pending, mask, info); + if (signr && unlikely(sig_kernel_stop(signr))) { + /* + * Set a marker that we have dequeued a stop signal. Our + * caller might release the siglock and then the pending + * stop signal it is about to process is no longer in the + * pending bitmasks, but must still be cleared by a SIGCONT + * (and overruled by a SIGKILL). So those cases clear this + * shared flag after we've set it. Note that this flag may + * remain set after the signal we return is ignored or + * handled. That doesn't matter because its only purpose + * is to alert stop-signal processing code when another + * processor has come along and cleared the flag. + */ + tsk->signal->flags |= SIGNAL_STOP_DEQUEUED; + } if ( signr && ((info->si_code & __SI_MASK) == __SI_TIMER) && info->si_sys_private){ @@ -576,15 +587,15 @@ void signal_wake_up(struct task_struct *t, int resume) set_tsk_thread_flag(t, TIF_SIGPENDING); /* - * If resume is set, we want to wake it up in the TASK_STOPPED case. - * We don't check for TASK_STOPPED because there is a race with it + * For SIGKILL, we want to wake it up in the stopped/traced case. + * We don't check t->state here because there is a race with it * executing another processor and just now entering stopped state. - * By calling wake_up_process any time resume is set, we ensure - * the process will wake up and handle its stop or death signal. + * By using wake_up_state, we ensure the process will wake up and + * handle its death signal. */ mask = TASK_INTERRUPTIBLE; if (resume) - mask |= TASK_STOPPED; + mask |= TASK_STOPPED | TASK_TRACED; if (!wake_up_state(t, mask)) kick_process(t); } @@ -659,6 +670,12 @@ static void handle_stop_signal(int sig, struct task_struct *p) { struct task_struct *t; + if (p->flags & SIGNAL_GROUP_EXIT) + /* + * The process is in the middle of dying already. + */ + return; + if (sig_kernel_stop(sig)) { /* * This is a stop signal. Remove SIGCONT from all queues. @@ -688,7 +705,7 @@ static void handle_stop_signal(int sig, struct task_struct *p) * the SIGCHLD was pending on entry to this kill. */ p->signal->group_stop_count = 0; - p->signal->stop_state = 1; + p->signal->flags = SIGNAL_STOP_CONTINUED; spin_unlock(&p->sighand->siglock); if (p->ptrace & PT_PTRACED) do_notify_parent_cldstop(p, p->parent, @@ -730,12 +747,12 @@ static void handle_stop_signal(int sig, struct task_struct *p) t = next_thread(t); } while (t != p); - if (p->signal->stop_state > 0) { + if (p->signal->flags & SIGNAL_STOP_STOPPED) { /* * We were in fact stopped, and are now continued. * Notify the parent with CLD_CONTINUED. */ - p->signal->stop_state = -1; + p->signal->flags = SIGNAL_STOP_CONTINUED; p->signal->group_exit_code = 0; spin_unlock(&p->sighand->siglock); if (p->ptrace & PT_PTRACED) @@ -747,7 +764,20 @@ static void handle_stop_signal(int sig, struct task_struct *p) p->group_leader->real_parent, CLD_CONTINUED); spin_lock(&p->sighand->siglock); + } else { + /* + * We are not stopped, but there could be a stop + * signal in the middle of being processed after + * being removed from the queue. Clear that too. + */ + p->signal->flags = 0; } + } else if (sig == SIGKILL) { + /* + * Make sure that any pending stop signal already dequeued + * is undone by the wakeup for SIGKILL. + */ + p->signal->flags = 0; } } @@ -772,14 +802,8 @@ static int send_signal(int sig, struct siginfo *info, struct task_struct *t, make sure at least one signal gets delivered and don't pass on the info struct. */ - if (atomic_read(&t->user->sigpending) < - t->rlim[RLIMIT_SIGPENDING].rlim_cur) - q = kmem_cache_alloc(sigqueue_cachep, GFP_ATOMIC); - + q = __sigqueue_alloc(t, GFP_ATOMIC); if (q) { - q->flags = 0; - q->user = get_uid(t->user); - atomic_inc(&q->user->sigpending); list_add_tail(&q->list, &signals->list); switch ((unsigned long) info) { case 0: @@ -832,10 +856,7 @@ specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t) if (!irqs_disabled()) BUG(); -#ifdef CONFIG_SMP - if (!spin_is_locked(&t->sighand->siglock)) - BUG(); -#endif + assert_spin_locked(&t->sighand->siglock); if (((unsigned long)info > 2) && (info->si_code == SI_TIMER)) /* @@ -919,12 +940,12 @@ __group_complete_signal(int sig, struct task_struct *p) struct task_struct *t; /* - * Don't bother zombies and stopped tasks (but - * SIGKILL will punch through stopped state) + * Don't bother traced and stopped tasks (but + * SIGKILL will punch through that). */ - mask = TASK_DEAD | TASK_ZOMBIE | TASK_TRACED; - if (sig != SIGKILL) - mask |= TASK_STOPPED; + mask = TASK_STOPPED | TASK_TRACED; + if (sig == SIGKILL) + mask = 0; /* * Now find a thread we can wake up to take the signal off the queue. @@ -967,7 +988,7 @@ __group_complete_signal(int sig, struct task_struct *p) * Found a killable thread. If the signal will be fatal, * then start taking the whole group down immediately. */ - if (sig_fatal(p, sig) && !p->signal->group_exit && + if (sig_fatal(p, sig) && !(p->signal->flags & SIGNAL_GROUP_EXIT) && !sigismember(&t->real_blocked, sig) && (sig == SIGKILL || !(t->ptrace & PT_PTRACED))) { /* @@ -980,7 +1001,7 @@ __group_complete_signal(int sig, struct task_struct *p) * running and doing things after a slower * thread has the fatal signal pending. */ - p->signal->group_exit = 1; + p->signal->flags = SIGNAL_GROUP_EXIT; p->signal->group_exit_code = sig; p->signal->group_stop_count = 0; t = p; @@ -1029,10 +1050,7 @@ __group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) { int ret = 0; -#ifdef CONFIG_SMP - if (!spin_is_locked(&p->sighand->siglock)) - BUG(); -#endif + assert_spin_locked(&p->sighand->siglock); handle_stop_signal(sig, p); if (((unsigned long)info > 2) && (info->si_code == SI_TIMER)) @@ -1069,6 +1087,7 @@ void zap_other_threads(struct task_struct *p) { struct task_struct *t; + p->signal->flags = SIGNAL_GROUP_EXIT; p->signal->group_stop_count = 0; if (thread_group_empty(p)) @@ -1078,7 +1097,7 @@ void zap_other_threads(struct task_struct *p) /* * Don't bother with already dead threads */ - if (t->state & (TASK_ZOMBIE|TASK_DEAD)) + if (t->exit_state) continue; /* @@ -1123,7 +1142,7 @@ int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp) { - struct task_struct *p; + struct task_struct *p = NULL; int retval, success; if (pgrp <= 0) @@ -1151,36 +1170,6 @@ kill_pg_info(int sig, struct siginfo *info, pid_t pgrp) return retval; } -/* - * kill_sl_info() sends a signal to the session leader: this is used - * to send SIGHUP to the controlling process of a terminal when - * the connection is lost. - */ - - -int -kill_sl_info(int sig, struct siginfo *info, pid_t sid) -{ - int err, retval = -EINVAL; - struct task_struct *p; - - if (sid <= 0) - goto out; - - retval = -ESRCH; - read_lock(&tasklist_lock); - do_each_task_pid(sid, PIDTYPE_SID, p) { - if (!p->signal->leader) - continue; - err = group_send_sig_info(sig, info, p); - if (retval) - retval = err; - } while_each_task_pid(sid, PIDTYPE_SID, p); - read_unlock(&tasklist_lock); -out: - return retval; -} - int kill_proc_info(int sig, struct siginfo *info, pid_t pid) { @@ -1316,12 +1305,6 @@ kill_pg(pid_t pgrp, int sig, int priv) return kill_pg_info(sig, (void *)(long)(priv != 0), pgrp); } -int -kill_sl(pid_t sess, int sig, int priv) -{ - return kill_sl_info(sig, (void *)(long)(priv != 0), sess); -} - int kill_proc(pid_t pid, int sig, int priv) { @@ -1342,7 +1325,7 @@ struct sigqueue *sigqueue_alloc(void) { struct sigqueue *q; - if ((q = __sigqueue_alloc())) + if ((q = __sigqueue_alloc(current, GFP_KERNEL))) q->flags |= SIGQUEUE_PREALLOC; return(q); } @@ -1457,28 +1440,12 @@ out: } /* - * Joy. Or not. Pthread wants us to wake up every thread - * in our parent group. + * Wake up any threads in the parent blocked in wait* syscalls. */ -static void __wake_up_parent(struct task_struct *p, +static inline void __wake_up_parent(struct task_struct *p, struct task_struct *parent) { - struct task_struct *tsk = parent; - - /* - * Fortunately this is not necessary for thread groups: - */ - if (p->tgid == tsk->tgid) { - wake_up_interruptible_sync(&tsk->wait_chldexit); - return; - } - - do { - wake_up_interruptible_sync(&tsk->wait_chldexit); - tsk = next_thread(tsk); - if (tsk->signal != parent->signal) - BUG(); - } while (tsk != parent); + wake_up_interruptible_sync(&parent->signal->wait_chldexit); } /* @@ -1492,8 +1459,7 @@ void do_notify_parent(struct task_struct *tsk, int sig) unsigned long flags; struct sighand_struct *psig; - if (sig == -1) - BUG(); + BUG_ON(sig == -1); /* do_notify_parent_cldstop should have been called instead. */ BUG_ON(tsk->state & (TASK_STOPPED|TASK_TRACED)); @@ -1507,8 +1473,10 @@ void do_notify_parent(struct task_struct *tsk, int sig) info.si_uid = tsk->uid; /* FIXME: find out whether or not this is supposed to be c*time. */ - info.si_utime = tsk->utime + tsk->signal->utime; - info.si_stime = tsk->stime + tsk->signal->stime; + info.si_utime = cputime_to_jiffies(cputime_add(tsk->utime, + tsk->signal->utime)); + info.si_stime = cputime_to_jiffies(cputime_add(tsk->stime, + tsk->signal->stime)); info.si_status = tsk->exit_code & 0x7f; if (tsk->exit_code & 0x80) @@ -1564,8 +1532,8 @@ do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent, info.si_uid = tsk->uid; /* FIXME: find out whether or not this is supposed to be c*time. */ - info.si_utime = tsk->utime; - info.si_stime = tsk->stime; + info.si_utime = cputime_to_jiffies(tsk->utime); + info.si_stime = cputime_to_jiffies(tsk->stime); info.si_code = why; switch (why) { @@ -1601,11 +1569,12 @@ do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent, * We always set current->last_siginfo while stopped here. * That makes it a way to test a stopped process for * being ptrace-stopped vs being job-control-stopped. + * + * If we actually decide not to stop at all because the tracer is gone, + * we leave nostop_code in current->exit_code. */ -static void ptrace_stop(int exit_code, siginfo_t *info) +static void ptrace_stop(int exit_code, int nostop_code, siginfo_t *info) { - BUG_ON(!(current->ptrace & PT_PTRACED)); - /* * If there is a group stop in progress, * we must participate in the bookkeeping. @@ -1620,9 +1589,24 @@ static void ptrace_stop(int exit_code, siginfo_t *info) set_current_state(TASK_TRACED); spin_unlock_irq(¤t->sighand->siglock); read_lock(&tasklist_lock); - do_notify_parent_cldstop(current, current->parent, CLD_TRAPPED); - read_unlock(&tasklist_lock); - schedule(); + if (likely(current->ptrace & PT_PTRACED) && + likely(current->parent != current->real_parent || + !(current->ptrace & PT_ATTACHED)) && + (likely(current->parent->signal != current->signal) || + !unlikely(current->signal->flags & SIGNAL_GROUP_EXIT))) { + do_notify_parent_cldstop(current, current->parent, + CLD_TRAPPED); + read_unlock(&tasklist_lock); + schedule(); + } else { + /* + * By the time we got the lock, our tracer went away. + * Don't stop here. + */ + read_unlock(&tasklist_lock); + set_current_state(TASK_RUNNING); + current->exit_code = nostop_code; + } /* * We are back. Now reacquire the siglock before touching @@ -1653,7 +1637,7 @@ void ptrace_notify(int exit_code) /* Let the debugger run. */ spin_lock_irq(¤t->sighand->siglock); - ptrace_stop(exit_code, &info); + ptrace_stop(exit_code, 0, &info); spin_unlock_irq(¤t->sighand->siglock); } @@ -1691,15 +1675,18 @@ finish_stop(int stop_count) /* * This performs the stopping for SIGSTOP and other stop signals. * We have to stop all threads in the thread group. + * Returns nonzero if we've actually stopped and released the siglock. + * Returns zero if we didn't stop and still hold the siglock. */ -static void +static int do_signal_stop(int signr) { struct signal_struct *sig = current->signal; struct sighand_struct *sighand = current->sighand; int stop_count = -1; - /* spin_lock_irq(&sighand->siglock) is now done in caller */ + if (!likely(sig->flags & SIGNAL_STOP_DEQUEUED)) + return 0; if (sig->group_stop_count > 0) { /* @@ -1711,7 +1698,7 @@ do_signal_stop(int signr) current->exit_code = signr; set_current_state(TASK_STOPPED); if (stop_count == 0) - sig->stop_state = 1; + sig->flags = SIGNAL_STOP_STOPPED; spin_unlock_irq(&sighand->siglock); } else if (thread_group_empty(current)) { @@ -1720,7 +1707,7 @@ do_signal_stop(int signr) */ current->exit_code = current->signal->group_exit_code = signr; set_current_state(TASK_STOPPED); - sig->stop_state = 1; + sig->flags = SIGNAL_STOP_STOPPED; spin_unlock_irq(&sighand->siglock); } else { @@ -1741,25 +1728,17 @@ do_signal_stop(int signr) read_lock(&tasklist_lock); spin_lock_irq(&sighand->siglock); - if (unlikely(sig->group_exit)) { - /* - * There is a group exit in progress now. - * We'll just ignore the stop and process the - * associated fatal signal. - */ - spin_unlock_irq(&sighand->siglock); - read_unlock(&tasklist_lock); - return; - } - - if (unlikely(sig_avoid_stop_race())) { + if (!likely(sig->flags & SIGNAL_STOP_DEQUEUED)) { /* - * Either a SIGCONT or a SIGKILL signal was - * posted in the siglock-not-held window. + * Another stop or continue happened while we + * didn't have the lock. We can just swallow this + * signal now. If we raced with a SIGCONT, that + * should have just cleared it now. If we raced + * with another processor delivering a stop signal, + * then the SIGCONT that wakes us up should clear it. */ - spin_unlock_irq(&sighand->siglock); read_unlock(&tasklist_lock); - return; + return 0; } if (sig->group_stop_count == 0) { @@ -1787,13 +1766,14 @@ do_signal_stop(int signr) current->exit_code = signr; set_current_state(TASK_STOPPED); if (stop_count == 0) - sig->stop_state = 1; + sig->flags = SIGNAL_STOP_STOPPED; spin_unlock_irq(&sighand->siglock); read_unlock(&tasklist_lock); } finish_stop(stop_count); + return 1; } /* @@ -1815,7 +1795,7 @@ static inline int handle_group_stop(void) return 0; } - if (current->signal->group_exit) + if (current->signal->flags & SIGNAL_GROUP_EXIT) /* * Group stop is so another thread can do a core dump, * or else we are racing against a death signal. @@ -1829,7 +1809,7 @@ static inline int handle_group_stop(void) */ stop_count = --current->signal->group_stop_count; if (stop_count == 0) - current->signal->stop_state = 1; + current->signal->flags = SIGNAL_STOP_STOPPED; current->exit_code = current->signal->group_exit_code; set_current_state(TASK_STOPPED); spin_unlock_irq(¤t->sighand->siglock); @@ -1861,7 +1841,7 @@ relock: ptrace_signal_deliver(regs, cookie); /* Let the debugger run. */ - ptrace_stop(signr, info); + ptrace_stop(signr, signr, info); /* We're back. Did the debugger cancel the sig? */ signr = current->exit_code; @@ -1912,6 +1892,11 @@ relock: if (current->pid == 1) continue; + /* virtual init is protected against user signals */ + if ((info->si_code == SI_USER) && + vx_current_initpid(current->pid)) + continue; + if (sig_kernel_stop(signr)) { /* * The default action is to stop all threads in @@ -1923,28 +1908,27 @@ relock: * This allows an intervening SIGCONT to be posted. * We need to check for that and bail out if necessary. */ - if (signr == SIGSTOP) { - do_signal_stop(signr); /* releases siglock */ - goto relock; - } - spin_unlock_irq(¤t->sighand->siglock); + if (signr != SIGSTOP) { + spin_unlock_irq(¤t->sighand->siglock); - /* signals can be posted during this window */ + /* signals can be posted during this window */ - if (is_orphaned_pgrp(process_group(current))) - goto relock; + if (is_orphaned_pgrp(process_group(current))) + goto relock; - spin_lock_irq(¤t->sighand->siglock); - if (unlikely(sig_avoid_stop_race())) { - /* - * Either a SIGCONT or a SIGKILL signal was - * posted in the siglock-not-held window. - */ - continue; + spin_lock_irq(¤t->sighand->siglock); } - do_signal_stop(signr); /* releases siglock */ - goto relock; + if (likely(do_signal_stop(signr))) { + /* It released the siglock. */ + goto relock; + } + + /* + * We didn't actually stop, due to a race + * with SIGCONT or something like that. + */ + continue; } spin_unlock_irq(¤t->sighand->siglock); @@ -1953,22 +1937,16 @@ relock: * Anything else is fatal, maybe with a core dump. */ current->flags |= PF_SIGNALED; - if (sig_kernel_coredump(signr) && - do_coredump((long)signr, signr, regs)) { + if (sig_kernel_coredump(signr)) { /* - * That killed all other threads in the group and - * synchronized with their demise, so there can't - * be any more left to kill now. The group_exit - * flags are set by do_coredump. Note that - * thread_group_empty won't always be true yet, - * because those threads were blocked in __exit_mm - * and we just let them go to finish dying. + * If it was able to dump core, this kills all + * other threads in the group and synchronizes with + * their demise. If we lost the race with another + * thread getting here, it set group_exit_code + * first and our do_group_exit call below will use + * that value and ignore the one we pass it. */ - const int code = signr | 0x80; - BUG_ON(!current->signal->group_exit); - BUG_ON(current->signal->group_exit_code != code); - do_exit(code); - /* NOTREACHED */ + do_coredump((long)signr, signr, regs); } /* @@ -1987,21 +1965,11 @@ EXPORT_SYMBOL(recalc_sigpending); EXPORT_SYMBOL_GPL(dequeue_signal); EXPORT_SYMBOL(flush_signals); EXPORT_SYMBOL(force_sig); -EXPORT_SYMBOL(force_sig_info); EXPORT_SYMBOL(kill_pg); -EXPORT_SYMBOL(kill_pg_info); EXPORT_SYMBOL(kill_proc); -EXPORT_SYMBOL(kill_proc_info); -EXPORT_SYMBOL(kill_sl); -EXPORT_SYMBOL(kill_sl_info); EXPORT_SYMBOL(ptrace_notify); EXPORT_SYMBOL(send_sig); EXPORT_SYMBOL(send_sig_info); -EXPORT_SYMBOL(send_group_sig_info); -EXPORT_SYMBOL(sigqueue_alloc); -EXPORT_SYMBOL(sigqueue_free); -EXPORT_SYMBOL(send_sigqueue); -EXPORT_SYMBOL(send_group_sigqueue); EXPORT_SYMBOL(sigprocmask); EXPORT_SYMBOL(block_all_signals); EXPORT_SYMBOL(unblock_all_signals);