Commit 36984265 authored by Mikael Pettersson's avatar Mikael Pettersson Committed by Russell King

ARM: 5677/1: ARM support for TIF_RESTORE_SIGMASK/pselect6/ppoll/epoll_pwait

This patch adds support for TIF_RESTORE_SIGMASK to ARM's
signal handling, which allows to hook up the pselect6, ppoll,
and epoll_pwait syscalls on ARM.

Tested here with eabi userspace and a test program with a
deliberate race between a child's exit and the parent's
sigprocmask/select sequence. Using sys_pselect6() instead
of sigprocmask/select reliably prevents the race.

The other arch's support for TIF_RESTORE_SIGMASK has evolved
over time:

In 2.6.16:
- add TIF_RESTORE_SIGMASK which parallels TIF_SIGPENDING
- test both when checking for pending signal [changed later]
- reimplement sys_sigsuspend() to use current->saved_sigmask,
  TIF_RESTORE_SIGMASK [changed later], and -ERESTARTNOHAND;
  ditto for sys_rt_sigsuspend(), but drop private code and
  use common code via __ARCH_WANT_SYS_RT_SIGSUSPEND;
- there are now no "extra" calls to do_signal() so its oldset
  parameter is always &current->blocked so need not be passed,
  also its return value is changed to void
- change handle_signal() to return 0/-errno
- change do_signal() to honor TIF_RESTORE_SIGMASK:
  + get oldset from current->saved_sigmask if TIF_RESTORE_SIGMASK
    is set
  + if handle_signal() was successful then clear TIF_RESTORE_SIGMASK
  + if no signal was delivered and TIF_RESTORE_SIGMASK is set then
    clear it and restore the sigmask
- hook up sys_pselect6() and sys_ppoll()

In 2.6.19:
- hook up sys_epoll_pwait()

In 2.6.26:
- allow archs to override how TIF_RESTORE_SIGMASK is implemented;
  default set_restore_sigmask() sets both TIF_RESTORE_SIGMASK and
  TIF_SIGPENDING; archs need now just test TIF_SIGPENDING again
  when checking for pending signal work; some archs now implement
  TIF_RESTORE_SIGMASK as a secondary/non-atomic thread flag bit
- call set_restore_sigmask() in sys_sigsuspend() instead of setting
  TIF_RESTORE_SIGMASK

In 2.6.29-rc:
- kill sys_pselect7() which no arch wanted

So for 2.6.31-rc6/ARM this patch does the following:
- Add TIF_RESTORE_SIGMASK. Use the generic set_restore_sigmask()
  which sets both TIF_SIGPENDING and TIF_RESTORE_SIGMASK, so
  TIF_RESTORE_SIGMASK need not claim one of the scarce low thread
  flags, and existing TIF_SIGPENDING and _TIF_WORK_MASK tests need
  not be extended for TIF_RESTORE_SIGMASK.
- sys_sigsuspend() is reimplemented to use current->saved_sigmask
  and set_restore_sigmask(), making it identical to most other archs
- The private code for sys_rt_sigsuspend() is removed, instead
  generic code supplies it via __ARCH_WANT_SYS_RT_SIGSUSPEND.
- sys_sigsuspend() and sys_rt_sigsuspend() no longer need a pt_regs
  parameter, so their assembly code wrappers are removed.
- handle_signal() is changed to return 0 on success or -errno.
- The oldset parameter to do_signal() is now redundant and removed,
  and the return value is now also redundant and changed to void.
- do_signal() is changed to honor TIF_RESTORE_SIGMASK:
  + get oldset from current->saved_sigmask if TIF_RESTORE_SIGMASK
    is set
  + if handle_signal() was successful then clear TIF_RESTORE_SIGMASK
  + if no signal was delivered and TIF_RESTORE_SIGMASK is set then
    clear it and restore the sigmask
- Hook up sys_pselect6, sys_ppoll, and sys_epoll_pwait.
Signed-off-by: default avatarMikael Pettersson <mikpe@it.uu.se>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 4bf1fa5a
...@@ -140,6 +140,7 @@ extern void vfp_sync_state(struct thread_info *thread); ...@@ -140,6 +140,7 @@ extern void vfp_sync_state(struct thread_info *thread);
#define TIF_USING_IWMMXT 17 #define TIF_USING_IWMMXT 17
#define TIF_MEMDIE 18 #define TIF_MEMDIE 18
#define TIF_FREEZE 19 #define TIF_FREEZE 19
#define TIF_RESTORE_SIGMASK 20
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
...@@ -147,6 +148,7 @@ extern void vfp_sync_state(struct thread_info *thread); ...@@ -147,6 +148,7 @@ extern void vfp_sync_state(struct thread_info *thread);
#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG)
#define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT) #define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT)
#define _TIF_FREEZE (1 << TIF_FREEZE) #define _TIF_FREEZE (1 << TIF_FREEZE)
#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK)
/* /*
* Change these and you break ASM code in entry-common.S * Change these and you break ASM code in entry-common.S
......
...@@ -360,8 +360,8 @@ ...@@ -360,8 +360,8 @@
#define __NR_readlinkat (__NR_SYSCALL_BASE+332) #define __NR_readlinkat (__NR_SYSCALL_BASE+332)
#define __NR_fchmodat (__NR_SYSCALL_BASE+333) #define __NR_fchmodat (__NR_SYSCALL_BASE+333)
#define __NR_faccessat (__NR_SYSCALL_BASE+334) #define __NR_faccessat (__NR_SYSCALL_BASE+334)
/* 335 for pselect6 */ #define __NR_pselect6 (__NR_SYSCALL_BASE+335)
/* 336 for ppoll */ #define __NR_ppoll (__NR_SYSCALL_BASE+336)
#define __NR_unshare (__NR_SYSCALL_BASE+337) #define __NR_unshare (__NR_SYSCALL_BASE+337)
#define __NR_set_robust_list (__NR_SYSCALL_BASE+338) #define __NR_set_robust_list (__NR_SYSCALL_BASE+338)
#define __NR_get_robust_list (__NR_SYSCALL_BASE+339) #define __NR_get_robust_list (__NR_SYSCALL_BASE+339)
...@@ -372,7 +372,7 @@ ...@@ -372,7 +372,7 @@
#define __NR_vmsplice (__NR_SYSCALL_BASE+343) #define __NR_vmsplice (__NR_SYSCALL_BASE+343)
#define __NR_move_pages (__NR_SYSCALL_BASE+344) #define __NR_move_pages (__NR_SYSCALL_BASE+344)
#define __NR_getcpu (__NR_SYSCALL_BASE+345) #define __NR_getcpu (__NR_SYSCALL_BASE+345)
/* 346 for epoll_pwait */ #define __NR_epoll_pwait (__NR_SYSCALL_BASE+346)
#define __NR_kexec_load (__NR_SYSCALL_BASE+347) #define __NR_kexec_load (__NR_SYSCALL_BASE+347)
#define __NR_utimensat (__NR_SYSCALL_BASE+348) #define __NR_utimensat (__NR_SYSCALL_BASE+348)
#define __NR_signalfd (__NR_SYSCALL_BASE+349) #define __NR_signalfd (__NR_SYSCALL_BASE+349)
...@@ -432,6 +432,7 @@ ...@@ -432,6 +432,7 @@
#define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPENDING
#define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_SIGPROCMASK
#define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGACTION
#define __ARCH_WANT_SYS_RT_SIGSUSPEND
#if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT) #if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT)
#define __ARCH_WANT_SYS_TIME #define __ARCH_WANT_SYS_TIME
......
...@@ -81,7 +81,7 @@ ...@@ -81,7 +81,7 @@
CALL(sys_ni_syscall) /* was sys_ssetmask */ CALL(sys_ni_syscall) /* was sys_ssetmask */
/* 70 */ CALL(sys_setreuid16) /* 70 */ CALL(sys_setreuid16)
CALL(sys_setregid16) CALL(sys_setregid16)
CALL(sys_sigsuspend_wrapper) CALL(sys_sigsuspend)
CALL(sys_sigpending) CALL(sys_sigpending)
CALL(sys_sethostname) CALL(sys_sethostname)
/* 75 */ CALL(sys_setrlimit) /* 75 */ CALL(sys_setrlimit)
...@@ -188,7 +188,7 @@ ...@@ -188,7 +188,7 @@
CALL(sys_rt_sigpending) CALL(sys_rt_sigpending)
CALL(sys_rt_sigtimedwait) CALL(sys_rt_sigtimedwait)
CALL(sys_rt_sigqueueinfo) CALL(sys_rt_sigqueueinfo)
CALL(sys_rt_sigsuspend_wrapper) CALL(sys_rt_sigsuspend)
/* 180 */ CALL(ABI(sys_pread64, sys_oabi_pread64)) /* 180 */ CALL(ABI(sys_pread64, sys_oabi_pread64))
CALL(ABI(sys_pwrite64, sys_oabi_pwrite64)) CALL(ABI(sys_pwrite64, sys_oabi_pwrite64))
CALL(sys_chown16) CALL(sys_chown16)
...@@ -344,8 +344,8 @@ ...@@ -344,8 +344,8 @@
CALL(sys_readlinkat) CALL(sys_readlinkat)
CALL(sys_fchmodat) CALL(sys_fchmodat)
CALL(sys_faccessat) CALL(sys_faccessat)
/* 335 */ CALL(sys_ni_syscall) /* eventually pselect6 */ /* 335 */ CALL(sys_pselect6)
CALL(sys_ni_syscall) /* eventually ppoll */ CALL(sys_ppoll)
CALL(sys_unshare) CALL(sys_unshare)
CALL(sys_set_robust_list) CALL(sys_set_robust_list)
CALL(sys_get_robust_list) CALL(sys_get_robust_list)
...@@ -355,7 +355,7 @@ ...@@ -355,7 +355,7 @@
CALL(sys_vmsplice) CALL(sys_vmsplice)
CALL(sys_move_pages) CALL(sys_move_pages)
/* 345 */ CALL(sys_getcpu) /* 345 */ CALL(sys_getcpu)
CALL(sys_ni_syscall) /* eventually epoll_pwait */ CALL(sys_epoll_pwait)
CALL(sys_kexec_load) CALL(sys_kexec_load)
CALL(sys_utimensat) CALL(sys_utimensat)
CALL(sys_signalfd) CALL(sys_signalfd)
......
...@@ -373,16 +373,6 @@ sys_clone_wrapper: ...@@ -373,16 +373,6 @@ sys_clone_wrapper:
b sys_clone b sys_clone
ENDPROC(sys_clone_wrapper) ENDPROC(sys_clone_wrapper)
sys_sigsuspend_wrapper:
add r3, sp, #S_OFF
b sys_sigsuspend
ENDPROC(sys_sigsuspend_wrapper)
sys_rt_sigsuspend_wrapper:
add r2, sp, #S_OFF
b sys_rt_sigsuspend
ENDPROC(sys_rt_sigsuspend_wrapper)
sys_sigreturn_wrapper: sys_sigreturn_wrapper:
add r0, sp, #S_OFF add r0, sp, #S_OFF
b sys_sigreturn b sys_sigreturn
......
...@@ -47,57 +47,22 @@ const unsigned long sigreturn_codes[7] = { ...@@ -47,57 +47,22 @@ const unsigned long sigreturn_codes[7] = {
MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, 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. * 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; mask &= _BLOCKABLE;
spin_lock_irq(&current->sighand->siglock); spin_lock_irq(&current->sighand->siglock);
saveset = current->blocked; current->saved_sigmask = current->blocked;
siginitset(&current->blocked, mask); siginitset(&current->blocked, mask);
recalc_sigpending(); recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock); spin_unlock_irq(&current->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(&current->sighand->siglock);
saveset = current->blocked;
current->blocked = newset;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
regs->ARM_r0 = -EINTR;
while (1) { current->state = TASK_INTERRUPTIBLE;
current->state = TASK_INTERRUPTIBLE; schedule();
schedule(); set_restore_sigmask();
if (do_signal(&saveset, regs, 0)) return -ERESTARTNOHAND;
return regs->ARM_r0;
}
} }
asmlinkage int asmlinkage int
...@@ -545,7 +510,7 @@ static inline void setup_syscall_restart(struct pt_regs *regs) ...@@ -545,7 +510,7 @@ static inline void setup_syscall_restart(struct pt_regs *regs)
/* /*
* OK, we're invoking a handler * OK, we're invoking a handler
*/ */
static void static int
handle_signal(unsigned long sig, struct k_sigaction *ka, handle_signal(unsigned long sig, struct k_sigaction *ka,
siginfo_t *info, sigset_t *oldset, siginfo_t *info, sigset_t *oldset,
struct pt_regs * regs, int syscall) struct pt_regs * regs, int syscall)
...@@ -596,7 +561,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, ...@@ -596,7 +561,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
if (ret != 0) { if (ret != 0) {
force_sigsegv(sig, tsk); force_sigsegv(sig, tsk);
return; return ret;
} }
/* /*
...@@ -610,6 +575,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, ...@@ -610,6 +575,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
recalc_sigpending(); recalc_sigpending();
spin_unlock_irq(&tsk->sighand->siglock); spin_unlock_irq(&tsk->sighand->siglock);
return 0;
} }
/* /*
...@@ -621,7 +587,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, ...@@ -621,7 +587,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
* the kernel can handle, and then we build all the user-level signal handling * the kernel can handle, and then we build all the user-level signal handling
* stack-frames in one go after that. * 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; struct k_sigaction ka;
siginfo_t info; siginfo_t info;
...@@ -634,7 +600,7 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) ...@@ -634,7 +600,7 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
* if so. * if so.
*/ */
if (!user_mode(regs)) if (!user_mode(regs))
return 0; return;
if (try_to_freeze()) if (try_to_freeze())
goto no_signal; goto no_signal;
...@@ -643,9 +609,24 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) ...@@ -643,9 +609,24 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
signr = get_signal_to_deliver(&info, &ka, regs, NULL); signr = get_signal_to_deliver(&info, &ka, regs, NULL);
if (signr > 0) { if (signr > 0) {
handle_signal(signr, &ka, &info, oldset, regs, syscall); sigset_t *oldset;
if (test_thread_flag(TIF_RESTORE_SIGMASK))
oldset = &current->saved_sigmask;
else
oldset = &current->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); single_step_set(current);
return 1; return;
} }
no_signal: no_signal:
...@@ -697,14 +678,21 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) ...@@ -697,14 +678,21 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
regs->ARM_r0 == -ERESTARTNOINTR) { regs->ARM_r0 == -ERESTARTNOINTR) {
setup_syscall_restart(regs); 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, &current->saved_sigmask, NULL);
}
} }
single_step_set(current); single_step_set(current);
return 0;
} }
asmlinkage void asmlinkage void
do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall)
{ {
if (thread_flags & _TIF_SIGPENDING) if (thread_flags & _TIF_SIGPENDING)
do_signal(&current->blocked, regs, syscall); do_signal(regs, syscall);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment