diff options
Diffstat (limited to 'arch/frv/kernel/signal.c')
-rw-r--r-- | arch/frv/kernel/signal.c | 120 |
1 files changed, 44 insertions, 76 deletions
diff --git a/arch/frv/kernel/signal.c b/arch/frv/kernel/signal.c index 5b7146f..679c1d5 100644 --- a/arch/frv/kernel/signal.c +++ b/arch/frv/kernel/signal.c @@ -35,74 +35,22 @@ struct fdpic_func_descriptor { unsigned long GOT; }; -static int do_signal(sigset_t *oldset); - /* * Atomically swap in the new signal mask, and wait for a signal. */ asmlinkage int 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); - __frame->gr8 = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(&saveset)) - /* return the signal number as the return value of this function - * - this is an utterly evil hack. syscalls should not invoke do_signal() - * as entry.S sets regs->gr8 to the return value of the system call - * - we can't just use sigpending() as we'd have to discard SIG_IGN signals - * and call waitpid() if SIGCHLD needed discarding - * - this only works on the i386 because it passes arguments to the signal - * handler on the stack, and the return value in EAX is effectively - * discarded - */ - return __frame->gr8; - } -} - -asmlinkage int sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize) -{ - 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); - - __frame->gr8 = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(&saveset)) - /* return the signal number as the return value of this function - * - this is an utterly evil hack. syscalls should not invoke do_signal() - * as entry.S sets regs->gr8 to the return value of the system call - * - we can't just use sigpending() as we'd have to discard SIG_IGN signals - * and call waitpid() if SIGCHLD needed discarding - * - this only works on the i386 because it passes arguments to the signal - * handler on the stack, and the return value in EAX is effectively - * discarded - */ - return __frame->gr8; - } + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; } asmlinkage int sys_sigaction(int sig, @@ -372,11 +320,11 @@ static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set) frame->pretcode); #endif - return 1; + return 0; give_sigsegv: force_sig(SIGSEGV, current); - return 0; + return -EFAULT; } /* end setup_frame() */ @@ -471,11 +419,11 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, frame->pretcode); #endif - return 1; + return 0; give_sigsegv: force_sig(SIGSEGV, current); - return 0; + return -EFAULT; } /* end setup_rt_frame() */ @@ -516,7 +464,7 @@ static int handle_signal(unsigned long sig, siginfo_t *info, else ret = setup_frame(sig, ka, oldset); - if (ret) { + if (ret == 0) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); @@ -536,10 +484,11 @@ static int handle_signal(unsigned long sig, siginfo_t *info, * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. */ -static int do_signal(sigset_t *oldset) +static void do_signal(void) { struct k_sigaction ka; siginfo_t info; + sigset_t *oldset; int signr; /* @@ -549,43 +498,62 @@ static int do_signal(sigset_t *oldset) * if so. */ if (!user_mode(__frame)) - 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, __frame, NULL); - if (signr > 0) - return handle_signal(signr, &info, &ka, oldset); + if (signr > 0) { + if (handle_signal(signr, &info, &ka, oldset) == 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); + } + + return; + } no_signal: /* Did we come from a system call? */ if (__frame->syscallno >= 0) { /* Restart the system call - no handlers present */ - if (__frame->gr8 == -ERESTARTNOHAND || - __frame->gr8 == -ERESTARTSYS || - __frame->gr8 == -ERESTARTNOINTR) { + switch (__frame->gr8) { + case -ERESTARTNOHAND: + case -ERESTARTSYS: + case -ERESTARTNOINTR: __frame->gr8 = __frame->orig_gr8; __frame->pc -= 4; - } + break; - if (__frame->gr8 == -ERESTART_RESTARTBLOCK){ + case -ERESTART_RESTARTBLOCK: __frame->gr8 = __NR_restart_syscall; __frame->pc -= 4; + break; } } - 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); + } } /* end do_signal() */ /*****************************************************************************/ /* * notification of userspace execution resumption - * - triggered by current->work.notify_resume + * - triggered by the TIF_WORK_MASK flags */ asmlinkage void do_notify_resume(__u32 thread_info_flags) { @@ -594,7 +562,7 @@ asmlinkage void do_notify_resume(__u32 thread_info_flags) clear_thread_flag(TIF_SINGLESTEP); /* deal with pending signal delivery */ - if (thread_info_flags & _TIF_SIGPENDING) - do_signal(NULL); + if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) + do_signal(); } /* end do_notify_resume() */ |