diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2006-01-19 05:42:49 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-01-19 05:42:49 -0500 |
commit | 2d7d5f05111a9d913131a2764d8b20157f8f758d (patch) | |
tree | 792deb7a3b9f72894d16affff1569a15b35e428b /arch/sparc64/kernel/signal.c | |
parent | f7111ceb5266750db2a1d193b98fb6a3d9b5a56a (diff) |
[SPARC]: Add support for *at(), ppoll, and pselect syscalls.
This also includes by necessity _TIF_RESTORE_SIGMASK support,
which actually resulted in a lot of cleanups.
The sparc signal handling code is quite a mess and I should
clean it up some day.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64/kernel/signal.c')
-rw-r--r-- | arch/sparc64/kernel/signal.c | 151 |
1 files changed, 40 insertions, 111 deletions
diff --git a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c index 60f5dfabb1e1..ca11a4c457d4 100644 --- a/arch/sparc64/kernel/signal.c +++ b/arch/sparc64/kernel/signal.c | |||
@@ -36,9 +36,6 @@ | |||
36 | 36 | ||
37 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | 37 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) |
38 | 38 | ||
39 | static int do_signal(sigset_t *oldset, struct pt_regs * regs, | ||
40 | unsigned long orig_o0, int ret_from_syscall); | ||
41 | |||
42 | /* {set, get}context() needed for 64-bit SparcLinux userland. */ | 39 | /* {set, get}context() needed for 64-bit SparcLinux userland. */ |
43 | asmlinkage void sparc64_set_context(struct pt_regs *regs) | 40 | asmlinkage void sparc64_set_context(struct pt_regs *regs) |
44 | { | 41 | { |
@@ -242,114 +239,29 @@ struct rt_signal_frame { | |||
242 | /* Align macros */ | 239 | /* Align macros */ |
243 | #define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame) + 7) & (~7))) | 240 | #define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame) + 7) & (~7))) |
244 | 241 | ||
245 | /* | 242 | static long _sigpause_common(old_sigset_t set) |
246 | * atomically swap in the new signal mask, and wait for a signal. | ||
247 | * This is really tricky on the Sparc, watch out... | ||
248 | */ | ||
249 | asmlinkage void _sigpause_common(old_sigset_t set, struct pt_regs *regs) | ||
250 | { | 243 | { |
251 | sigset_t saveset; | ||
252 | |||
253 | #ifdef CONFIG_SPARC32_COMPAT | ||
254 | if (test_thread_flag(TIF_32BIT)) { | ||
255 | extern asmlinkage void _sigpause32_common(compat_old_sigset_t, | ||
256 | struct pt_regs *); | ||
257 | _sigpause32_common(set, regs); | ||
258 | return; | ||
259 | } | ||
260 | #endif | ||
261 | set &= _BLOCKABLE; | 244 | set &= _BLOCKABLE; |
262 | spin_lock_irq(¤t->sighand->siglock); | 245 | spin_lock_irq(¤t->sighand->siglock); |
263 | saveset = current->blocked; | 246 | current->saved_sigmask = current->blocked; |
264 | siginitset(¤t->blocked, set); | 247 | siginitset(¤t->blocked, set); |
265 | recalc_sigpending(); | 248 | recalc_sigpending(); |
266 | spin_unlock_irq(¤t->sighand->siglock); | 249 | spin_unlock_irq(¤t->sighand->siglock); |
267 | |||
268 | if (test_thread_flag(TIF_32BIT)) { | ||
269 | regs->tpc = (regs->tnpc & 0xffffffff); | ||
270 | regs->tnpc = (regs->tnpc + 4) & 0xffffffff; | ||
271 | } else { | ||
272 | regs->tpc = regs->tnpc; | ||
273 | regs->tnpc += 4; | ||
274 | } | ||
275 | 250 | ||
276 | /* Condition codes and return value where set here for sigpause, | 251 | current->state = TASK_INTERRUPTIBLE; |
277 | * and so got used by setup_frame, which again causes sigreturn() | 252 | schedule(); |
278 | * to return -EINTR. | 253 | set_thread_flag(TIF_RESTORE_SIGMASK); |
279 | */ | 254 | return -ERESTARTNOHAND; |
280 | while (1) { | ||
281 | current->state = TASK_INTERRUPTIBLE; | ||
282 | schedule(); | ||
283 | /* | ||
284 | * Return -EINTR and set condition code here, | ||
285 | * so the interrupted system call actually returns | ||
286 | * these. | ||
287 | */ | ||
288 | regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY); | ||
289 | regs->u_regs[UREG_I0] = EINTR; | ||
290 | if (do_signal(&saveset, regs, 0, 0)) | ||
291 | return; | ||
292 | } | ||
293 | } | 255 | } |
294 | 256 | ||
295 | asmlinkage void do_sigpause(unsigned int set, struct pt_regs *regs) | 257 | asmlinkage long sys_sigpause(unsigned int set) |
296 | { | 258 | { |
297 | _sigpause_common(set, regs); | 259 | return _sigpause_common(set); |
298 | } | 260 | } |
299 | 261 | ||
300 | asmlinkage void do_sigsuspend(struct pt_regs *regs) | 262 | asmlinkage long sys_sigsuspend(old_sigset_t set) |
301 | { | 263 | { |
302 | _sigpause_common(regs->u_regs[UREG_I0], regs); | 264 | return _sigpause_common(set); |
303 | } | ||
304 | |||
305 | asmlinkage void do_rt_sigsuspend(sigset_t __user *uset, size_t sigsetsize, struct pt_regs *regs) | ||
306 | { | ||
307 | sigset_t oldset, set; | ||
308 | |||
309 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
310 | if (sigsetsize != sizeof(sigset_t)) { | ||
311 | regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY); | ||
312 | regs->u_regs[UREG_I0] = EINVAL; | ||
313 | return; | ||
314 | } | ||
315 | if (copy_from_user(&set, uset, sizeof(set))) { | ||
316 | regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY); | ||
317 | regs->u_regs[UREG_I0] = EFAULT; | ||
318 | return; | ||
319 | } | ||
320 | |||
321 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
322 | spin_lock_irq(¤t->sighand->siglock); | ||
323 | oldset = current->blocked; | ||
324 | current->blocked = set; | ||
325 | recalc_sigpending(); | ||
326 | spin_unlock_irq(¤t->sighand->siglock); | ||
327 | |||
328 | if (test_thread_flag(TIF_32BIT)) { | ||
329 | regs->tpc = (regs->tnpc & 0xffffffff); | ||
330 | regs->tnpc = (regs->tnpc + 4) & 0xffffffff; | ||
331 | } else { | ||
332 | regs->tpc = regs->tnpc; | ||
333 | regs->tnpc += 4; | ||
334 | } | ||
335 | |||
336 | /* Condition codes and return value where set here for sigpause, | ||
337 | * and so got used by setup_frame, which again causes sigreturn() | ||
338 | * to return -EINTR. | ||
339 | */ | ||
340 | while (1) { | ||
341 | current->state = TASK_INTERRUPTIBLE; | ||
342 | schedule(); | ||
343 | /* | ||
344 | * Return -EINTR and set condition code here, | ||
345 | * so the interrupted system call actually returns | ||
346 | * these. | ||
347 | */ | ||
348 | regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY); | ||
349 | regs->u_regs[UREG_I0] = EINTR; | ||
350 | if (do_signal(&oldset, regs, 0, 0)) | ||
351 | return; | ||
352 | } | ||
353 | } | 265 | } |
354 | 266 | ||
355 | static inline int | 267 | static inline int |
@@ -607,26 +519,29 @@ static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs, | |||
607 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | 519 | * want to handle. Thus you cannot kill init even with a SIGKILL even by |
608 | * mistake. | 520 | * mistake. |
609 | */ | 521 | */ |
610 | static int do_signal(sigset_t *oldset, struct pt_regs * regs, | 522 | static void do_signal(struct pt_regs *regs, unsigned long orig_i0, int restart_syscall) |
611 | unsigned long orig_i0, int restart_syscall) | ||
612 | { | 523 | { |
613 | siginfo_t info; | 524 | siginfo_t info; |
614 | struct signal_deliver_cookie cookie; | 525 | struct signal_deliver_cookie cookie; |
615 | struct k_sigaction ka; | 526 | struct k_sigaction ka; |
616 | int signr; | 527 | int signr; |
528 | sigset_t *oldset; | ||
617 | 529 | ||
618 | cookie.restart_syscall = restart_syscall; | 530 | cookie.restart_syscall = restart_syscall; |
619 | cookie.orig_i0 = orig_i0; | 531 | cookie.orig_i0 = orig_i0; |
620 | 532 | ||
621 | if (!oldset) | 533 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) |
534 | oldset = ¤t->saved_sigmask; | ||
535 | else | ||
622 | oldset = ¤t->blocked; | 536 | oldset = ¤t->blocked; |
623 | 537 | ||
624 | #ifdef CONFIG_SPARC32_COMPAT | 538 | #ifdef CONFIG_SPARC32_COMPAT |
625 | if (test_thread_flag(TIF_32BIT)) { | 539 | if (test_thread_flag(TIF_32BIT)) { |
626 | extern int do_signal32(sigset_t *, struct pt_regs *, | 540 | extern void do_signal32(sigset_t *, struct pt_regs *, |
627 | unsigned long, int); | 541 | unsigned long, int); |
628 | return do_signal32(oldset, regs, orig_i0, | 542 | do_signal32(oldset, regs, orig_i0, |
629 | cookie.restart_syscall); | 543 | cookie.restart_syscall); |
544 | return; | ||
630 | } | 545 | } |
631 | #endif | 546 | #endif |
632 | 547 | ||
@@ -635,7 +550,15 @@ static int do_signal(sigset_t *oldset, struct pt_regs * regs, | |||
635 | if (cookie.restart_syscall) | 550 | if (cookie.restart_syscall) |
636 | syscall_restart(orig_i0, regs, &ka.sa); | 551 | syscall_restart(orig_i0, regs, &ka.sa); |
637 | handle_signal(signr, &ka, &info, oldset, regs); | 552 | handle_signal(signr, &ka, &info, oldset, regs); |
638 | return 1; | 553 | |
554 | /* a signal was successfully delivered; the saved | ||
555 | * sigmask will have been stored in the signal frame, | ||
556 | * and will be restored by sigreturn, so we can simply | ||
557 | * clear the TIF_RESTORE_SIGMASK flag. | ||
558 | */ | ||
559 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
560 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
561 | return; | ||
639 | } | 562 | } |
640 | if (cookie.restart_syscall && | 563 | if (cookie.restart_syscall && |
641 | (regs->u_regs[UREG_I0] == ERESTARTNOHAND || | 564 | (regs->u_regs[UREG_I0] == ERESTARTNOHAND || |
@@ -652,15 +575,21 @@ static int do_signal(sigset_t *oldset, struct pt_regs * regs, | |||
652 | regs->tpc -= 4; | 575 | regs->tpc -= 4; |
653 | regs->tnpc -= 4; | 576 | regs->tnpc -= 4; |
654 | } | 577 | } |
655 | return 0; | 578 | |
579 | /* if there's no signal to deliver, we just put the saved sigmask | ||
580 | * back | ||
581 | */ | ||
582 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) { | ||
583 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
584 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | ||
585 | } | ||
656 | } | 586 | } |
657 | 587 | ||
658 | void do_notify_resume(sigset_t *oldset, struct pt_regs *regs, | 588 | void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, int restart_syscall, |
659 | unsigned long orig_i0, int restart_syscall, | ||
660 | unsigned long thread_info_flags) | 589 | unsigned long thread_info_flags) |
661 | { | 590 | { |
662 | if (thread_info_flags & _TIF_SIGPENDING) | 591 | if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) |
663 | do_signal(oldset, regs, orig_i0, restart_syscall); | 592 | do_signal(regs, orig_i0, restart_syscall); |
664 | } | 593 | } |
665 | 594 | ||
666 | void ptrace_signal_deliver(struct pt_regs *regs, void *cookie) | 595 | void ptrace_signal_deliver(struct pt_regs *regs, void *cookie) |