CRISv32: handle multiple signals
authorRabin Vincent <rabin@rab.in>
Sun, 8 Feb 2015 17:19:17 +0000 (18:19 +0100)
committerJesper Nilsson <jespern@axis.com>
Wed, 25 Mar 2015 09:53:54 +0000 (10:53 +0100)
Al Viro noted that CRIS fails to handle multiple signals.

This fixes the problem for CRISv32 by making it use a C work_pending
handling loop similar to the ARM implementation in 0a267fa6a15d41c
("ARM: 7472/1: pull all work_pending logics into C function").

This also happens to fixes the warnings which currently trigger on
CRISv32 due to do_signal() being called with interrupts disabled.

Test case (should die of the SIGSEGV which gets raised when setting up
the stack for SIGALRM, but instead reaches and executes the _exit(1)):

  #include <unistd.h>
  #include <signal.h>
  #include <sys/time.h>
  #include <err.h>

  static void handler(int sig) { }

  int main(int argc, char *argv[])
  {
   int ret;
   struct itimerval t1 = { .it_value = {1} };
   stack_t ss = {
   .ss_sp = NULL,
   .ss_size = SIGSTKSZ,
   };
   struct sigaction action = {
   .sa_handler = handler,
   .sa_flags = SA_ONSTACK,
   };

   ret = sigaltstack(&ss, NULL);
   if (ret < 0)
   err(1, "sigaltstack");

   sigaction(SIGALRM, &action, NULL);
    setitimer(ITIMER_REAL, &t1, NULL);

   pause();

   _exit(1);

   return 0;
  }

Reported-by: Al Viro <viro@ZenIV.linux.org.uk>
Link: http://lkml.kernel.org/r/20121208074429.GC4939@ZenIV.linux.org.uk
Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: Jesper Nilsson <jespern@axis.com>
arch/cris/arch-v32/kernel/entry.S
arch/cris/kernel/ptrace.c

index 1ea29b7..026a0b2 100644 (file)
@@ -280,44 +280,15 @@ _syscall_exit_work:
 
        .type   _work_pending,@function
 _work_pending:
-       addoq   +TI_flags, $r0, $acr
-       move.d  [$acr], $r10
-       btstq   TIF_NEED_RESCHED, $r10  ; Need resched?
-       bpl     _work_notifysig         ; No, must be signal/notify.
-       nop
-       .size   _work_pending, . - _work_pending
-
-       .type   _work_resched,@function
-_work_resched:
-       move.d  $r9, $r1                ; Preserve R9.
-       jsr     schedule
-       nop
-       move.d  $r1, $r9
-       di
-
-       addoq   +TI_flags, $r0, $acr
-       move.d  [$acr], $r1
-       and.d   _TIF_WORK_MASK, $r1     ; Ignore sycall trace counter.
-       beq     _Rexit
-       nop
-       btstq   TIF_NEED_RESCHED, $r1
-       bmi     _work_resched           ; current->work.need_resched.
-       nop
-       .size   _work_resched, . - _work_resched
-
-       .type   _work_notifysig,@function
-_work_notifysig:
-       ;; Deal with pending signals and notify-resume requests.
-
        addoq   +TI_flags, $r0, $acr
        move.d  [$acr], $r12            ; The thread_info_flags parameter.
        move.d  $sp, $r11               ; The regs param.
-       jsr     do_notify_resume
-       move.d  $r9, $r10               ; do_notify_resume syscall/irq param.
+       jsr     do_work_pending
+       move.d  $r9, $r10               ; The syscall/irq param.
 
        ba _Rexit
        nop
-       .size   _work_notifysig, . - _work_notifysig
+       .size   _work_pending, . - _work_pending
 
        ;; We get here as a sidetrack when we've entered a syscall with the
        ;; trace-bit set. We need to call do_syscall_trace and then continue
index 58d44ee..fd3427e 100644 (file)
@@ -42,3 +42,26 @@ void do_notify_resume(int canrestart, struct pt_regs *regs,
                tracehook_notify_resume(regs);
        }
 }
+
+void do_work_pending(int syscall, struct pt_regs *regs,
+                    unsigned int thread_flags)
+{
+       do {
+               if (likely(thread_flags & _TIF_NEED_RESCHED)) {
+                       schedule();
+               } else {
+                       if (unlikely(!user_mode(regs)))
+                               return;
+                       local_irq_enable();
+                       if (thread_flags & _TIF_SIGPENDING) {
+                               do_signal(syscall, regs);
+                               syscall = 0;
+                       } else {
+                               clear_thread_flag(TIF_NOTIFY_RESUME);
+                               tracehook_notify_resume(regs);
+                       }
+               }
+               local_irq_disable();
+               thread_flags = current_thread_info()->flags;
+       } while (thread_flags & _TIF_WORK_MASK);
+}