return err;
 }
 
-static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset)
+static int kern_do_signal(struct pt_regs *regs)
 {
        struct k_sigaction ka_copy;
        siginfo_t info;
+       sigset_t *oldset;
        int sig, handled_sig = 0;
 
+       if (test_thread_flag(TIF_RESTORE_SIGMASK))
+               oldset = ¤t->saved_sigmask;
+       else
+               oldset = ¤t->blocked;
+
        while((sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL)) > 0){
                handled_sig = 1;
                /* Whee!  Actually deliver the signal.  */
-               if(!handle_signal(regs, sig, &ka_copy, &info, oldset))
+               if(!handle_signal(regs, sig, &ka_copy, &info, oldset)){
+                       /* a signal was successfully delivered; the saved
+                        * sigmask will have been stored in the signal frame,
+                        * and will be restored by sigreturn, so we can simply
+                        * clear the TIF_RESTORE_SIGMASK flag */
+                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
+                               clear_thread_flag(TIF_RESTORE_SIGMASK);
                        break;
+               }
        }
 
        /* Did we come from a system call? */
        if(!handled_sig && (PT_REGS_SYSCALL_NR(regs) >= 0)){
                /* Restart the system call - no handlers present */
-               if(PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOHAND ||
-                  PT_REGS_SYSCALL_RET(regs) == -ERESTARTSYS ||
-                  PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOINTR){
+               switch(PT_REGS_SYSCALL_RET(regs)){
+               case -ERESTARTNOHAND:
+               case -ERESTARTSYS:
+               case -ERESTARTNOINTR:
                        PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
                        PT_REGS_RESTART_SYSCALL(regs);
-               }
-               else if(PT_REGS_SYSCALL_RET(regs) == -ERESTART_RESTARTBLOCK){
+                       break;
+               case -ERESTART_RESTARTBLOCK:
                        PT_REGS_SYSCALL_RET(regs) = __NR_restart_syscall;
                        PT_REGS_RESTART_SYSCALL(regs);
+                       break;
                }
        }
 
        if(current->ptrace & PT_DTRACE)
                current->thread.singlestep_syscall =
                        is_syscall(PT_REGS_IP(¤t->thread.regs));
+
+       /* if there's no signal to deliver, we just put the saved sigmask
+        * back */
+       if (!handled_sig && test_thread_flag(TIF_RESTORE_SIGMASK)) {
+               clear_thread_flag(TIF_RESTORE_SIGMASK);
+               sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL);
+       }
        return(handled_sig);
 }
 
 int do_signal(void)
 {
-       return(kern_do_signal(¤t->thread.regs, ¤t->blocked));
+       return(kern_do_signal(¤t->thread.regs));
 }
 
 /*
  */
 long sys_sigsuspend(int history0, int history1, old_sigset_t mask)
 {
-       sigset_t saveset;
-
        mask &= _BLOCKABLE;
        spin_lock_irq(¤t->sighand->siglock);
-       saveset = current->blocked;
+       current->saved_sigmask = current->blocked;
        siginitset(¤t->blocked, mask);
        recalc_sigpending();
        spin_unlock_irq(¤t->sighand->siglock);
 
-       PT_REGS_SYSCALL_RET(¤t->thread.regs) = -EINTR;
-       while (1) {
-               current->state = TASK_INTERRUPTIBLE;
-               schedule();
-               if(kern_do_signal(¤t->thread.regs, &saveset))
-                       return(-EINTR);
-       }
+       current->state = TASK_INTERRUPTIBLE;
+       schedule();
+       set_thread_flag(TIF_RESTORE_SIGMASK);
+       return -ERESTARTNOHAND;
 }
 
 long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize)
 {
-       sigset_t saveset, newset;
+       sigset_t newset;
 
        /* XXX: Don't preclude handling different sized sigset_t's.  */
        if (sigsetsize != sizeof(sigset_t))
        sigdelsetmask(&newset, ~_BLOCKABLE);
 
        spin_lock_irq(¤t->sighand->siglock);
-       saveset = current->blocked;
+       current->saved_sigmask = current->blocked;
        current->blocked = newset;
        recalc_sigpending();
        spin_unlock_irq(¤t->sighand->siglock);
 
-       PT_REGS_SYSCALL_RET(¤t->thread.regs) = -EINTR;
-       while (1) {
-               current->state = TASK_INTERRUPTIBLE;
-               schedule();
-               if (kern_do_signal(¤t->thread.regs, &saveset))
-                       return(-EINTR);
-       }
+       current->state = TASK_INTERRUPTIBLE;
+       schedule();
+       set_thread_flag(TIF_RESTORE_SIGMASK);
+       return -ERESTARTNOHAND;
 }
 
 long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss)
 {
        return(do_sigaltstack(uss, uoss, PT_REGS_SP(¤t->thread.regs)));
 }
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-file-style: "linux"
- * End:
- */