|
@@ -1,8 +1,7 @@
|
|
|
/*
|
|
|
* arch/s390/kernel/signal.c
|
|
|
*
|
|
|
- * S390 version
|
|
|
- * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
|
|
|
+ * Copyright (C) IBM Corp. 1999,2006
|
|
|
* Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
|
|
|
*
|
|
|
* Based on Intel version
|
|
@@ -51,60 +50,24 @@ typedef struct
|
|
|
struct ucontext uc;
|
|
|
} rt_sigframe;
|
|
|
|
|
|
-int do_signal(struct pt_regs *regs, sigset_t *oldset);
|
|
|
-
|
|
|
/*
|
|
|
* Atomically swap in the new signal mask, and wait for a signal.
|
|
|
*/
|
|
|
asmlinkage int
|
|
|
-sys_sigsuspend(struct pt_regs * regs, int history0, int history1,
|
|
|
- old_sigset_t mask)
|
|
|
+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);
|
|
|
- regs->gprs[2] = -EINTR;
|
|
|
-
|
|
|
- while (1) {
|
|
|
- set_current_state(TASK_INTERRUPTIBLE);
|
|
|
- schedule();
|
|
|
- if (do_signal(regs, &saveset))
|
|
|
- return -EINTR;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-asmlinkage long
|
|
|
-sys_rt_sigsuspend(struct pt_regs *regs, 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;
|
|
|
+ current->state = TASK_INTERRUPTIBLE;
|
|
|
+ schedule();
|
|
|
+ set_thread_flag(TIF_RESTORE_SIGMASK);
|
|
|
|
|
|
- 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->gprs[2] = -EINTR;
|
|
|
-
|
|
|
- while (1) {
|
|
|
- set_current_state(TASK_INTERRUPTIBLE);
|
|
|
- schedule();
|
|
|
- if (do_signal(regs, &saveset))
|
|
|
- return -EINTR;
|
|
|
- }
|
|
|
+ return -ERESTARTNOHAND;
|
|
|
}
|
|
|
|
|
|
asmlinkage long
|
|
@@ -306,8 +269,8 @@ static inline int map_signal(int sig)
|
|
|
return sig;
|
|
|
}
|
|
|
|
|
|
-static void setup_frame(int sig, struct k_sigaction *ka,
|
|
|
- sigset_t *set, struct pt_regs * regs)
|
|
|
+static int setup_frame(int sig, struct k_sigaction *ka,
|
|
|
+ sigset_t *set, struct pt_regs * regs)
|
|
|
{
|
|
|
sigframe __user *frame;
|
|
|
|
|
@@ -355,13 +318,14 @@ static void setup_frame(int sig, struct k_sigaction *ka,
|
|
|
/* Place signal number on stack to allow backtrace from handler. */
|
|
|
if (__put_user(regs->gprs[2], (int __user *) &frame->signo))
|
|
|
goto give_sigsegv;
|
|
|
- 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)
|
|
|
{
|
|
|
int err = 0;
|
|
@@ -409,32 +373,39 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
|
|
regs->gprs[2] = map_signal(sig);
|
|
|
regs->gprs[3] = (unsigned long) &frame->info;
|
|
|
regs->gprs[4] = (unsigned long) &frame->uc;
|
|
|
- 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;
|
|
|
+
|
|
|
/* 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 (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);
|
|
|
+ }
|
|
|
|
|
|
- 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;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -446,12 +417,13 @@ 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.
|
|
|
*/
|
|
|
-int do_signal(struct pt_regs *regs, sigset_t *oldset)
|
|
|
+void do_signal(struct pt_regs *regs)
|
|
|
{
|
|
|
unsigned long retval = 0, continue_addr = 0, restart_addr = 0;
|
|
|
siginfo_t info;
|
|
|
int signr;
|
|
|
struct k_sigaction ka;
|
|
|
+ sigset_t *oldset;
|
|
|
|
|
|
/*
|
|
|
* We want the common case to go fast, which
|
|
@@ -460,9 +432,11 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset)
|
|
|
* if so.
|
|
|
*/
|
|
|
if (!user_mode(regs))
|
|
|
- return 1;
|
|
|
+ return;
|
|
|
|
|
|
- if (!oldset)
|
|
|
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
|
|
|
+ oldset = ¤t->saved_sigmask;
|
|
|
+ else
|
|
|
oldset = ¤t->blocked;
|
|
|
|
|
|
/* Are we from a system call? */
|
|
@@ -473,12 +447,14 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset)
|
|
|
|
|
|
/* Prepare for system call restart. We do this here so that a
|
|
|
debugger will see the already changed PSW. */
|
|
|
- if (retval == -ERESTARTNOHAND ||
|
|
|
- retval == -ERESTARTSYS ||
|
|
|
- retval == -ERESTARTNOINTR) {
|
|
|
+ switch (retval) {
|
|
|
+ case -ERESTARTNOHAND:
|
|
|
+ case -ERESTARTSYS:
|
|
|
+ case -ERESTARTNOINTR:
|
|
|
regs->gprs[2] = regs->orig_gpr2;
|
|
|
regs->psw.addr = restart_addr;
|
|
|
- } else if (retval == -ERESTART_RESTARTBLOCK) {
|
|
|
+ break;
|
|
|
+ case -ERESTART_RESTARTBLOCK:
|
|
|
regs->gprs[2] = -EINTR;
|
|
|
}
|
|
|
}
|
|
@@ -503,17 +479,38 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset)
|
|
|
/* Whee! Actually deliver the signal. */
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
if (test_thread_flag(TIF_31BIT)) {
|
|
|
- extern void handle_signal32(unsigned long sig,
|
|
|
- struct k_sigaction *ka,
|
|
|
- siginfo_t *info,
|
|
|
- sigset_t *oldset,
|
|
|
- struct pt_regs *regs);
|
|
|
- handle_signal32(signr, &ka, &info, oldset, regs);
|
|
|
- return 1;
|
|
|
+ extern int handle_signal32(unsigned long sig,
|
|
|
+ struct k_sigaction *ka,
|
|
|
+ siginfo_t *info,
|
|
|
+ sigset_t *oldset,
|
|
|
+ struct pt_regs *regs);
|
|
|
+ if (handle_signal32(
|
|
|
+ signr, &ka, &info, oldset, regs) == 0) {
|
|
|
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
|
|
|
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
|
|
|
+ }
|
|
|
+ return;
|
|
|
}
|
|
|
#endif
|
|
|
- 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);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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);
|
|
|
}
|
|
|
|
|
|
/* Restart a different system call. */
|
|
@@ -522,5 +519,4 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset)
|
|
|
regs->gprs[2] = __NR_restart_syscall;
|
|
|
set_thread_flag(TIF_RESTART_SVC);
|
|
|
}
|
|
|
- return 0;
|
|
|
}
|