Merge branch 'drm-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied...
[powerpc.git] / arch / powerpc / kernel / signal_32.c
index d3f0b6d..c6d0595 100644 (file)
@@ -252,67 +252,19 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs);
 /*
  * Atomically swap in the new signal mask, and wait for a signal.
  */
-long sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7,
-              struct pt_regs *regs)
+long sys_sigsuspend(old_sigset_t mask)
 {
-       sigset_t saveset;
-
        mask &= _BLOCKABLE;
        spin_lock_irq(&current->sighand->siglock);
-       saveset = current->blocked;
+       current->saved_sigmask = current->blocked;
        siginitset(&current->blocked, mask);
        recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
 
-       regs->result = -EINTR;
-       regs->gpr[3] = EINTR;
-       regs->ccr |= 0x10000000;
-       while (1) {
-               current->state = TASK_INTERRUPTIBLE;
-               schedule();
-               if (do_signal(&saveset, regs)) {
-                       set_thread_flag(TIF_RESTOREALL);
-                       return 0;
-               }
-       }
-}
-
-long sys_rt_sigsuspend(
-#ifdef CONFIG_PPC64
-               compat_sigset_t __user *unewset,
-#else
-               sigset_t __user *unewset,
-#endif
-               size_t sigsetsize, int p3, int p4,
-               int p6, int p7, struct pt_regs *regs)
-{
-       sigset_t saveset, newset;
-
-       /* XXX: Don't preclude handling different sized sigset_t's.  */
-       if (sigsetsize != sizeof(sigset_t))
-               return -EINVAL;
-
-       if (get_sigset_t(&newset, unewset))
-               return -EFAULT;
-       sigdelsetmask(&newset, ~_BLOCKABLE);
-
-       spin_lock_irq(&current->sighand->siglock);
-       saveset = current->blocked;
-       current->blocked = newset;
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
-
-       regs->result = -EINTR;
-       regs->gpr[3] = EINTR;
-       regs->ccr |= 0x10000000;
-       while (1) {
-               current->state = TASK_INTERRUPTIBLE;
-               schedule();
-               if (do_signal(&saveset, regs)) {
-                       set_thread_flag(TIF_RESTOREALL);
-                       return 0;
-               }
-       }
+       current->state = TASK_INTERRUPTIBLE;
+       schedule();
+       set_thread_flag(TIF_RESTORE_SIGMASK);
+       return -ERESTARTNOHAND;
 }
 
 #ifdef CONFIG_PPC32
@@ -497,6 +449,15 @@ static long restore_user_regs(struct pt_regs *regs,
        if (err)
                return 1;
 
+       /*
+        * Do this before updating the thread state in
+        * current->thread.fpr/vr/evr.  That way, if we get preempted
+        * and another task grabs the FPU/Altivec/SPE, it won't be
+        * tempted to save the current CPU state into the thread_struct
+        * and corrupt what we are writing there.
+        */
+       discard_lazy_cpu_state();
+
        /* force the process to reload the FP registers from
           current->thread when it next does FP instructions */
        regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1);
@@ -538,18 +499,6 @@ static long restore_user_regs(struct pt_regs *regs,
                return 1;
 #endif /* CONFIG_SPE */
 
-#ifndef CONFIG_SMP
-       preempt_disable();
-       if (last_task_used_math == current)
-               last_task_used_math = NULL;
-       if (last_task_used_altivec == current)
-               last_task_used_altivec = NULL;
-#ifdef CONFIG_SPE
-       if (last_task_used_spe == current)
-               last_task_used_spe = NULL;
-#endif
-       preempt_enable();
-#endif
        return 0;
 }
 
@@ -1177,7 +1126,7 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs)
 {
        siginfo_t info;
        struct k_sigaction ka;
-       unsigned int frame, newsp;
+       unsigned int newsp;
        int signr, ret;
 
 #ifdef CONFIG_PPC32
@@ -1188,11 +1137,11 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs)
        }
 #endif
 
-       if (!oldset)
+       if (test_thread_flag(TIF_RESTORE_SIGMASK))
+               oldset = &current->saved_sigmask;
+       else if (!oldset)
                oldset = &current->blocked;
 
-       newsp = frame = 0;
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
 #ifdef CONFIG_PPC32
 no_signal:
@@ -1222,8 +1171,14 @@ no_signal:
                }
        }
 
-       if (signr == 0)
+       if (signr == 0) {
+               /* No signal to deliver -- put the saved sigmask back */
+               if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
+                       clear_thread_flag(TIF_RESTORE_SIGMASK);
+                       sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
+               }
                return 0;               /* no signals delivered */
+       }
 
        if ((ka.sa.sa_flags & SA_ONSTACK) && current->sas_ss_size
            && !on_sig_stack(regs->gpr[1]))
@@ -1256,6 +1211,10 @@ no_signal:
                        sigaddset(&current->blocked, signr);
                recalc_sigpending();
                spin_unlock_irq(&current->sighand->siglock);
+               /* A signal was successfully delivered; the saved sigmask is in
+                  its frame, and we can clear the TIF_RESTORE_SIGMASK flag */
+               if (test_thread_flag(TIF_RESTORE_SIGMASK))
+                       clear_thread_flag(TIF_RESTORE_SIGMASK);
        }
 
        return ret;