]> Pileus Git - ~andy/linux/blobdiff - kernel/signal.c
[PATCH] relax sig_needs_tasklist()
[~andy/linux] / kernel / signal.c
index 5dafbd36d62e0e1a7245dfa35befcfa28876994c..e99ec2f891a0700861f0d23e790be37dc2998c40 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/security.h>
 #include <linux/syscalls.h>
 #include <linux/ptrace.h>
-#include <linux/posix-timers.h>
 #include <linux/signal.h>
 #include <linux/audit.h>
 #include <linux/capability.h>
@@ -147,6 +146,8 @@ static kmem_cache_t *sigqueue_cachep;
 #define sig_kernel_stop(sig) \
                (((sig) < SIGRTMIN)  && T(sig, SIG_KERNEL_STOP_MASK))
 
+#define sig_needs_tasklist(sig)        ((sig) == SIGCONT)
+
 #define sig_user_defined(t, signr) \
        (((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_DFL) &&  \
         ((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_IGN))
@@ -283,7 +284,7 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
        return(q);
 }
 
-static inline void __sigqueue_free(struct sigqueue *q)
+static void __sigqueue_free(struct sigqueue *q)
 {
        if (q->flags & SIGQUEUE_PREALLOC)
                return;
@@ -292,7 +293,7 @@ static inline void __sigqueue_free(struct sigqueue *q)
        kmem_cache_free(sigqueue_cachep, q);
 }
 
-static void flush_sigqueue(struct sigpending *queue)
+void flush_sigqueue(struct sigpending *queue)
 {
        struct sigqueue *q;
 
@@ -307,9 +308,7 @@ static void flush_sigqueue(struct sigpending *queue)
 /*
  * Flush all pending signals for a task.
  */
-
-void
-flush_signals(struct task_struct *t)
+void flush_signals(struct task_struct *t)
 {
        unsigned long flags;
 
@@ -320,109 +319,6 @@ flush_signals(struct task_struct *t)
        spin_unlock_irqrestore(&t->sighand->siglock, flags);
 }
 
-/*
- * This function expects the tasklist_lock write-locked.
- */
-void __exit_sighand(struct task_struct *tsk)
-{
-       struct sighand_struct * sighand = tsk->sighand;
-
-       /* Ok, we're done with the signal handlers */
-       tsk->sighand = NULL;
-       if (atomic_dec_and_test(&sighand->count))
-               sighand_free(sighand);
-}
-
-void exit_sighand(struct task_struct *tsk)
-{
-       write_lock_irq(&tasklist_lock);
-       rcu_read_lock();
-       if (tsk->sighand != NULL) {
-               struct sighand_struct *sighand = rcu_dereference(tsk->sighand);
-               spin_lock(&sighand->siglock);
-               __exit_sighand(tsk);
-               spin_unlock(&sighand->siglock);
-       }
-       rcu_read_unlock();
-       write_unlock_irq(&tasklist_lock);
-}
-
-/*
- * This function expects the tasklist_lock write-locked.
- */
-void __exit_signal(struct task_struct *tsk)
-{
-       struct signal_struct * sig = tsk->signal;
-       struct sighand_struct * sighand;
-
-       if (!sig)
-               BUG();
-       if (!atomic_read(&sig->count))
-               BUG();
-       rcu_read_lock();
-       sighand = rcu_dereference(tsk->sighand);
-       spin_lock(&sighand->siglock);
-       posix_cpu_timers_exit(tsk);
-       if (atomic_dec_and_test(&sig->count)) {
-               posix_cpu_timers_exit_group(tsk);
-               tsk->signal = NULL;
-               __exit_sighand(tsk);
-               spin_unlock(&sighand->siglock);
-               flush_sigqueue(&sig->shared_pending);
-       } else {
-               /*
-                * If there is any task waiting for the group exit
-                * then notify it:
-                */
-               if (sig->group_exit_task && atomic_read(&sig->count) == sig->notify_count) {
-                       wake_up_process(sig->group_exit_task);
-                       sig->group_exit_task = NULL;
-               }
-               if (tsk == sig->curr_target)
-                       sig->curr_target = next_thread(tsk);
-               tsk->signal = NULL;
-               /*
-                * Accumulate here the counters for all threads but the
-                * group leader as they die, so they can be added into
-                * the process-wide totals when those are taken.
-                * The group leader stays around as a zombie as long
-                * as there are other threads.  When it gets reaped,
-                * the exit.c code will add its counts into these totals.
-                * We won't ever get here for the group leader, since it
-                * will have been the last reference on the signal_struct.
-                */
-               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;
-               sig->nivcsw += tsk->nivcsw;
-               sig->sched_time += tsk->sched_time;
-               __exit_sighand(tsk);
-               spin_unlock(&sighand->siglock);
-               sig = NULL;     /* Marker for below.  */
-       }
-       rcu_read_unlock();
-       clear_tsk_thread_flag(tsk,TIF_SIGPENDING);
-       flush_sigqueue(&tsk->pending);
-       if (sig) {
-               /*
-                * We are cleaning up the signal_struct here.
-                */
-               exit_thread_group_keys(sig);
-               kmem_cache_free(signal_cachep, sig);
-       }
-}
-
-void exit_signal(struct task_struct *tsk)
-{
-       atomic_dec(&tsk->signal->live);
-
-       write_lock_irq(&tasklist_lock);
-       __exit_signal(tsk);
-       write_unlock_irq(&tasklist_lock);
-}
-
 /*
  * Flush all handlers for a task.
  */
@@ -1120,27 +1016,37 @@ void zap_other_threads(struct task_struct *p)
 /*
  * Must be called under rcu_read_lock() or with tasklist_lock read-held.
  */
+struct sighand_struct *lock_task_sighand(struct task_struct *tsk, unsigned long *flags)
+{
+       struct sighand_struct *sighand;
+
+       for (;;) {
+               sighand = rcu_dereference(tsk->sighand);
+               if (unlikely(sighand == NULL))
+                       break;
+
+               spin_lock_irqsave(&sighand->siglock, *flags);
+               if (likely(sighand == tsk->sighand))
+                       break;
+               spin_unlock_irqrestore(&sighand->siglock, *flags);
+       }
+
+       return sighand;
+}
+
 int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
 {
        unsigned long flags;
-       struct sighand_struct *sp;
        int ret;
 
-retry:
        ret = check_kill_permission(sig, info, p);
-       if (!ret && sig && (sp = rcu_dereference(p->sighand))) {
-               spin_lock_irqsave(&sp->siglock, flags);
-               if (p->sighand != sp) {
-                       spin_unlock_irqrestore(&sp->siglock, flags);
-                       goto retry;
-               }
-               if ((atomic_read(&sp->count) == 0) ||
-                               (atomic_read(&p->usage) == 0)) {
-                       spin_unlock_irqrestore(&sp->siglock, flags);
-                       return -ESRCH;
+
+       if (!ret && sig) {
+               ret = -ESRCH;
+               if (lock_task_sighand(p, &flags)) {
+                       ret = __group_send_sig_info(sig, info, p);
+                       unlock_task_sighand(p, &flags);
                }
-               ret = __group_send_sig_info(sig, info, p);
-               spin_unlock_irqrestore(&sp->siglock, flags);
        }
 
        return ret;
@@ -1189,7 +1095,7 @@ kill_proc_info(int sig, struct siginfo *info, pid_t pid)
        struct task_struct *p;
 
        rcu_read_lock();
-       if (unlikely(sig_kernel_stop(sig) || sig == SIGCONT)) {
+       if (unlikely(sig_needs_tasklist(sig))) {
                read_lock(&tasklist_lock);
                acquired_tasklist_lock = 1;
        }
@@ -1922,6 +1828,8 @@ int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
        sigset_t *mask = &current->blocked;
        int signr = 0;
 
+       try_to_freeze();
+
 relock:
        spin_lock_irq(&current->sighand->siglock);
        for (;;) {
@@ -1988,7 +1896,7 @@ relock:
                        continue;
 
                /* Init gets no signals it doesn't want.  */
-               if (current->pid == 1)
+               if (current == child_reaper)
                        continue;
 
                if (sig_kernel_stop(signr)) {
@@ -2099,10 +2007,11 @@ long do_no_restart_syscall(struct restart_block *param)
 int sigprocmask(int how, sigset_t *set, sigset_t *oldset)
 {
        int error;
-       sigset_t old_block;
 
        spin_lock_irq(&current->sighand->siglock);
-       old_block = current->blocked;
+       if (oldset)
+               *oldset = current->blocked;
+
        error = 0;
        switch (how) {
        case SIG_BLOCK:
@@ -2119,8 +2028,7 @@ int sigprocmask(int how, sigset_t *set, sigset_t *oldset)
        }
        recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
-       if (oldset)
-               *oldset = old_block;
+
        return error;
 }
 
@@ -2307,7 +2215,6 @@ sys_rt_sigtimedwait(const sigset_t __user *uthese,
 
                        timeout = schedule_timeout_interruptible(timeout);
 
-                       try_to_freeze();
                        spin_lock_irq(&current->sighand->siglock);
                        sig = dequeue_signal(current, &these, &info);
                        current->blocked = current->real_blocked;
@@ -2430,7 +2337,7 @@ sys_rt_sigqueueinfo(int pid, int sig, siginfo_t __user *uinfo)
 }
 
 int
-do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact)
+do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
 {
        struct k_sigaction *k;
        sigset_t mask;
@@ -2454,6 +2361,8 @@ do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact)
                *oact = *k;
 
        if (act) {
+               sigdelsetmask(&act->sa.sa_mask,
+                             sigmask(SIGKILL) | sigmask(SIGSTOP));
                /*
                 * POSIX 3.3.1.3:
                 *  "Setting a signal action to SIG_IGN for a signal that is
@@ -2479,8 +2388,6 @@ do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact)
                        read_lock(&tasklist_lock);
                        spin_lock_irq(&t->sighand->siglock);
                        *k = *act;
-                       sigdelsetmask(&k->sa.sa_mask,
-                                     sigmask(SIGKILL) | sigmask(SIGSTOP));
                        sigemptyset(&mask);
                        sigaddset(&mask, sig);
                        rm_from_queue_full(&mask, &t->signal->shared_pending);
@@ -2495,8 +2402,6 @@ do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact)
                }
 
                *k = *act;
-               sigdelsetmask(&k->sa.sa_mask,
-                             sigmask(SIGKILL) | sigmask(SIGSTOP));
        }
 
        spin_unlock_irq(&current->sighand->siglock);
@@ -2702,6 +2607,7 @@ sys_signal(int sig, __sighandler_t handler)
 
        new_sa.sa.sa_handler = handler;
        new_sa.sa.sa_flags = SA_ONESHOT | SA_NOMASK;
+       sigemptyset(&new_sa.sa.sa_mask);
 
        ret = do_sigaction(sig, &new_sa, &old_sa);
 
@@ -2721,6 +2627,32 @@ sys_pause(void)
 
 #endif
 
+#ifdef __ARCH_WANT_SYS_RT_SIGSUSPEND
+asmlinkage long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize)
+{
+       sigset_t newset;
+
+       /* XXX: Don't preclude handling different sized sigset_t's.  */
+       if (sigsetsize != sizeof(sigset_t))
+               return -EINVAL;
+
+       if (copy_from_user(&newset, unewset, sizeof(newset)))
+               return -EFAULT;
+       sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP));
+
+       spin_lock_irq(&current->sighand->siglock);
+       current->saved_sigmask = current->blocked;
+       current->blocked = newset;
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+
+       current->state = TASK_INTERRUPTIBLE;
+       schedule();
+       set_thread_flag(TIF_RESTORE_SIGMASK);
+       return -ERESTARTNOHAND;
+}
+#endif /* __ARCH_WANT_SYS_RT_SIGSUSPEND */
+
 void __init signals_init(void)
 {
        sigqueue_cachep =