aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/signal.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2012-07-19 12:48:21 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2012-07-28 06:11:52 -0400
commit81783786d5cf4aa0d3e15bb0fac856aa8ebf1a76 (patch)
treeea1266df42b055ef03df4f3937f6c6b4326bafc7 /arch/arm/kernel/signal.c
parent0a267fa6a15d41c4061358bf2e67c633fdbffc90 (diff)
ARM: 7473/1: deal with handlerless restarts without leaving the kernel
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/kernel/signal.c')
-rw-r--r--arch/arm/kernel/signal.c50
1 files changed, 29 insertions, 21 deletions
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
index 8756e5dcd377..99851cb9591d 100644
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -569,12 +569,13 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
569 * the kernel can handle, and then we build all the user-level signal handling 569 * the kernel can handle, and then we build all the user-level signal handling
570 * stack-frames in one go after that. 570 * stack-frames in one go after that.
571 */ 571 */
572static void do_signal(struct pt_regs *regs, int syscall) 572static int do_signal(struct pt_regs *regs, int syscall)
573{ 573{
574 unsigned int retval = 0, continue_addr = 0, restart_addr = 0; 574 unsigned int retval = 0, continue_addr = 0, restart_addr = 0;
575 struct k_sigaction ka; 575 struct k_sigaction ka;
576 siginfo_t info; 576 siginfo_t info;
577 int signr; 577 int signr;
578 int restart = 0;
578 579
579 /* 580 /*
580 * If we were from a system call, check for system call restarting... 581 * If we were from a system call, check for system call restarting...
@@ -589,10 +590,12 @@ static void do_signal(struct pt_regs *regs, int syscall)
589 * debugger will see the already changed PSW. 590 * debugger will see the already changed PSW.
590 */ 591 */
591 switch (retval) { 592 switch (retval) {
593 case -ERESTART_RESTARTBLOCK:
594 restart++;
592 case -ERESTARTNOHAND: 595 case -ERESTARTNOHAND:
593 case -ERESTARTSYS: 596 case -ERESTARTSYS:
594 case -ERESTARTNOINTR: 597 case -ERESTARTNOINTR:
595 case -ERESTART_RESTARTBLOCK: 598 restart++;
596 regs->ARM_r0 = regs->ARM_ORIG_r0; 599 regs->ARM_r0 = regs->ARM_ORIG_r0;
597 regs->ARM_pc = restart_addr; 600 regs->ARM_pc = restart_addr;
598 break; 601 break;
@@ -604,13 +607,15 @@ static void do_signal(struct pt_regs *regs, int syscall)
604 * point the debugger may change all our registers ... 607 * point the debugger may change all our registers ...
605 */ 608 */
606 signr = get_signal_to_deliver(&info, &ka, regs, NULL); 609 signr = get_signal_to_deliver(&info, &ka, regs, NULL);
610 /*
611 * Depending on the signal settings we may need to revert the
612 * decision to restart the system call. But skip this if a
613 * debugger has chosen to restart at a different PC.
614 */
615 if (regs->ARM_pc != restart_addr)
616 restart = 0;
607 if (signr > 0) { 617 if (signr > 0) {
608 /* 618 if (unlikely(restart)) {
609 * Depending on the signal settings we may need to revert the
610 * decision to restart the system call. But skip this if a
611 * debugger has chosen to restart at a different PC.
612 */
613 if (regs->ARM_pc == restart_addr) {
614 if (retval == -ERESTARTNOHAND || 619 if (retval == -ERESTARTNOHAND ||
615 retval == -ERESTART_RESTARTBLOCK 620 retval == -ERESTART_RESTARTBLOCK
616 || (retval == -ERESTARTSYS 621 || (retval == -ERESTARTSYS
@@ -618,28 +623,23 @@ static void do_signal(struct pt_regs *regs, int syscall)
618 regs->ARM_r0 = -EINTR; 623 regs->ARM_r0 = -EINTR;
619 regs->ARM_pc = continue_addr; 624 regs->ARM_pc = continue_addr;
620 } 625 }
621 clear_thread_flag(TIF_SYSCALL_RESTARTSYS);
622 } 626 }
623 627
624 handle_signal(signr, &ka, &info, regs); 628 handle_signal(signr, &ka, &info, regs);
625 return; 629 return 0;
626 } 630 }
627 631
628 if (syscall) { 632 if (unlikely(restart)) {
629 /* 633 if (restart > 1)
630 * Handle restarting a different system call. As above,
631 * if a debugger has chosen to restart at a different PC,
632 * ignore the restart.
633 */
634 if (retval == -ERESTART_RESTARTBLOCK
635 && regs->ARM_pc == restart_addr)
636 set_thread_flag(TIF_SYSCALL_RESTARTSYS); 634 set_thread_flag(TIF_SYSCALL_RESTARTSYS);
635 regs->ARM_pc = continue_addr;
637 } 636 }
638 637
639 restore_saved_sigmask(); 638 restore_saved_sigmask();
639 return restart;
640} 640}
641 641
642asmlinkage void 642asmlinkage int
643do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) 643do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
644{ 644{
645 do { 645 do {
@@ -647,10 +647,17 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
647 schedule(); 647 schedule();
648 } else { 648 } else {
649 if (unlikely(!user_mode(regs))) 649 if (unlikely(!user_mode(regs)))
650 return; 650 return 0;
651 local_irq_enable(); 651 local_irq_enable();
652 if (thread_flags & _TIF_SIGPENDING) { 652 if (thread_flags & _TIF_SIGPENDING) {
653 do_signal(regs, syscall); 653 if (unlikely(do_signal(regs, syscall))) {
654 /*
655 * Restart without handlers.
656 * Deal with it without leaving
657 * the kernel space.
658 */
659 return 1;
660 }
654 syscall = 0; 661 syscall = 0;
655 } else { 662 } else {
656 clear_thread_flag(TIF_NOTIFY_RESUME); 663 clear_thread_flag(TIF_NOTIFY_RESUME);
@@ -660,4 +667,5 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
660 local_irq_disable(); 667 local_irq_disable();
661 thread_flags = current_thread_info()->flags; 668 thread_flags = current_thread_info()->flags;
662 } while (thread_flags & _TIF_WORK_MASK); 669 } while (thread_flags & _TIF_WORK_MASK);
670 return 0;
663} 671}