[PATCH] ARM: entry: fix unwinder problems caused by IRQ stacks

Ard Biesheuvel ardb at kernel.org
Thu Mar 10 08:24:45 PST 2022


The IRQ stacks series made some changes to the unwinder, to permit
unwinding across different stacks. This is needed because otherwise, the
call stack would terminate at the point where the stack switch between
the task stack and the IRQ stack occurs, which would defeat any
diagnostics that rely on timer interrupts, such as RCU stall detection.

Unfortunately, getting the unwind annotations correct turns out to be
difficult, given that this now involves a frame pointer which needs to
point into the right location in the task stack when unwinding from the
IRQ stack. Getting this wrong for an exception handling routine results
in the stack pointer to be unwound from the wrong location, causing any
subsequent unwind attempts to cause all kinds of issues, as reported by
Naresh here [0].

So let's simplify this, by deferring the stack switch to
call_with_stack(), which already has the correct unwind annotations, and
removing all the complicated handling of the stack frame from the IRQ
exception entrypoint itself.

[0] https://lore.kernel.org/all/CA+G9fYtpy8VgK+ag6OsA9TDrwi5YGU4hu7GM8xwpO7v6LrCD4Q@mail.gmail.com/

Signed-off-by: Ard Biesheuvel <ardb at kernel.org>
---
 arch/arm/kernel/entry-armv.S | 57 +++++++++---------------------------
 1 file changed, 14 insertions(+), 43 deletions(-)

diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 5609ca8ae46a..0ce9035e3ead 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -33,58 +33,29 @@
  * Interrupt handling.
  */
 	.macro	irq_handler, from_user:req
-	mov	r0, sp
-#ifdef CONFIG_IRQSTACKS
-#ifdef CONFIG_UNWINDER_ARM
-	mov	fpreg, sp		@ Preserve original SP
-#else
-	mov	r7, fp			@ Preserve original FP
-	mov	r8, sp			@ Preserve original SP
-#endif
-	ldr_this_cpu sp, irq_stack_ptr, r2, r3
+	mov	r1, sp
+	ldr_this_cpu r2, irq_stack_ptr, r2, r3
 	.if	\from_user == 0
-UNWIND(	.setfp	fpreg, sp		)
 	@
 	@ If we took the interrupt while running in the kernel, we may already
 	@ be using the IRQ stack, so revert to the original value in that case.
 	@
-	subs	r2, sp, r0		@ SP above bottom of IRQ stack?
-	rsbscs	r2, r2, #THREAD_SIZE	@ ... and below the top?
+	subs	r3, r2, r1		@ SP above bottom of IRQ stack?
+	rsbscs	r3, r3, #THREAD_SIZE	@ ... and below the top?
 #ifdef CONFIG_VMAP_STACK
-	ldr_va	r2, high_memory, cc	@ End of the linear region
-	cmpcc	r2, r0			@ Stack pointer was below it?
+	ldr_va	r3, high_memory, cc	@ End of the linear region
+	cmpcc	r3, r1			@ Stack pointer was below it?
 #endif
-	movcs	sp, r0			@ If so, revert to incoming SP
-
-#ifndef CONFIG_UNWINDER_ARM
-	@
-	@ Inform the frame pointer unwinder where the next frame lives
-	@
-	movcc	lr, pc			@ Make LR point into .entry.text so
-					@ that we will get a dump of the
-					@ exception stack for this frame.
-#ifdef CONFIG_CC_IS_GCC
-	movcc	ip, r0			@ Store the old SP in the frame record.
-	stmdbcc	sp!, {fp, ip, lr, pc}	@ Push frame record
-	addcc	fp, sp, #12
-#else
-	stmdbcc	sp!, {fp, lr}		@ Push frame record
-	movcc	fp, sp
-#endif // CONFIG_CC_IS_GCC
-#endif // CONFIG_UNWINDER_ARM
-	.endif
-#endif // CONFIG_IRQSTACKS
-
+	bcc	0f			@ If not, switch to the IRQ stack
+	mov	r0, r1
 	bl	generic_handle_arch_irq
+	b	1f
+0:
+	.endif
 
-#ifdef CONFIG_IRQSTACKS
-#ifdef CONFIG_UNWINDER_ARM
-	mov	sp, fpreg		@ Restore original SP
-#else
-	mov	fp, r7			@ Restore original FP
-	mov	sp, r8			@ Restore original SP
-#endif // CONFIG_UNWINDER_ARM
-#endif // CONFIG_IRQSTACKS
+	mov_l	r0, generic_handle_arch_irq
+	bl	call_with_stack
+1:
 	.endm
 
 	.macro	pabt_helper
-- 
2.30.2




More information about the linux-arm-kernel mailing list