Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/64s/interrupt: Fix lost interrupts when returning to soft-masked context

It's possible for an interrupt returning to an irqs-disabled context to
lose a pending soft-masked irq because it branches to part of the exit
code for irqs-enabled contexts, which is meant to clear only the
PACA_IRQS_HARD_DIS flag from PACAIRQHAPPENED by zeroing the byte. This
just looks like a simple thinko from a recent commit (if there was no
hard mask pending, there would be no reason to clear it anyway).

This also adds comment to the code that actually does need to clear the
flag.

Fixes: e485f6c751e0a ("powerpc/64/interrupt: Fix return to masked context after hard-mask irq becomes pending")
Reported-by: Sachin Sant <sachinp@linux.ibm.com>
Reported-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20221013064418.1311104-1-npiggin@gmail.com

authored by

Nicholas Piggin and committed by
Michael Ellerman
a4cb3651 e2375062

+13 -2
+13 -2
arch/powerpc/kernel/interrupt_64.S
··· 538 538 beq .Lfast_kernel_interrupt_return_\srr\() // EE already disabled 539 539 lbz r11,PACAIRQHAPPENED(r13) 540 540 andi. r10,r11,PACA_IRQ_MUST_HARD_MASK 541 - beq 1f // No HARD_MASK pending 541 + beq .Lfast_kernel_interrupt_return_\srr\() // No HARD_MASK pending 542 542 543 543 /* Must clear MSR_EE from _MSR */ 544 544 #ifdef CONFIG_PPC_BOOK3S ··· 555 555 b .Lfast_kernel_interrupt_return_\srr\() 556 556 557 557 .Linterrupt_return_\srr\()_soft_enabled: 558 + /* 559 + * In the soft-enabled case, need to double-check that we have no 560 + * pending interrupts that might have come in before we reached the 561 + * restart section of code, and restart the exit so those can be 562 + * handled. 563 + * 564 + * If there are none, it is be possible that the interrupt still 565 + * has PACA_IRQ_HARD_DIS set, which needs to be cleared for the 566 + * interrupted context. This clear will not clobber a new pending 567 + * interrupt coming in, because we're in the restart section, so 568 + * such would return to the restart location. 569 + */ 558 570 #ifdef CONFIG_PPC_BOOK3S 559 571 lbz r11,PACAIRQHAPPENED(r13) 560 572 andi. r11,r11,(~PACA_IRQ_HARD_DIS)@l 561 573 bne- interrupt_return_\srr\()_kernel_restart 562 574 #endif 563 - 1: 564 575 li r11,0 565 576 stb r11,PACAIRQHAPPENED(r13) // clear the possible HARD_DIS 566 577