| 1 | From: Mikael Pettersson <mikpe@it.uu.se> |
| 2 | Date: Sat, 15 Aug 2009 11:58:11 +0000 (+0100) |
| 3 | Subject: ARM: 5677/1: ARM support for TIF_RESTORE_SIGMASK/pselect6/ppoll/epoll_pwait |
| 4 | X-Git-Tag: next-20090817~86^2~1^6 |
| 5 | X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Fnext%2Flinux-next.git;a=commitdiff_plain;h=369842658a36bcea28ecb643ba4bdb53919330dd |
| 6 | |
| 7 | ARM: 5677/1: ARM support for TIF_RESTORE_SIGMASK/pselect6/ppoll/epoll_pwait |
| 8 | |
| 9 | This patch adds support for TIF_RESTORE_SIGMASK to ARM's |
| 10 | signal handling, which allows to hook up the pselect6, ppoll, |
| 11 | and epoll_pwait syscalls on ARM. |
| 12 | |
| 13 | Tested here with eabi userspace and a test program with a |
| 14 | deliberate race between a child's exit and the parent's |
| 15 | sigprocmask/select sequence. Using sys_pselect6() instead |
| 16 | of sigprocmask/select reliably prevents the race. |
| 17 | |
| 18 | The other arch's support for TIF_RESTORE_SIGMASK has evolved |
| 19 | over time: |
| 20 | |
| 21 | In 2.6.16: |
| 22 | - add TIF_RESTORE_SIGMASK which parallels TIF_SIGPENDING |
| 23 | - test both when checking for pending signal [changed later] |
| 24 | - reimplement sys_sigsuspend() to use current->saved_sigmask, |
| 25 | TIF_RESTORE_SIGMASK [changed later], and -ERESTARTNOHAND; |
| 26 | ditto for sys_rt_sigsuspend(), but drop private code and |
| 27 | use common code via __ARCH_WANT_SYS_RT_SIGSUSPEND; |
| 28 | - there are now no "extra" calls to do_signal() so its oldset |
| 29 | parameter is always ¤t->blocked so need not be passed, |
| 30 | also its return value is changed to void |
| 31 | - change handle_signal() to return 0/-errno |
| 32 | - change do_signal() to honor TIF_RESTORE_SIGMASK: |
| 33 | + get oldset from current->saved_sigmask if TIF_RESTORE_SIGMASK |
| 34 | is set |
| 35 | + if handle_signal() was successful then clear TIF_RESTORE_SIGMASK |
| 36 | + if no signal was delivered and TIF_RESTORE_SIGMASK is set then |
| 37 | clear it and restore the sigmask |
| 38 | - hook up sys_pselect6() and sys_ppoll() |
| 39 | |
| 40 | In 2.6.19: |
| 41 | - hook up sys_epoll_pwait() |
| 42 | |
| 43 | In 2.6.26: |
| 44 | - allow archs to override how TIF_RESTORE_SIGMASK is implemented; |
| 45 | default set_restore_sigmask() sets both TIF_RESTORE_SIGMASK and |
| 46 | TIF_SIGPENDING; archs need now just test TIF_SIGPENDING again |
| 47 | when checking for pending signal work; some archs now implement |
| 48 | TIF_RESTORE_SIGMASK as a secondary/non-atomic thread flag bit |
| 49 | - call set_restore_sigmask() in sys_sigsuspend() instead of setting |
| 50 | TIF_RESTORE_SIGMASK |
| 51 | |
| 52 | In 2.6.29-rc: |
| 53 | - kill sys_pselect7() which no arch wanted |
| 54 | |
| 55 | So for 2.6.31-rc6/ARM this patch does the following: |
| 56 | - Add TIF_RESTORE_SIGMASK. Use the generic set_restore_sigmask() |
| 57 | which sets both TIF_SIGPENDING and TIF_RESTORE_SIGMASK, so |
| 58 | TIF_RESTORE_SIGMASK need not claim one of the scarce low thread |
| 59 | flags, and existing TIF_SIGPENDING and _TIF_WORK_MASK tests need |
| 60 | not be extended for TIF_RESTORE_SIGMASK. |
| 61 | - sys_sigsuspend() is reimplemented to use current->saved_sigmask |
| 62 | and set_restore_sigmask(), making it identical to most other archs |
| 63 | - The private code for sys_rt_sigsuspend() is removed, instead |
| 64 | generic code supplies it via __ARCH_WANT_SYS_RT_SIGSUSPEND. |
| 65 | - sys_sigsuspend() and sys_rt_sigsuspend() no longer need a pt_regs |
| 66 | parameter, so their assembly code wrappers are removed. |
| 67 | - handle_signal() is changed to return 0 on success or -errno. |
| 68 | - The oldset parameter to do_signal() is now redundant and removed, |
| 69 | and the return value is now also redundant and changed to void. |
| 70 | - do_signal() is changed to honor TIF_RESTORE_SIGMASK: |
| 71 | + get oldset from current->saved_sigmask if TIF_RESTORE_SIGMASK |
| 72 | is set |
| 73 | + if handle_signal() was successful then clear TIF_RESTORE_SIGMASK |
| 74 | + if no signal was delivered and TIF_RESTORE_SIGMASK is set then |
| 75 | clear it and restore the sigmask |
| 76 | - Hook up sys_pselect6, sys_ppoll, and sys_epoll_pwait. |
| 77 | |
| 78 | Signed-off-by: Mikael Pettersson <mikpe@it.uu.se> |
| 79 | Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> |
| 80 | --- |
| 81 | |
| 82 | --- a/arch/arm/include/asm/thread_info.h |
| 83 | +++ b/arch/arm/include/asm/thread_info.h |
| 84 | @@ -141,6 +141,7 @@ |
| 85 | #define TIF_USING_IWMMXT 17 |
| 86 | #define TIF_MEMDIE 18 |
| 87 | #define TIF_FREEZE 19 |
| 88 | +#define TIF_RESTORE_SIGMASK 20 |
| 89 | |
| 90 | #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) |
| 91 | #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) |
| 92 | @@ -148,6 +149,7 @@ |
| 93 | #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) |
| 94 | #define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT) |
| 95 | #define _TIF_FREEZE (1 << TIF_FREEZE) |
| 96 | +#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK) |
| 97 | |
| 98 | /* |
| 99 | * Change these and you break ASM code in entry-common.S |
| 100 | --- a/arch/arm/include/asm/unistd.h |
| 101 | +++ b/arch/arm/include/asm/unistd.h |
| 102 | @@ -360,8 +360,8 @@ |
| 103 | #define __NR_readlinkat (__NR_SYSCALL_BASE+332) |
| 104 | #define __NR_fchmodat (__NR_SYSCALL_BASE+333) |
| 105 | #define __NR_faccessat (__NR_SYSCALL_BASE+334) |
| 106 | - /* 335 for pselect6 */ |
| 107 | - /* 336 for ppoll */ |
| 108 | +#define __NR_pselect6 (__NR_SYSCALL_BASE+335) |
| 109 | +#define __NR_ppoll (__NR_SYSCALL_BASE+336) |
| 110 | #define __NR_unshare (__NR_SYSCALL_BASE+337) |
| 111 | #define __NR_set_robust_list (__NR_SYSCALL_BASE+338) |
| 112 | #define __NR_get_robust_list (__NR_SYSCALL_BASE+339) |
| 113 | @@ -372,7 +372,7 @@ |
| 114 | #define __NR_vmsplice (__NR_SYSCALL_BASE+343) |
| 115 | #define __NR_move_pages (__NR_SYSCALL_BASE+344) |
| 116 | #define __NR_getcpu (__NR_SYSCALL_BASE+345) |
| 117 | - /* 346 for epoll_pwait */ |
| 118 | +#define __NR_epoll_pwait (__NR_SYSCALL_BASE+346) |
| 119 | #define __NR_kexec_load (__NR_SYSCALL_BASE+347) |
| 120 | #define __NR_utimensat (__NR_SYSCALL_BASE+348) |
| 121 | #define __NR_signalfd (__NR_SYSCALL_BASE+349) |
| 122 | @@ -432,6 +432,7 @@ |
| 123 | #define __ARCH_WANT_SYS_SIGPENDING |
| 124 | #define __ARCH_WANT_SYS_SIGPROCMASK |
| 125 | #define __ARCH_WANT_SYS_RT_SIGACTION |
| 126 | +#define __ARCH_WANT_SYS_RT_SIGSUSPEND |
| 127 | |
| 128 | #if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT) |
| 129 | #define __ARCH_WANT_SYS_TIME |
| 130 | --- a/arch/arm/kernel/calls.S |
| 131 | +++ b/arch/arm/kernel/calls.S |
| 132 | @@ -81,7 +81,7 @@ |
| 133 | CALL(sys_ni_syscall) /* was sys_ssetmask */ |
| 134 | /* 70 */ CALL(sys_setreuid16) |
| 135 | CALL(sys_setregid16) |
| 136 | - CALL(sys_sigsuspend_wrapper) |
| 137 | + CALL(sys_sigsuspend) |
| 138 | CALL(sys_sigpending) |
| 139 | CALL(sys_sethostname) |
| 140 | /* 75 */ CALL(sys_setrlimit) |
| 141 | @@ -188,7 +188,7 @@ |
| 142 | CALL(sys_rt_sigpending) |
| 143 | CALL(sys_rt_sigtimedwait) |
| 144 | CALL(sys_rt_sigqueueinfo) |
| 145 | - CALL(sys_rt_sigsuspend_wrapper) |
| 146 | + CALL(sys_rt_sigsuspend) |
| 147 | /* 180 */ CALL(ABI(sys_pread64, sys_oabi_pread64)) |
| 148 | CALL(ABI(sys_pwrite64, sys_oabi_pwrite64)) |
| 149 | CALL(sys_chown16) |
| 150 | @@ -344,8 +344,8 @@ |
| 151 | CALL(sys_readlinkat) |
| 152 | CALL(sys_fchmodat) |
| 153 | CALL(sys_faccessat) |
| 154 | -/* 335 */ CALL(sys_ni_syscall) /* eventually pselect6 */ |
| 155 | - CALL(sys_ni_syscall) /* eventually ppoll */ |
| 156 | +/* 335 */ CALL(sys_pselect6) |
| 157 | + CALL(sys_ppoll) |
| 158 | CALL(sys_unshare) |
| 159 | CALL(sys_set_robust_list) |
| 160 | CALL(sys_get_robust_list) |
| 161 | @@ -355,7 +355,7 @@ |
| 162 | CALL(sys_vmsplice) |
| 163 | CALL(sys_move_pages) |
| 164 | /* 345 */ CALL(sys_getcpu) |
| 165 | - CALL(sys_ni_syscall) /* eventually epoll_pwait */ |
| 166 | + CALL(sys_epoll_pwait) |
| 167 | CALL(sys_kexec_load) |
| 168 | CALL(sys_utimensat) |
| 169 | CALL(sys_signalfd) |
| 170 | --- a/arch/arm/kernel/entry-common.S |
| 171 | +++ b/arch/arm/kernel/entry-common.S |
| 172 | @@ -373,16 +373,6 @@ |
| 173 | b sys_clone |
| 174 | ENDPROC(sys_clone_wrapper) |
| 175 | |
| 176 | -sys_sigsuspend_wrapper: |
| 177 | - add r3, sp, #S_OFF |
| 178 | - b sys_sigsuspend |
| 179 | -ENDPROC(sys_sigsuspend_wrapper) |
| 180 | - |
| 181 | -sys_rt_sigsuspend_wrapper: |
| 182 | - add r2, sp, #S_OFF |
| 183 | - b sys_rt_sigsuspend |
| 184 | -ENDPROC(sys_rt_sigsuspend_wrapper) |
| 185 | - |
| 186 | sys_sigreturn_wrapper: |
| 187 | add r0, sp, #S_OFF |
| 188 | b sys_sigreturn |
| 189 | --- a/arch/arm/kernel/signal.c |
| 190 | +++ b/arch/arm/kernel/signal.c |
| 191 | @@ -47,57 +47,22 @@ |
| 192 | MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, |
| 193 | }; |
| 194 | |
| 195 | -static int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall); |
| 196 | - |
| 197 | /* |
| 198 | * atomically swap in the new signal mask, and wait for a signal. |
| 199 | */ |
| 200 | -asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask, struct pt_regs *regs) |
| 201 | +asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask) |
| 202 | { |
| 203 | - sigset_t saveset; |
| 204 | - |
| 205 | mask &= _BLOCKABLE; |
| 206 | spin_lock_irq(¤t->sighand->siglock); |
| 207 | - saveset = current->blocked; |
| 208 | + current->saved_sigmask = current->blocked; |
| 209 | siginitset(¤t->blocked, mask); |
| 210 | recalc_sigpending(); |
| 211 | spin_unlock_irq(¤t->sighand->siglock); |
| 212 | - regs->ARM_r0 = -EINTR; |
| 213 | - |
| 214 | - while (1) { |
| 215 | - current->state = TASK_INTERRUPTIBLE; |
| 216 | - schedule(); |
| 217 | - if (do_signal(&saveset, regs, 0)) |
| 218 | - return regs->ARM_r0; |
| 219 | - } |
| 220 | -} |
| 221 | - |
| 222 | -asmlinkage int |
| 223 | -sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, struct pt_regs *regs) |
| 224 | -{ |
| 225 | - sigset_t saveset, newset; |
| 226 | - |
| 227 | - /* XXX: Don't preclude handling different sized sigset_t's. */ |
| 228 | - if (sigsetsize != sizeof(sigset_t)) |
| 229 | - return -EINVAL; |
| 230 | - |
| 231 | - if (copy_from_user(&newset, unewset, sizeof(newset))) |
| 232 | - return -EFAULT; |
| 233 | - sigdelsetmask(&newset, ~_BLOCKABLE); |
| 234 | - |
| 235 | - spin_lock_irq(¤t->sighand->siglock); |
| 236 | - saveset = current->blocked; |
| 237 | - current->blocked = newset; |
| 238 | - recalc_sigpending(); |
| 239 | - spin_unlock_irq(¤t->sighand->siglock); |
| 240 | - regs->ARM_r0 = -EINTR; |
| 241 | |
| 242 | - while (1) { |
| 243 | - current->state = TASK_INTERRUPTIBLE; |
| 244 | - schedule(); |
| 245 | - if (do_signal(&saveset, regs, 0)) |
| 246 | - return regs->ARM_r0; |
| 247 | - } |
| 248 | + current->state = TASK_INTERRUPTIBLE; |
| 249 | + schedule(); |
| 250 | + set_restore_sigmask(); |
| 251 | + return -ERESTARTNOHAND; |
| 252 | } |
| 253 | |
| 254 | asmlinkage int |
| 255 | @@ -545,7 +510,7 @@ |
| 256 | /* |
| 257 | * OK, we're invoking a handler |
| 258 | */ |
| 259 | -static void |
| 260 | +static int |
| 261 | handle_signal(unsigned long sig, struct k_sigaction *ka, |
| 262 | siginfo_t *info, sigset_t *oldset, |
| 263 | struct pt_regs * regs, int syscall) |
| 264 | @@ -596,7 +561,7 @@ |
| 265 | |
| 266 | if (ret != 0) { |
| 267 | force_sigsegv(sig, tsk); |
| 268 | - return; |
| 269 | + return ret; |
| 270 | } |
| 271 | |
| 272 | /* |
| 273 | @@ -610,6 +575,7 @@ |
| 274 | recalc_sigpending(); |
| 275 | spin_unlock_irq(&tsk->sighand->siglock); |
| 276 | |
| 277 | + return 0; |
| 278 | } |
| 279 | |
| 280 | /* |
| 281 | @@ -621,11 +587,12 @@ |
| 282 | * the kernel can handle, and then we build all the user-level signal handling |
| 283 | * stack-frames in one go after that. |
| 284 | */ |
| 285 | -static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) |
| 286 | +static void do_signal(struct pt_regs *regs, int syscall) |
| 287 | { |
| 288 | struct k_sigaction ka; |
| 289 | siginfo_t info; |
| 290 | int signr; |
| 291 | + sigset_t *oldset; |
| 292 | |
| 293 | /* |
| 294 | * We want the common case to go fast, which |
| 295 | @@ -634,18 +601,32 @@ |
| 296 | * if so. |
| 297 | */ |
| 298 | if (!user_mode(regs)) |
| 299 | - return 0; |
| 300 | + return; |
| 301 | |
| 302 | if (try_to_freeze()) |
| 303 | goto no_signal; |
| 304 | |
| 305 | single_step_clear(current); |
| 306 | |
| 307 | + if (test_thread_flag(TIF_RESTORE_SIGMASK)) |
| 308 | + oldset = ¤t->saved_sigmask; |
| 309 | + else |
| 310 | + oldset = ¤t->blocked; |
| 311 | + |
| 312 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); |
| 313 | if (signr > 0) { |
| 314 | - handle_signal(signr, &ka, &info, oldset, regs, syscall); |
| 315 | + if (handle_signal(signr, &ka, &info, oldset, regs, syscall) == 0) { |
| 316 | + /* |
| 317 | + * A signal was successfully delivered; the saved |
| 318 | + * sigmask will have been stored in the signal frame, |
| 319 | + * and will be restored by sigreturn, so we can simply |
| 320 | + * clear the TIF_RESTORE_SIGMASK flag. |
| 321 | + */ |
| 322 | + if (test_thread_flag(TIF_RESTORE_SIGMASK)) |
| 323 | + clear_thread_flag(TIF_RESTORE_SIGMASK); |
| 324 | + } |
| 325 | single_step_set(current); |
| 326 | - return 1; |
| 327 | + return; |
| 328 | } |
| 329 | |
| 330 | no_signal: |
| 331 | @@ -697,14 +678,21 @@ |
| 332 | regs->ARM_r0 == -ERESTARTNOINTR) { |
| 333 | setup_syscall_restart(regs); |
| 334 | } |
| 335 | + |
| 336 | + /* If there's no signal to deliver, we just put the saved sigmask |
| 337 | + * back. |
| 338 | + */ |
| 339 | + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { |
| 340 | + clear_thread_flag(TIF_RESTORE_SIGMASK); |
| 341 | + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); |
| 342 | + } |
| 343 | } |
| 344 | single_step_set(current); |
| 345 | - return 0; |
| 346 | } |
| 347 | |
| 348 | asmlinkage void |
| 349 | do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) |
| 350 | { |
| 351 | if (thread_flags & _TIF_SIGPENDING) |
| 352 | - do_signal(¤t->blocked, regs, syscall); |
| 353 | + do_signal(regs, syscall); |
| 354 | } |
| 355 | |