diff options
Diffstat (limited to 'arch/arm/kernel/signal.c')
-rw-r--r-- | arch/arm/kernel/signal.c | 86 |
1 files changed, 37 insertions, 49 deletions
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index b76fe06d92e7..1423a3419789 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -48,57 +48,22 @@ const unsigned long sigreturn_codes[7] = { MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, }; -static int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall); - /* * atomically swap in the new signal mask, and wait for a signal. */ -asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask, struct pt_regs *regs) +asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, 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); - regs->ARM_r0 = -EINTR; - - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(&saveset, regs, 0)) - return regs->ARM_r0; - } -} - -asmlinkage int -sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, 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(¤t->sighand->siglock); - saveset = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - regs->ARM_r0 = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(&saveset, regs, 0)) - return regs->ARM_r0; - } + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_restore_sigmask(); + return -ERESTARTNOHAND; } asmlinkage int @@ -546,7 +511,7 @@ static inline void setup_syscall_restart(struct pt_regs *regs) /* * OK, we're invoking a handler */ -static void +static int handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, int syscall) @@ -597,7 +562,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, if (ret != 0) { force_sigsegv(sig, tsk); - return; + return ret; } /* @@ -611,6 +576,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, recalc_sigpending(); spin_unlock_irq(&tsk->sighand->siglock); + return 0; } /* @@ -622,7 +588,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, * the kernel can handle, and then we build all the user-level signal handling * stack-frames in one go after that. */ -static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) +static void do_signal(struct pt_regs *regs, int syscall) { struct k_sigaction ka; siginfo_t info; @@ -635,7 +601,7 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) * if so. */ if (!user_mode(regs)) - return 0; + return; if (try_to_freeze()) goto no_signal; @@ -644,9 +610,24 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { - handle_signal(signr, &ka, &info, oldset, regs, syscall); + sigset_t *oldset; + + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else + oldset = ¤t->blocked; + if (handle_signal(signr, &ka, &info, oldset, regs, syscall) == 0) { + /* + * 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); + } single_step_set(current); - return 1; + return; } no_signal: @@ -698,16 +679,23 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) regs->ARM_r0 == -ERESTARTNOINTR) { setup_syscall_restart(regs); } + + /* If there's no signal to deliver, we just put the saved sigmask + * back. + */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } } single_step_set(current); - return 0; } asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) { if (thread_flags & _TIF_SIGPENDING) - do_signal(¤t->blocked, regs, syscall); + do_signal(regs, syscall); if (thread_flags & _TIF_NOTIFY_RESUME) { clear_thread_flag(TIF_NOTIFY_RESUME); |