[PATCH] ARM: 5677/1: ARM support for TIF_RESTORE_SIGMASK/pselect6/ppoll/epoll_pwait
Amit Kucheria
amit.kucheria at canonical.com
Wed Feb 24 13:29:34 UTC 2010
On 10 Feb 24, Eric Miao wrote:
> Bryan, could you please check if this is applicable to dove as well, or its
> just backport for imx51?
>
Dove is already 2.6.32-based. The patch was integrated in 2.6.32.
/Amit
>
> On Feb 24, 2010 6:05 PM, "Bryan Wu" <bryan.wu at canonical.com> wrote:
>
> From: Mikael Pettersson <mikpe at it.uu.se>
>
> 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 ¤t->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.
>
> BugLink: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/319729
> (cherry-picked from commit 369842658a36bcea28ecb643ba4bdb53919330dd)
>
> Signed-off-by: Mikael Pettersson <mikpe at it.uu.se>
> Signed-off-by: Russell King
> <rmk+kernel at arm.linux.org.uk<rmk%2Bkernel at arm.linux.org.uk>
> >
> Signed-off-by: Bryan Wu <bryan.wu at canonical.com>
> ---
> arch/arm/include/asm/thread_info.h | 2 +
> arch/arm/include/asm/unistd.h | 7 ++-
> arch/arm/kernel/calls.S | 10 ++--
> arch/arm/kernel/entry-common.S | 10 ----
> arch/arm/kernel/signal.c | 86
> +++++++++++++++--------------------
> 5 files changed, 48 insertions(+), 67 deletions(-)
>
> diff --git a/arch/arm/include/asm/thread_info.h
> b/arch/arm/include/asm/thread_info.h
> index 73394e5..e20d805 100644
> --- a/arch/arm/include/asm/thread_info.h
> +++ b/arch/arm/include/asm/thread_info.h
> @@ -140,6 +140,7 @@ extern void vfp_sync_state(struct thread_info *thread);
> #define TIF_USING_IWMMXT 17
> #define TIF_MEMDIE 18
> #define TIF_FREEZE 19
> +#define TIF_RESTORE_SIGMASK 20
>
> #define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
> #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
> @@ -147,6 +148,7 @@ extern void vfp_sync_state(struct thread_info *thread);
> #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG)
> #define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT)
> #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
> diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h
> index 0e97b8c..9122c9e 100644
> --- a/arch/arm/include/asm/unistd.h
> +++ b/arch/arm/include/asm/unistd.h
> @@ -360,8 +360,8 @@
> #define __NR_readlinkat (__NR_SYSCALL_BASE+332)
> #define __NR_fchmodat (__NR_SYSCALL_BASE+333)
> #define __NR_faccessat (__NR_SYSCALL_BASE+334)
> - /* 335 for pselect6 */
> - /* 336 for ppoll */
> +#define __NR_pselect6 (__NR_SYSCALL_BASE+335)
> +#define __NR_ppoll (__NR_SYSCALL_BASE+336)
> #define __NR_unshare (__NR_SYSCALL_BASE+337)
> #define __NR_set_robust_list (__NR_SYSCALL_BASE+338)
> #define __NR_get_robust_list (__NR_SYSCALL_BASE+339)
> @@ -372,7 +372,7 @@
> #define __NR_vmsplice (__NR_SYSCALL_BASE+343)
> #define __NR_move_pages (__NR_SYSCALL_BASE+344)
> #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_utimensat (__NR_SYSCALL_BASE+348)
> #define __NR_signalfd (__NR_SYSCALL_BASE+349)
> @@ -432,6 +432,7 @@
> #define __ARCH_WANT_SYS_SIGPENDING
> #define __ARCH_WANT_SYS_SIGPROCMASK
> #define __ARCH_WANT_SYS_RT_SIGACTION
> +#define __ARCH_WANT_SYS_RT_SIGSUSPEND
>
> #if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT)
> #define __ARCH_WANT_SYS_TIME
> diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
> index 6e1b8bf..9296c59 100644
> --- a/arch/arm/kernel/calls.S
> +++ b/arch/arm/kernel/calls.S
> @@ -81,7 +81,7 @@
> CALL(sys_ni_syscall) /* was sys_ssetmask */
> /* 70 */ CALL(sys_setreuid16)
> CALL(sys_setregid16)
> - CALL(sys_sigsuspend_wrapper)
> + CALL(sys_sigsuspend)
> CALL(sys_sigpending)
> CALL(sys_sethostname)
> /* 75 */ CALL(sys_setrlimit)
> @@ -188,7 +188,7 @@
> CALL(sys_rt_sigpending)
> CALL(sys_rt_sigtimedwait)
> CALL(sys_rt_sigqueueinfo)
> - CALL(sys_rt_sigsuspend_wrapper)
> + CALL(sys_rt_sigsuspend)
> /* 180 */ CALL(ABI(sys_pread64, sys_oabi_pread64))
> CALL(ABI(sys_pwrite64, sys_oabi_pwrite64))
> CALL(sys_chown16)
> @@ -344,8 +344,8 @@
> CALL(sys_readlinkat)
> CALL(sys_fchmodat)
> CALL(sys_faccessat)
> -/* 335 */ CALL(sys_ni_syscall) /* eventually pselect6 */
> - CALL(sys_ni_syscall) /* eventually ppoll */
> +/* 335 */ CALL(sys_pselect6)
> + CALL(sys_ppoll)
> CALL(sys_unshare)
> CALL(sys_set_robust_list)
> CALL(sys_get_robust_list)
> @@ -355,7 +355,7 @@
> CALL(sys_vmsplice)
> CALL(sys_move_pages)
> /* 345 */ CALL(sys_getcpu)
> - CALL(sys_ni_syscall) /* eventually epoll_pwait */
> + CALL(sys_epoll_pwait)
> CALL(sys_kexec_load)
> CALL(sys_utimensat)
> CALL(sys_signalfd)
> diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
> index a762152..9565752 100644
> --- a/arch/arm/kernel/entry-common.S
> +++ b/arch/arm/kernel/entry-common.S
> @@ -373,16 +373,6 @@ sys_clone_wrapper:
> b sys_clone
> 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:
> add r0, sp, #S_OFF
> b sys_sigreturn
> diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
> index 6dd5008..7dfd3b8 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
> @@ -628,7 +593,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)
> @@ -679,7 +644,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
>
> if (ret != 0) {
> force_sigsegv(sig, tsk);
> - return;
> + return ret;
> }
>
> /*
> @@ -693,6 +658,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
> recalc_sigpending();
> spin_unlock_irq(&tsk->sighand->siglock);
>
> + return 0;
> }
>
> /*
> @@ -704,7 +670,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;
> @@ -717,7 +683,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;
> @@ -726,9 +692,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:
> @@ -780,14 +761,21 @@ 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);
> }
> --
> 1.6.3.3
>
>
> --
> kernel-team mailing list
> kernel-team at lists.ubuntu.com
> https://lists.ubuntu.com/mailman/listinfo/kernel-team
> --
> kernel-team mailing list
> kernel-team at lists.ubuntu.com
> https://lists.ubuntu.com/mailman/listinfo/kernel-team
--
----------------------------------------------------------------------
Amit Kucheria, Kernel Engineer || amit.kucheria at canonical.com
----------------------------------------------------------------------
More information about the kernel-team
mailing list