ARM: 7473/1: deal with handlerless restarts without leaving the kernel
authorAl Viro <viro@zeniv.linux.org.uk>
Thu, 19 Jul 2012 16:48:21 +0000 (17:48 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Sat, 28 Jul 2012 10:11:52 +0000 (11:11 +0100)
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>
arch/arm/kernel/entry-common.S
arch/arm/kernel/signal.c

index 1873f653641c3b0b979018852ce094b972939035..8ae58c47dce6f69129364b4800fce33622f3bd84 100644 (file)
@@ -54,7 +54,11 @@ work_pending:
        mov     r0, sp                          @ 'regs'
        mov     r2, why                         @ 'syscall'
        bl      do_work_pending
-       b       no_work_pending
+       tst     r0, #1
+       beq     no_work_pending
+       ldmia   sp, {r0 - r6}                   @ have to reload r0 - r6
+       b       local_restart                   @ ... and off we go
+
 /*
  * "slow" syscall return path.  "why" tells us if this was a real syscall.
  */
@@ -396,6 +400,7 @@ ENTRY(vector_swi)
        eor     scno, scno, #__NR_SYSCALL_BASE  @ check OS number
 #endif
 
+local_restart:
        ldr     r10, [tsk, #TI_FLAGS]           @ check for syscall tracing
        stmdb   sp!, {r4, r5}                   @ push fifth and sixth args
 
index 8756e5dcd377d1d1b14712d64e8c7fbc51cf77a2..99851cb9591dc8d0abb7eb41a2bad5d8294344bd 100644 (file)
@@ -569,12 +569,13 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
  * the kernel can handle, and then we build all the user-level signal handling
  * stack-frames in one go after that.
  */
-static void do_signal(struct pt_regs *regs, int syscall)
+static int do_signal(struct pt_regs *regs, int syscall)
 {
        unsigned int retval = 0, continue_addr = 0, restart_addr = 0;
        struct k_sigaction ka;
        siginfo_t info;
        int signr;
+       int restart = 0;
 
        /*
         * 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)
                 * debugger will see the already changed PSW.
                 */
                switch (retval) {
+               case -ERESTART_RESTARTBLOCK:
+                       restart++;
                case -ERESTARTNOHAND:
                case -ERESTARTSYS:
                case -ERESTARTNOINTR:
-               case -ERESTART_RESTARTBLOCK:
+                       restart++;
                        regs->ARM_r0 = regs->ARM_ORIG_r0;
                        regs->ARM_pc = restart_addr;
                        break;
@@ -604,13 +607,15 @@ static void do_signal(struct pt_regs *regs, int syscall)
         * point the debugger may change all our registers ...
         */
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+       /*
+        * Depending on the signal settings we may need to revert the
+        * decision to restart the system call.  But skip this if a
+        * debugger has chosen to restart at a different PC.
+        */
+       if (regs->ARM_pc != restart_addr)
+               restart = 0;
        if (signr > 0) {
-               /*
-                * Depending on the signal settings we may need to revert the
-                * decision to restart the system call.  But skip this if a
-                * debugger has chosen to restart at a different PC.
-                */
-               if (regs->ARM_pc == restart_addr) {
+               if (unlikely(restart)) {
                        if (retval == -ERESTARTNOHAND ||
                            retval == -ERESTART_RESTARTBLOCK
                            || (retval == -ERESTARTSYS
@@ -618,28 +623,23 @@ static void do_signal(struct pt_regs *regs, int syscall)
                                regs->ARM_r0 = -EINTR;
                                regs->ARM_pc = continue_addr;
                        }
-                       clear_thread_flag(TIF_SYSCALL_RESTARTSYS);
                }
 
                handle_signal(signr, &ka, &info, regs);
-               return;
+               return 0;
        }
 
-       if (syscall) {
-               /*
-                * Handle restarting a different system call.  As above,
-                * if a debugger has chosen to restart at a different PC,
-                * ignore the restart.
-                */
-               if (retval == -ERESTART_RESTARTBLOCK
-                   && regs->ARM_pc == restart_addr)
+       if (unlikely(restart)) {
+               if (restart > 1)
                        set_thread_flag(TIF_SYSCALL_RESTARTSYS);
+               regs->ARM_pc = continue_addr;
        }
 
        restore_saved_sigmask();
+       return restart;
 }
 
-asmlinkage void
+asmlinkage int
 do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
 {
        do {
@@ -647,10 +647,17 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
                        schedule();
                } else {
                        if (unlikely(!user_mode(regs)))
-                               return;
+                               return 0;
                        local_irq_enable();
                        if (thread_flags & _TIF_SIGPENDING) {
-                               do_signal(regs, syscall);
+                               if (unlikely(do_signal(regs, syscall))) {
+                                       /*
+                                        * Restart without handlers.
+                                        * Deal with it without leaving
+                                        * the kernel space.
+                                        */
+                                       return 1;
+                               }
                                syscall = 0;
                        } else {
                                clear_thread_flag(TIF_NOTIFY_RESUME);
@@ -660,4 +667,5 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
                local_irq_disable();
                thread_flags = current_thread_info()->flags;
        } while (thread_flags & _TIF_WORK_MASK);
+       return 0;
 }
This page took 0.049208 seconds and 5 git commands to generate.