[PATCH 1/4] sparc32: keep PIL raised until the IRQ frame is gone

From: Ramin Moussavi

Date: Thu Oct 15 2026 - 05:01:51 EST


The per-cpu timer interrupts on sun4m and sun4d SMP, and the LEON SMP
IPI, leave their handler with

wr %l0, PSR_ET, %psr
WRITE_PAUSE
RESTORE_ALL

%l0 is the PSR at trap time, so this turns traps back on at the PIL of
the interrupted code, usually 0, while the trap frame is still on the
stack. Any interrupt that is pending at that point is taken on top of
the frame, and if it leaves through the same path it opens the same
window one level deeper. A source that keeps firing makes the nesting
unbounded: the kernel stack runs into its thread_info, w_saved and the
task pointer get overwritten, and the next window trap stores through
them with traps disabled. The CPU goes to error state.

The generic path does not have this problem. real_irq_continue keeps
PIL at 15 after handler_irq and lets ret_trap_entry restore the old PIL
together with the rett. It was fixed that way in Linux 2.1.129
(entry.S 1.153, November 1998):

- wr %l0, PSR_ET, %psr
+ or %l0, PSR_PIL, %g2 ! restore PIL after handler_irq
+ wr %g2, PSR_ET, %psr ! keep ET up

smp_ticker had been added in 2.1.36 with a copy of the old exit, and
kept it through the rename to smp4m_ticker and the addition of
smp4d_ticker in 2.1.97. smpleon_ipi copied the pattern in 2011. Do the
same as real_irq_continue in all three.

On hardware the window is a few instructions long and no interrupt
source refills it that fast. qemu does: its ESCC finishes a transmit
instantly, and since 8.2 it takes a pending interrupt right after rett
(930f1865cc65 "target/sparc: Clear may_lookup for npc == DYNAMIC_PC").
A burst of console output on an SS-20 with -smp 4 is enough. A trap
ring buffer in trap_setup showed the level 12 serial interrupt taken
over and over at smp4m_ticker+0x34, timer ticks nesting further down,
and the frames sinking below the thread_info.

Tested on qemu 11.1.0, SS-20, -smp 4, with a test that writes a few
hundred lines to the serial console from a multithreaded process: the
kernel went to error state in 4 of 4 boots before, and booted cleanly
8 of 8 times after. Two full runs of the uClibc-ng test suite on top
then completed without a crash. LEON SMP was tested on qemu's
leon3_generic with -smp 4, where a full run of the suite completed
without a crash as well. sun4d gets the same one line change but was
not run; qemu has no sun4d machine.

Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramin Moussavi <ramin.moussavi@xxxxxxxxx>
---
arch/sparc/kernel/entry.S | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
index ea51ef52c952..87d03a58446e 100644
--- a/arch/sparc/kernel/entry.S
+++ b/arch/sparc/kernel/entry.S
@@ -252,7 +252,8 @@ smp4m_ticker:
WRITE_PAUSE
call smp4m_percpu_timer_interrupt
add %sp, STACKFRAME_SZ, %o0
- wr %l0, PSR_ET, %psr
+ or %l0, PSR_PIL, %g2 ! keep PIL up until the frame is gone
+ wr %g2, PSR_ET, %psr
WRITE_PAUSE
RESTORE_ALL

@@ -353,7 +354,8 @@ smp4d_ticker:
WRITE_PAUSE
call smp4d_percpu_timer_interrupt
add %sp, STACKFRAME_SZ, %o0
- wr %l0, PSR_ET, %psr
+ or %l0, PSR_PIL, %g2 ! keep PIL up until the frame is gone
+ wr %g2, PSR_ET, %psr
WRITE_PAUSE
RESTORE_ALL

@@ -407,7 +409,8 @@ smpleon_ipi:
WRITE_PAUSE
call leonsmp_ipi_interrupt
add %sp, STACKFRAME_SZ, %o1 ! pt_regs
- wr %l0, PSR_ET, %psr
+ or %l0, PSR_PIL, %g2 ! keep PIL up until the frame is gone
+ wr %g2, PSR_ET, %psr
WRITE_PAUSE
RESTORE_ALL


base-commit: d419d682257d813f6cb7a28e42b35d0edf4ad038
--
2.53.0