diff options
author | Mikael Pettersson <mikpe@it.uu.se> | 2009-08-15 07:58:11 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-08-15 10:10:31 -0400 |
commit | 369842658a36bcea28ecb643ba4bdb53919330dd (patch) | |
tree | 1a590eabceea85b974360eca2cfba54d86ae4933 | |
parent | 4bf1fa5a34aa2dd0d2cc58f0fc213a2e22d007a4 (diff) |
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 ¤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.
Signed-off-by: Mikael Pettersson <mikpe@it.uu.se>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | arch/arm/include/asm/thread_info.h | 2 | ||||
-rw-r--r-- | arch/arm/include/asm/unistd.h | 7 | ||||
-rw-r--r-- | arch/arm/kernel/calls.S | 10 | ||||
-rw-r--r-- | arch/arm/kernel/entry-common.S | 10 | ||||
-rw-r--r-- | 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 73394e50cbca..e20d80539b42 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); | |||
140 | #define TIF_USING_IWMMXT 17 | 140 | #define TIF_USING_IWMMXT 17 |
141 | #define TIF_MEMDIE 18 | 141 | #define TIF_MEMDIE 18 |
142 | #define TIF_FREEZE 19 | 142 | #define TIF_FREEZE 19 |
143 | #define TIF_RESTORE_SIGMASK 20 | ||
143 | 144 | ||
144 | #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) | 145 | #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) |
145 | #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) | 146 | #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) |
@@ -147,6 +148,7 @@ extern void vfp_sync_state(struct thread_info *thread); | |||
147 | #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) | 148 | #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) |
148 | #define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT) | 149 | #define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT) |
149 | #define _TIF_FREEZE (1 << TIF_FREEZE) | 150 | #define _TIF_FREEZE (1 << TIF_FREEZE) |
151 | #define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK) | ||
150 | 152 | ||
151 | /* | 153 | /* |
152 | * Change these and you break ASM code in entry-common.S | 154 | * 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 0e97b8cb77d5..9122c9ee18fb 100644 --- a/arch/arm/include/asm/unistd.h +++ b/arch/arm/include/asm/unistd.h | |||
@@ -360,8 +360,8 @@ | |||
360 | #define __NR_readlinkat (__NR_SYSCALL_BASE+332) | 360 | #define __NR_readlinkat (__NR_SYSCALL_BASE+332) |
361 | #define __NR_fchmodat (__NR_SYSCALL_BASE+333) | 361 | #define __NR_fchmodat (__NR_SYSCALL_BASE+333) |
362 | #define __NR_faccessat (__NR_SYSCALL_BASE+334) | 362 | #define __NR_faccessat (__NR_SYSCALL_BASE+334) |
363 | /* 335 for pselect6 */ | 363 | #define __NR_pselect6 (__NR_SYSCALL_BASE+335) |
364 | /* 336 for ppoll */ | 364 | #define __NR_ppoll (__NR_SYSCALL_BASE+336) |
365 | #define __NR_unshare (__NR_SYSCALL_BASE+337) | 365 | #define __NR_unshare (__NR_SYSCALL_BASE+337) |
366 | #define __NR_set_robust_list (__NR_SYSCALL_BASE+338) | 366 | #define __NR_set_robust_list (__NR_SYSCALL_BASE+338) |
367 | #define __NR_get_robust_list (__NR_SYSCALL_BASE+339) | 367 | #define __NR_get_robust_list (__NR_SYSCALL_BASE+339) |
@@ -372,7 +372,7 @@ | |||
372 | #define __NR_vmsplice (__NR_SYSCALL_BASE+343) | 372 | #define __NR_vmsplice (__NR_SYSCALL_BASE+343) |
373 | #define __NR_move_pages (__NR_SYSCALL_BASE+344) | 373 | #define __NR_move_pages (__NR_SYSCALL_BASE+344) |
374 | #define __NR_getcpu (__NR_SYSCALL_BASE+345) | 374 | #define __NR_getcpu (__NR_SYSCALL_BASE+345) |
375 | /* 346 for epoll_pwait */ | 375 | #define __NR_epoll_pwait (__NR_SYSCALL_BASE+346) |
376 | #define __NR_kexec_load (__NR_SYSCALL_BASE+347) | 376 | #define __NR_kexec_load (__NR_SYSCALL_BASE+347) |
377 | #define __NR_utimensat (__NR_SYSCALL_BASE+348) | 377 | #define __NR_utimensat (__NR_SYSCALL_BASE+348) |
378 | #define __NR_signalfd (__NR_SYSCALL_BASE+349) | 378 | #define __NR_signalfd (__NR_SYSCALL_BASE+349) |
@@ -432,6 +432,7 @@ | |||
432 | #define __ARCH_WANT_SYS_SIGPENDING | 432 | #define __ARCH_WANT_SYS_SIGPENDING |
433 | #define __ARCH_WANT_SYS_SIGPROCMASK | 433 | #define __ARCH_WANT_SYS_SIGPROCMASK |
434 | #define __ARCH_WANT_SYS_RT_SIGACTION | 434 | #define __ARCH_WANT_SYS_RT_SIGACTION |
435 | #define __ARCH_WANT_SYS_RT_SIGSUSPEND | ||
435 | 436 | ||
436 | #if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT) | 437 | #if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT) |
437 | #define __ARCH_WANT_SYS_TIME | 438 | #define __ARCH_WANT_SYS_TIME |
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S index f776e72a4cb8..ecfa98954d1d 100644 --- a/arch/arm/kernel/calls.S +++ b/arch/arm/kernel/calls.S | |||
@@ -81,7 +81,7 @@ | |||
81 | CALL(sys_ni_syscall) /* was sys_ssetmask */ | 81 | CALL(sys_ni_syscall) /* was sys_ssetmask */ |
82 | /* 70 */ CALL(sys_setreuid16) | 82 | /* 70 */ CALL(sys_setreuid16) |
83 | CALL(sys_setregid16) | 83 | CALL(sys_setregid16) |
84 | CALL(sys_sigsuspend_wrapper) | 84 | CALL(sys_sigsuspend) |
85 | CALL(sys_sigpending) | 85 | CALL(sys_sigpending) |
86 | CALL(sys_sethostname) | 86 | CALL(sys_sethostname) |
87 | /* 75 */ CALL(sys_setrlimit) | 87 | /* 75 */ CALL(sys_setrlimit) |
@@ -188,7 +188,7 @@ | |||
188 | CALL(sys_rt_sigpending) | 188 | CALL(sys_rt_sigpending) |
189 | CALL(sys_rt_sigtimedwait) | 189 | CALL(sys_rt_sigtimedwait) |
190 | CALL(sys_rt_sigqueueinfo) | 190 | CALL(sys_rt_sigqueueinfo) |
191 | CALL(sys_rt_sigsuspend_wrapper) | 191 | CALL(sys_rt_sigsuspend) |
192 | /* 180 */ CALL(ABI(sys_pread64, sys_oabi_pread64)) | 192 | /* 180 */ CALL(ABI(sys_pread64, sys_oabi_pread64)) |
193 | CALL(ABI(sys_pwrite64, sys_oabi_pwrite64)) | 193 | CALL(ABI(sys_pwrite64, sys_oabi_pwrite64)) |
194 | CALL(sys_chown16) | 194 | CALL(sys_chown16) |
@@ -344,8 +344,8 @@ | |||
344 | CALL(sys_readlinkat) | 344 | CALL(sys_readlinkat) |
345 | CALL(sys_fchmodat) | 345 | CALL(sys_fchmodat) |
346 | CALL(sys_faccessat) | 346 | CALL(sys_faccessat) |
347 | /* 335 */ CALL(sys_ni_syscall) /* eventually pselect6 */ | 347 | /* 335 */ CALL(sys_pselect6) |
348 | CALL(sys_ni_syscall) /* eventually ppoll */ | 348 | CALL(sys_ppoll) |
349 | CALL(sys_unshare) | 349 | CALL(sys_unshare) |
350 | CALL(sys_set_robust_list) | 350 | CALL(sys_set_robust_list) |
351 | CALL(sys_get_robust_list) | 351 | CALL(sys_get_robust_list) |
@@ -355,7 +355,7 @@ | |||
355 | CALL(sys_vmsplice) | 355 | CALL(sys_vmsplice) |
356 | CALL(sys_move_pages) | 356 | CALL(sys_move_pages) |
357 | /* 345 */ CALL(sys_getcpu) | 357 | /* 345 */ CALL(sys_getcpu) |
358 | CALL(sys_ni_syscall) /* eventually epoll_pwait */ | 358 | CALL(sys_epoll_pwait) |
359 | CALL(sys_kexec_load) | 359 | CALL(sys_kexec_load) |
360 | CALL(sys_utimensat) | 360 | CALL(sys_utimensat) |
361 | CALL(sys_signalfd) | 361 | CALL(sys_signalfd) |
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 366e5097a41a..bfa7f0af7ede 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S | |||
@@ -373,16 +373,6 @@ sys_clone_wrapper: | |||
373 | b sys_clone | 373 | b sys_clone |
374 | ENDPROC(sys_clone_wrapper) | 374 | ENDPROC(sys_clone_wrapper) |
375 | 375 | ||
376 | sys_sigsuspend_wrapper: | ||
377 | add r3, sp, #S_OFF | ||
378 | b sys_sigsuspend | ||
379 | ENDPROC(sys_sigsuspend_wrapper) | ||
380 | |||
381 | sys_rt_sigsuspend_wrapper: | ||
382 | add r2, sp, #S_OFF | ||
383 | b sys_rt_sigsuspend | ||
384 | ENDPROC(sys_rt_sigsuspend_wrapper) | ||
385 | |||
386 | sys_sigreturn_wrapper: | 376 | sys_sigreturn_wrapper: |
387 | add r0, sp, #S_OFF | 377 | add r0, sp, #S_OFF |
388 | b sys_sigreturn | 378 | b sys_sigreturn |
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 93bb4247b7ed..e27ee1f701d5 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c | |||
@@ -47,57 +47,22 @@ const unsigned long sigreturn_codes[7] = { | |||
47 | MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, | 47 | MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, |
48 | }; | 48 | }; |
49 | 49 | ||
50 | static int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall); | ||
51 | |||
52 | /* | 50 | /* |
53 | * atomically swap in the new signal mask, and wait for a signal. | 51 | * atomically swap in the new signal mask, and wait for a signal. |
54 | */ | 52 | */ |
55 | asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask, struct pt_regs *regs) | 53 | asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask) |
56 | { | 54 | { |
57 | sigset_t saveset; | ||
58 | |||
59 | mask &= _BLOCKABLE; | 55 | mask &= _BLOCKABLE; |
60 | spin_lock_irq(¤t->sighand->siglock); | 56 | spin_lock_irq(¤t->sighand->siglock); |
61 | saveset = current->blocked; | 57 | current->saved_sigmask = current->blocked; |
62 | siginitset(¤t->blocked, mask); | 58 | siginitset(¤t->blocked, mask); |
63 | recalc_sigpending(); | 59 | recalc_sigpending(); |
64 | spin_unlock_irq(¤t->sighand->siglock); | 60 | spin_unlock_irq(¤t->sighand->siglock); |
65 | regs->ARM_r0 = -EINTR; | ||
66 | |||
67 | while (1) { | ||
68 | current->state = TASK_INTERRUPTIBLE; | ||
69 | schedule(); | ||
70 | if (do_signal(&saveset, regs, 0)) | ||
71 | return regs->ARM_r0; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | asmlinkage int | ||
76 | sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, struct pt_regs *regs) | ||
77 | { | ||
78 | sigset_t saveset, newset; | ||
79 | |||
80 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
81 | if (sigsetsize != sizeof(sigset_t)) | ||
82 | return -EINVAL; | ||
83 | |||
84 | if (copy_from_user(&newset, unewset, sizeof(newset))) | ||
85 | return -EFAULT; | ||
86 | sigdelsetmask(&newset, ~_BLOCKABLE); | ||
87 | |||
88 | spin_lock_irq(¤t->sighand->siglock); | ||
89 | saveset = current->blocked; | ||
90 | current->blocked = newset; | ||
91 | recalc_sigpending(); | ||
92 | spin_unlock_irq(¤t->sighand->siglock); | ||
93 | regs->ARM_r0 = -EINTR; | ||
94 | 61 | ||
95 | while (1) { | 62 | current->state = TASK_INTERRUPTIBLE; |
96 | current->state = TASK_INTERRUPTIBLE; | 63 | schedule(); |
97 | schedule(); | 64 | set_restore_sigmask(); |
98 | if (do_signal(&saveset, regs, 0)) | 65 | return -ERESTARTNOHAND; |
99 | return regs->ARM_r0; | ||
100 | } | ||
101 | } | 66 | } |
102 | 67 | ||
103 | asmlinkage int | 68 | asmlinkage int |
@@ -545,7 +510,7 @@ static inline void setup_syscall_restart(struct pt_regs *regs) | |||
545 | /* | 510 | /* |
546 | * OK, we're invoking a handler | 511 | * OK, we're invoking a handler |
547 | */ | 512 | */ |
548 | static void | 513 | static int |
549 | handle_signal(unsigned long sig, struct k_sigaction *ka, | 514 | handle_signal(unsigned long sig, struct k_sigaction *ka, |
550 | siginfo_t *info, sigset_t *oldset, | 515 | siginfo_t *info, sigset_t *oldset, |
551 | struct pt_regs * regs, int syscall) | 516 | struct pt_regs * regs, int syscall) |
@@ -596,7 +561,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, | |||
596 | 561 | ||
597 | if (ret != 0) { | 562 | if (ret != 0) { |
598 | force_sigsegv(sig, tsk); | 563 | force_sigsegv(sig, tsk); |
599 | return; | 564 | return ret; |
600 | } | 565 | } |
601 | 566 | ||
602 | /* | 567 | /* |
@@ -610,6 +575,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, | |||
610 | recalc_sigpending(); | 575 | recalc_sigpending(); |
611 | spin_unlock_irq(&tsk->sighand->siglock); | 576 | spin_unlock_irq(&tsk->sighand->siglock); |
612 | 577 | ||
578 | return 0; | ||
613 | } | 579 | } |
614 | 580 | ||
615 | /* | 581 | /* |
@@ -621,7 +587,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, | |||
621 | * the kernel can handle, and then we build all the user-level signal handling | 587 | * the kernel can handle, and then we build all the user-level signal handling |
622 | * stack-frames in one go after that. | 588 | * stack-frames in one go after that. |
623 | */ | 589 | */ |
624 | static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) | 590 | static void do_signal(struct pt_regs *regs, int syscall) |
625 | { | 591 | { |
626 | struct k_sigaction ka; | 592 | struct k_sigaction ka; |
627 | siginfo_t info; | 593 | siginfo_t info; |
@@ -634,7 +600,7 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) | |||
634 | * if so. | 600 | * if so. |
635 | */ | 601 | */ |
636 | if (!user_mode(regs)) | 602 | if (!user_mode(regs)) |
637 | return 0; | 603 | return; |
638 | 604 | ||
639 | if (try_to_freeze()) | 605 | if (try_to_freeze()) |
640 | goto no_signal; | 606 | goto no_signal; |
@@ -643,9 +609,24 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) | |||
643 | 609 | ||
644 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | 610 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); |
645 | if (signr > 0) { | 611 | if (signr > 0) { |
646 | handle_signal(signr, &ka, &info, oldset, regs, syscall); | 612 | sigset_t *oldset; |
613 | |||
614 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
615 | oldset = ¤t->saved_sigmask; | ||
616 | else | ||
617 | oldset = ¤t->blocked; | ||
618 | if (handle_signal(signr, &ka, &info, oldset, regs, syscall) == 0) { | ||
619 | /* | ||
620 | * A signal was successfully delivered; the saved | ||
621 | * sigmask will have been stored in the signal frame, | ||
622 | * and will be restored by sigreturn, so we can simply | ||
623 | * clear the TIF_RESTORE_SIGMASK flag. | ||
624 | */ | ||
625 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
626 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
627 | } | ||
647 | single_step_set(current); | 628 | single_step_set(current); |
648 | return 1; | 629 | return; |
649 | } | 630 | } |
650 | 631 | ||
651 | no_signal: | 632 | no_signal: |
@@ -697,14 +678,21 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) | |||
697 | regs->ARM_r0 == -ERESTARTNOINTR) { | 678 | regs->ARM_r0 == -ERESTARTNOINTR) { |
698 | setup_syscall_restart(regs); | 679 | setup_syscall_restart(regs); |
699 | } | 680 | } |
681 | |||
682 | /* If there's no signal to deliver, we just put the saved sigmask | ||
683 | * back. | ||
684 | */ | ||
685 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) { | ||
686 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
687 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | ||
688 | } | ||
700 | } | 689 | } |
701 | single_step_set(current); | 690 | single_step_set(current); |
702 | return 0; | ||
703 | } | 691 | } |
704 | 692 | ||
705 | asmlinkage void | 693 | asmlinkage void |
706 | do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) | 694 | do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) |
707 | { | 695 | { |
708 | if (thread_flags & _TIF_SIGPENDING) | 696 | if (thread_flags & _TIF_SIGPENDING) |
709 | do_signal(¤t->blocked, regs, syscall); | 697 | do_signal(regs, syscall); |
710 | } | 698 | } |