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 <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Rabin Vincent <[email protected]>
---
 arch/cris/arch-v32/kernel/entry.S | 35 +++--------------------------------
 arch/cris/kernel/ptrace.c         | 23 +++++++++++++++++++++++
 2 files changed, 26 insertions(+), 32 deletions(-)

diff --git a/arch/cris/arch-v32/kernel/entry.S 
b/arch/cris/arch-v32/kernel/entry.S
index 1ea29b7..026a0b2 100644
--- a/arch/cris/arch-v32/kernel/entry.S
+++ b/arch/cris/arch-v32/kernel/entry.S
@@ -281,43 +281,14 @@ _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
diff --git a/arch/cris/kernel/ptrace.c b/arch/cris/kernel/ptrace.c
index 58d44ee..fd3427e 100644
--- a/arch/cris/kernel/ptrace.c
+++ b/arch/cris/kernel/ptrace.c
@@ -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);
+}
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to