diff options
Diffstat (limited to 'arch/sh/kernel/signal.c')
-rw-r--r-- | arch/sh/kernel/signal.c | 137 |
1 files changed, 65 insertions, 72 deletions
diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c index eb6a3b97e46c..2f1c9545b49f 100644 --- a/arch/sh/kernel/signal.c +++ b/arch/sh/kernel/signal.c @@ -29,13 +29,10 @@ #include <asm/pgtable.h> #include <asm/cacheflush.h> -#define DEBUG_SIG 0 +#undef DEBUG #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset, - unsigned int save_r0); - /* * Atomically swap in the new signal mask, and wait for a signal. */ @@ -44,51 +41,17 @@ sys_sigsuspend(old_sigset_t mask, unsigned long r5, unsigned long r6, unsigned long r7, struct pt_regs regs) { - 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.regs[0] = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(®s, &saveset, regs.regs[0])) - return -EINTR; - } -} - -asmlinkage int -sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, - unsigned long r6, unsigned long r7, - 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.regs[0] = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(®s, &saveset, regs.regs[0])) - return -EINTR; - } + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; } asmlinkage int @@ -349,7 +312,7 @@ get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) return (void __user *)((sp - frame_size) & -8ul); } -static void setup_frame(int sig, struct k_sigaction *ka, +static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *regs) { struct sigframe __user *frame; @@ -369,10 +332,9 @@ static void setup_frame(int sig, struct k_sigaction *ka, err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); - if (_NSIG_WORDS > 1) { + if (_NSIG_WORDS > 1) err |= __copy_to_user(frame->extramask, &set->sig[1], sizeof(frame->extramask)); - } /* Set up to return from userspace. If provided, use a stub already in userspace. */ @@ -403,21 +365,22 @@ static void setup_frame(int sig, struct k_sigaction *ka, set_fs(USER_DS); -#if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", - current->comm, current->pid, frame, regs->pc, regs->pr); -#endif + pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", + current->comm, current->pid, frame, regs->pc, regs->pr); flush_cache_sigtramp(regs->pr); + if ((-regs->pr & (L1_CACHE_BYTES-1)) < sizeof(frame->retcode)) flush_cache_sigtramp(regs->pr + L1_CACHE_BYTES); - return; + + return 0; give_sigsegv: force_sigsegv(sig, current); + return -EFAULT; } -static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, +static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *regs) { struct rt_sigframe __user *frame; @@ -478,28 +441,31 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, set_fs(USER_DS); -#if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", - current->comm, current->pid, frame, regs->pc, regs->pr); -#endif + pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", + current->comm, current->pid, frame, regs->pc, regs->pr); flush_cache_sigtramp(regs->pr); + if ((-regs->pr & (L1_CACHE_BYTES-1)) < sizeof(frame->retcode)) flush_cache_sigtramp(regs->pr + L1_CACHE_BYTES); - return; + + return 0; give_sigsegv: force_sigsegv(sig, current); + return -EFAULT; } /* * 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 ret; + /* Are we from a system call? */ if (regs->tra >= 0) { /* If so, check system call restarting.. */ @@ -540,19 +506,23 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, /* Set up the stack frame */ if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs); + ret = setup_rt_frame(sig, ka, info, oldset, regs); else - setup_frame(sig, ka, oldset, regs); + ret = setup_frame(sig, ka, oldset, regs); if (ka->sa.sa_flags & SA_ONESHOT) ka->sa.sa_handler = SIG_DFL; - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + if (ret == 0) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + + return ret; } /* @@ -564,11 +534,12 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, * the kernel can handle, and then we build all the user-level signal handling * stack-frames in one go after that. */ -int do_signal(struct pt_regs *regs, sigset_t *oldset, unsigned int save_r0) +static void do_signal(struct pt_regs *regs, unsigned int save_r0) { siginfo_t info; int signr; struct k_sigaction ka; + sigset_t *oldset; /* * We want the common case to go fast, which @@ -577,19 +548,27 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset, unsigned int save_r0) * if so. */ if (!user_mode(regs)) - return 1; + return; if (try_to_freeze()) goto no_signal; - if (!oldset) + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else oldset = ¤t->blocked; signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { /* Whee! Actually deliver the signal. */ - handle_signal(signr, &ka, &info, oldset, regs); - return 1; + if (handle_signal(signr, &ka, &info, oldset, regs) == 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); + } } no_signal: @@ -606,5 +585,19 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset, unsigned int save_r0) regs->regs[3] = __NR_restart_syscall; } } - return 0; + + /* 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); + } +} + +asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned int save_r0, + __u32 thread_info_flags) +{ + /* deal with pending signal delivery */ + if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) + do_signal(regs, save_r0); } |