aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/process.c
diff options
context:
space:
mode:
authorMatt Redfearn <matt.redfearn@imgtec.com>2017-03-21 10:52:25 -0400
committerRalf Baechle <ralf@linux-mips.org>2017-03-22 06:53:57 -0400
commitdb8466c581cca1a08b505f1319c3ecd246f16fa8 (patch)
tree00a7a77ab90df244dc16dcd32582985ca6a846d6 /arch/mips/kernel/process.c
parent0be032c190abcdcfa948082b6a1e0d461184ba4d (diff)
MIPS: IRQ Stack: Unwind IRQ stack onto task stack
When the separate IRQ stack was introduced, stack unwinding only proceeded as far as the top of the IRQ stack, leading to kernel backtraces being less useful, lacking the trace of what was interrupted. Fix this by providing a means for the kernel to unwind the IRQ stack onto the interrupted task stack. The processor state is saved to the kernel task stack on interrupt. The IRQ_STACK_START macro reserves an unsigned long at the top of the IRQ stack where the interrupted task stack pointer can be saved. After the active stack is switched to the IRQ stack, save the interrupted tasks stack pointer to the reserved location. Fix the stack unwinding code to look for the frame being the top of the IRQ stack and if so get the next frame from the saved location. The existing test does not work with the separate stack since the ra is no longer pointed at ret_from_{irq,exception}. The test to stop unwinding the stack 32 bytes from the top of a stack must be modified to allow unwinding to continue up to the location of the saved task stack pointer when on the IRQ stack. The low / high marks of the stack are set depending on whether the sp is on an irq stack or not. Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Marcin Nowakowski <marcin.nowakowski@imgtec.com> Cc: Masanari Iida <standby24x7@gmail.com> Cc: Chris Metcalf <cmetcalf@mellanox.com> Cc: James Hogan <james.hogan@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Jason A. Donenfeld <jason@zx2c4.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/15788/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/kernel/process.c')
-rw-r--r--arch/mips/kernel/process.c56
1 files changed, 38 insertions, 18 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index fb6b6b650719..b68e10fc453d 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -488,31 +488,52 @@ unsigned long notrace unwind_stack_by_address(unsigned long stack_page,
488 unsigned long pc, 488 unsigned long pc,
489 unsigned long *ra) 489 unsigned long *ra)
490{ 490{
491 unsigned long low, high, irq_stack_high;
491 struct mips_frame_info info; 492 struct mips_frame_info info;
492 unsigned long size, ofs; 493 unsigned long size, ofs;
494 struct pt_regs *regs;
493 int leaf; 495 int leaf;
494 extern void ret_from_irq(void);
495 extern void ret_from_exception(void);
496 496
497 if (!stack_page) 497 if (!stack_page)
498 return 0; 498 return 0;
499 499
500 /* 500 /*
501 * If we reached the bottom of interrupt context, 501 * IRQ stacks start at IRQ_STACK_START
502 * return saved pc in pt_regs. 502 * task stacks at THREAD_SIZE - 32
503 */ 503 */
504 if (pc == (unsigned long)ret_from_irq || 504 low = stack_page;
505 pc == (unsigned long)ret_from_exception) { 505 if (!preemptible() && on_irq_stack(raw_smp_processor_id(), *sp)) {
506 struct pt_regs *regs; 506 high = stack_page + IRQ_STACK_START;
507 if (*sp >= stack_page && 507 irq_stack_high = high;
508 *sp + sizeof(*regs) <= stack_page + THREAD_SIZE - 32) { 508 } else {
509 regs = (struct pt_regs *)*sp; 509 high = stack_page + THREAD_SIZE - 32;
510 pc = regs->cp0_epc; 510 irq_stack_high = 0;
511 if (!user_mode(regs) && __kernel_text_address(pc)) { 511 }
512 *sp = regs->regs[29]; 512
513 *ra = regs->regs[31]; 513 /*
514 return pc; 514 * If we reached the top of the interrupt stack, start unwinding
515 } 515 * the interrupted task stack.
516 */
517 if (unlikely(*sp == irq_stack_high)) {
518 unsigned long task_sp = *(unsigned long *)*sp;
519
520 /*
521 * Check that the pointer saved in the IRQ stack head points to
522 * something within the stack of the current task
523 */
524 if (!object_is_on_stack((void *)task_sp))
525 return 0;
526
527 /*
528 * Follow pointer to tasks kernel stack frame where interrupted
529 * state was saved.
530 */
531 regs = (struct pt_regs *)task_sp;
532 pc = regs->cp0_epc;
533 if (!user_mode(regs) && __kernel_text_address(pc)) {
534 *sp = regs->regs[29];
535 *ra = regs->regs[31];
536 return pc;
516 } 537 }
517 return 0; 538 return 0;
518 } 539 }
@@ -533,8 +554,7 @@ unsigned long notrace unwind_stack_by_address(unsigned long stack_page,
533 if (leaf < 0) 554 if (leaf < 0)
534 return 0; 555 return 0;
535 556
536 if (*sp < stack_page || 557 if (*sp < low || *sp + info.frame_size > high)
537 *sp + info.frame_size > stack_page + THREAD_SIZE - 32)
538 return 0; 558 return 0;
539 559
540 if (leaf) 560 if (leaf)