diff options
Diffstat (limited to 'arch/arm/kernel/signal.c')
-rw-r--r-- | arch/arm/kernel/signal.c | 94 |
1 files changed, 45 insertions, 49 deletions
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index f6bc5d44278..1423a341978 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/personality.h> | 12 | #include <linux/personality.h> |
13 | #include <linux/freezer.h> | 13 | #include <linux/freezer.h> |
14 | #include <linux/uaccess.h> | 14 | #include <linux/uaccess.h> |
15 | #include <linux/tracehook.h> | ||
15 | 16 | ||
16 | #include <asm/elf.h> | 17 | #include <asm/elf.h> |
17 | #include <asm/cacheflush.h> | 18 | #include <asm/cacheflush.h> |
@@ -47,57 +48,22 @@ const unsigned long sigreturn_codes[7] = { | |||
47 | MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, | 48 | MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, |
48 | }; | 49 | }; |
49 | 50 | ||
50 | static int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall); | ||
51 | |||
52 | /* | 51 | /* |
53 | * atomically swap in the new signal mask, and wait for a signal. | 52 | * atomically swap in the new signal mask, and wait for a signal. |
54 | */ | 53 | */ |
55 | asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask, struct pt_regs *regs) | 54 | asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask) |
56 | { | 55 | { |
57 | sigset_t saveset; | ||
58 | |||
59 | mask &= _BLOCKABLE; | 56 | mask &= _BLOCKABLE; |
60 | spin_lock_irq(¤t->sighand->siglock); | 57 | spin_lock_irq(¤t->sighand->siglock); |
61 | saveset = current->blocked; | 58 | current->saved_sigmask = current->blocked; |
62 | siginitset(¤t->blocked, mask); | 59 | siginitset(¤t->blocked, mask); |
63 | recalc_sigpending(); | 60 | recalc_sigpending(); |
64 | spin_unlock_irq(¤t->sighand->siglock); | 61 | spin_unlock_irq(¤t->sighand->siglock); |
65 | regs->ARM_r0 = -EINTR; | ||
66 | 62 | ||
67 | while (1) { | 63 | current->state = TASK_INTERRUPTIBLE; |
68 | current->state = TASK_INTERRUPTIBLE; | 64 | schedule(); |
69 | schedule(); | 65 | set_restore_sigmask(); |
70 | if (do_signal(&saveset, regs, 0)) | 66 | return -ERESTARTNOHAND; |
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 | |||
95 | while (1) { | ||
96 | current->state = TASK_INTERRUPTIBLE; | ||
97 | schedule(); | ||
98 | if (do_signal(&saveset, regs, 0)) | ||
99 | return regs->ARM_r0; | ||
100 | } | ||
101 | } | 67 | } |
102 | 68 | ||
103 | asmlinkage int | 69 | asmlinkage int |
@@ -545,7 +511,7 @@ static inline void setup_syscall_restart(struct pt_regs *regs) | |||
545 | /* | 511 | /* |
546 | * OK, we're invoking a handler | 512 | * OK, we're invoking a handler |
547 | */ | 513 | */ |
548 | static void | 514 | static int |
549 | handle_signal(unsigned long sig, struct k_sigaction *ka, | 515 | handle_signal(unsigned long sig, struct k_sigaction *ka, |
550 | siginfo_t *info, sigset_t *oldset, | 516 | siginfo_t *info, sigset_t *oldset, |
551 | struct pt_regs * regs, int syscall) | 517 | struct pt_regs * regs, int syscall) |
@@ -596,7 +562,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, | |||
596 | 562 | ||
597 | if (ret != 0) { | 563 | if (ret != 0) { |
598 | force_sigsegv(sig, tsk); | 564 | force_sigsegv(sig, tsk); |
599 | return; | 565 | return ret; |
600 | } | 566 | } |
601 | 567 | ||
602 | /* | 568 | /* |
@@ -610,6 +576,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, | |||
610 | recalc_sigpending(); | 576 | recalc_sigpending(); |
611 | spin_unlock_irq(&tsk->sighand->siglock); | 577 | spin_unlock_irq(&tsk->sighand->siglock); |
612 | 578 | ||
579 | return 0; | ||
613 | } | 580 | } |
614 | 581 | ||
615 | /* | 582 | /* |
@@ -621,7 +588,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 | 588 | * the kernel can handle, and then we build all the user-level signal handling |
622 | * stack-frames in one go after that. | 589 | * stack-frames in one go after that. |
623 | */ | 590 | */ |
624 | static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) | 591 | static void do_signal(struct pt_regs *regs, int syscall) |
625 | { | 592 | { |
626 | struct k_sigaction ka; | 593 | struct k_sigaction ka; |
627 | siginfo_t info; | 594 | siginfo_t info; |
@@ -634,7 +601,7 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) | |||
634 | * if so. | 601 | * if so. |
635 | */ | 602 | */ |
636 | if (!user_mode(regs)) | 603 | if (!user_mode(regs)) |
637 | return 0; | 604 | return; |
638 | 605 | ||
639 | if (try_to_freeze()) | 606 | if (try_to_freeze()) |
640 | goto no_signal; | 607 | goto no_signal; |
@@ -643,9 +610,24 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) | |||
643 | 610 | ||
644 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | 611 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); |
645 | if (signr > 0) { | 612 | if (signr > 0) { |
646 | handle_signal(signr, &ka, &info, oldset, regs, syscall); | 613 | sigset_t *oldset; |
614 | |||
615 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
616 | oldset = ¤t->saved_sigmask; | ||
617 | else | ||
618 | oldset = ¤t->blocked; | ||
619 | if (handle_signal(signr, &ka, &info, oldset, regs, syscall) == 0) { | ||
620 | /* | ||
621 | * A signal was successfully delivered; the saved | ||
622 | * sigmask will have been stored in the signal frame, | ||
623 | * and will be restored by sigreturn, so we can simply | ||
624 | * clear the TIF_RESTORE_SIGMASK flag. | ||
625 | */ | ||
626 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
627 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
628 | } | ||
647 | single_step_set(current); | 629 | single_step_set(current); |
648 | return 1; | 630 | return; |
649 | } | 631 | } |
650 | 632 | ||
651 | no_signal: | 633 | no_signal: |
@@ -697,14 +679,28 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) | |||
697 | regs->ARM_r0 == -ERESTARTNOINTR) { | 679 | regs->ARM_r0 == -ERESTARTNOINTR) { |
698 | setup_syscall_restart(regs); | 680 | setup_syscall_restart(regs); |
699 | } | 681 | } |
682 | |||
683 | /* If there's no signal to deliver, we just put the saved sigmask | ||
684 | * back. | ||
685 | */ | ||
686 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) { | ||
687 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
688 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | ||
689 | } | ||
700 | } | 690 | } |
701 | single_step_set(current); | 691 | single_step_set(current); |
702 | return 0; | ||
703 | } | 692 | } |
704 | 693 | ||
705 | asmlinkage void | 694 | asmlinkage void |
706 | do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) | 695 | do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) |
707 | { | 696 | { |
708 | if (thread_flags & _TIF_SIGPENDING) | 697 | if (thread_flags & _TIF_SIGPENDING) |
709 | do_signal(¤t->blocked, regs, syscall); | 698 | do_signal(regs, syscall); |
699 | |||
700 | if (thread_flags & _TIF_NOTIFY_RESUME) { | ||
701 | clear_thread_flag(TIF_NOTIFY_RESUME); | ||
702 | tracehook_notify_resume(regs); | ||
703 | if (current->replacement_session_keyring) | ||
704 | key_replace_session_keyring(); | ||
705 | } | ||
710 | } | 706 | } |