Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/agpgart
[pandora-kernel.git] / arch / powerpc / kernel / signal_64.c
index 5462bef..497a5d3 100644 (file)
@@ -60,49 +60,13 @@ struct rt_sigframe {
        struct ucontext uc;
        unsigned long _unused[2];
        unsigned int tramp[TRAMP_SIZE];
-       struct siginfo *pinfo;
-       void *puc;
+       struct siginfo __user *pinfo;
+       void __user *puc;
        struct siginfo info;
        /* 64 bit ABI allows for 288 bytes below sp before decrementing it. */
        char abigap[288];
 } __attribute__ ((aligned (16)));
 
-
-/*
- * Atomically swap in the new signal mask, and wait for a signal.
- */
-long sys_rt_sigsuspend(sigset_t __user *unewset, 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 (copy_from_user(&newset, unewset, sizeof(newset)))
-               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;
-               }
-       }
-}
-
 long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, unsigned long r5,
                     unsigned long r6, unsigned long r7, unsigned long r8,
                     struct pt_regs *regs)
@@ -207,10 +171,20 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig,
 
        if (!sig)
                regs->gpr[13] = save_r13;
-       err |= __copy_from_user(&current->thread.fpr, &sc->fp_regs, FP_REGS_SIZE);
        if (set != NULL)
                err |=  __get_user(set->sig[0], &sc->oldmask);
 
+       /*
+        * Do this before updating the thread state in
+        * current->thread.fpr/vr.  That way, if we get preempted
+        * and another task grabs the FPU/Altivec, 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();
+
+       err |= __copy_from_user(&current->thread.fpr, &sc->fp_regs, FP_REGS_SIZE);
+
 #ifdef CONFIG_ALTIVEC
        err |= __get_user(v_regs, &sc->v_regs);
        err |= __get_user(msr, &sc->gp_regs[PT_MSR]);
@@ -229,14 +203,6 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig,
                current->thread.vrsave = 0;
 #endif /* CONFIG_ALTIVEC */
 
-#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;
-       preempt_enable();
-#endif
        /* Force reload of FP/VEC */
        regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1 | MSR_VEC);
 
@@ -554,11 +520,15 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs)
        if (test_thread_flag(TIF_32BIT))
                return do_signal32(oldset, regs);
 
-       if (!oldset)
+       if (test_thread_flag(TIF_RESTORE_SIGMASK))
+               oldset = &current->saved_sigmask;
+       else if (!oldset)
                oldset = &current->blocked;
 
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
+               int ret;
+
                /* Whee!  Actually deliver the signal.  */
                if (TRAP(regs) == 0x0C00)
                        syscall_restart(regs, &ka);
@@ -571,7 +541,14 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs)
                if (current->thread.dabr)
                        set_dabr(current->thread.dabr);
 
-               return handle_signal(signr, &ka, &info, oldset, regs);
+               ret = handle_signal(signr, &ka, &info, oldset, regs);
+
+               /* If a signal was successfully delivered, the saved sigmask is in
+                  its frame, and we can clear the TIF_RESTORE_SIGMASK flag */
+               if (ret && test_thread_flag(TIF_RESTORE_SIGMASK))
+                       clear_thread_flag(TIF_RESTORE_SIGMASK);
+
+               return ret;
        }
 
        if (TRAP(regs) == 0x0C00) {     /* System Call! */
@@ -587,6 +564,11 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs)
                        regs->result = 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;
 }