diff options
author | Rabin Vincent <rabin@rab.in> | 2015-02-08 12:19:17 -0500 |
---|---|---|
committer | Jesper Nilsson <jespern@axis.com> | 2015-03-25 05:53:54 -0400 |
commit | 9a7449d3e975fe5c5ca12b7fea4f4bd69188a5f9 (patch) | |
tree | f0e601428db322438e1c14a054955340804106be /arch/cris | |
parent | 0f72e5c0df732658d5e9e3c556c9c6928034e291 (diff) |
CRISv32: handle multiple signals
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>
Diffstat (limited to 'arch/cris')
-rw-r--r-- | arch/cris/arch-v32/kernel/entry.S | 35 | ||||
-rw-r--r-- | 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 1ea29b7f263c..026a0b21b8f0 100644 --- a/arch/cris/arch-v32/kernel/entry.S +++ b/arch/cris/arch-v32/kernel/entry.S | |||
@@ -281,43 +281,14 @@ _syscall_exit_work: | |||
281 | .type _work_pending,@function | 281 | .type _work_pending,@function |
282 | _work_pending: | 282 | _work_pending: |
283 | addoq +TI_flags, $r0, $acr | 283 | addoq +TI_flags, $r0, $acr |
284 | move.d [$acr], $r10 | ||
285 | btstq TIF_NEED_RESCHED, $r10 ; Need resched? | ||
286 | bpl _work_notifysig ; No, must be signal/notify. | ||
287 | nop | ||
288 | .size _work_pending, . - _work_pending | ||
289 | |||
290 | .type _work_resched,@function | ||
291 | _work_resched: | ||
292 | move.d $r9, $r1 ; Preserve R9. | ||
293 | jsr schedule | ||
294 | nop | ||
295 | move.d $r1, $r9 | ||
296 | di | ||
297 | |||
298 | addoq +TI_flags, $r0, $acr | ||
299 | move.d [$acr], $r1 | ||
300 | and.d _TIF_WORK_MASK, $r1 ; Ignore sycall trace counter. | ||
301 | beq _Rexit | ||
302 | nop | ||
303 | btstq TIF_NEED_RESCHED, $r1 | ||
304 | bmi _work_resched ; current->work.need_resched. | ||
305 | nop | ||
306 | .size _work_resched, . - _work_resched | ||
307 | |||
308 | .type _work_notifysig,@function | ||
309 | _work_notifysig: | ||
310 | ;; Deal with pending signals and notify-resume requests. | ||
311 | |||
312 | addoq +TI_flags, $r0, $acr | ||
313 | move.d [$acr], $r12 ; The thread_info_flags parameter. | 284 | move.d [$acr], $r12 ; The thread_info_flags parameter. |
314 | move.d $sp, $r11 ; The regs param. | 285 | move.d $sp, $r11 ; The regs param. |
315 | jsr do_notify_resume | 286 | jsr do_work_pending |
316 | move.d $r9, $r10 ; do_notify_resume syscall/irq param. | 287 | move.d $r9, $r10 ; The syscall/irq param. |
317 | 288 | ||
318 | ba _Rexit | 289 | ba _Rexit |
319 | nop | 290 | nop |
320 | .size _work_notifysig, . - _work_notifysig | 291 | .size _work_pending, . - _work_pending |
321 | 292 | ||
322 | ;; We get here as a sidetrack when we've entered a syscall with the | 293 | ;; We get here as a sidetrack when we've entered a syscall with the |
323 | ;; trace-bit set. We need to call do_syscall_trace and then continue | 294 | ;; 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 58d44ee1a71f..fd3427e563c5 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, | |||
42 | tracehook_notify_resume(regs); | 42 | tracehook_notify_resume(regs); |
43 | } | 43 | } |
44 | } | 44 | } |
45 | |||
46 | void do_work_pending(int syscall, struct pt_regs *regs, | ||
47 | unsigned int thread_flags) | ||
48 | { | ||
49 | do { | ||
50 | if (likely(thread_flags & _TIF_NEED_RESCHED)) { | ||
51 | schedule(); | ||
52 | } else { | ||
53 | if (unlikely(!user_mode(regs))) | ||
54 | return; | ||
55 | local_irq_enable(); | ||
56 | if (thread_flags & _TIF_SIGPENDING) { | ||
57 | do_signal(syscall, regs); | ||
58 | syscall = 0; | ||
59 | } else { | ||
60 | clear_thread_flag(TIF_NOTIFY_RESUME); | ||
61 | tracehook_notify_resume(regs); | ||
62 | } | ||
63 | } | ||
64 | local_irq_disable(); | ||
65 | thread_flags = current_thread_info()->flags; | ||
66 | } while (thread_flags & _TIF_WORK_MASK); | ||
67 | } | ||