Commit a4cb3651a1743 ("powerpc/64s/interrupt: Fix lost interrupts when
returning to soft-masked context") fixed the problem of pending irqs
pending cleared when clearing the HARD_DIS bit, but then it didn't clear
the bit at all. This change clears HARD_DIS without affecting other bits
in the mask.

When an interrupt hits in a soft-masked section that has MSR[EE]=1, it
can hard disable and set PACA_IRQS_HARD_DIS,which  must be cleared
when returning to the EE=1 caller (unless it was set due to a
MUST_HARD_MASK interrupt becoming pending). Failure to clear this leaves
the returned-to context running with MSR[EE]=1 and PACA_IRQS_HARD_DIS,
which confuses irq assertions and could be dangerous for code that might
test the flag.

This was observed in a hash MMU kernel where a kernel hash fault hits in
a local_irqs_disabled region that has EE=1. The hash fault also runs
with EE=1, then as it returns, a decrementer hits in the restart section
and the irq restart code hard-masks which sets the PACA_IRQ_HARD_DIS
flag, which is not clear when the original context is returned to.

Reported-by: Sachin Sant <sach...@linux.ibm.com>
Fixes: a4cb3651a1743 ("powerpc/64s/interrupt: Fix lost interrupts when 
returning to soft-masked context")
Signed-off-by: Nicholas Piggin <npig...@gmail.com>
---
 arch/powerpc/kernel/interrupt_64.S | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/kernel/interrupt_64.S 
b/arch/powerpc/kernel/interrupt_64.S
index 978a173eb339..a019ed6fc839 100644
--- a/arch/powerpc/kernel/interrupt_64.S
+++ b/arch/powerpc/kernel/interrupt_64.S
@@ -532,15 +532,24 @@ _ASM_NOKPROBE_SYMBOL(interrupt_return_\srr\()_kernel)
         * Returning to soft-disabled context.
         * Check if a MUST_HARD_MASK interrupt has become pending, in which
         * case we need to disable MSR[EE] in the return context.
+        *
+        * The MSR[EE] check catches among other things the short incoherency
+        * in hard_irq_disable() between clearing MSR[EE] and setting
+        * PACA_IRQ_HARD_DIS.
         */
        ld      r12,_MSR(r1)
        andi.   r10,r12,MSR_EE
        beq     .Lfast_kernel_interrupt_return_\srr\() // EE already disabled
        lbz     r11,PACAIRQHAPPENED(r13)
        andi.   r10,r11,PACA_IRQ_MUST_HARD_MASK
-       beq     .Lfast_kernel_interrupt_return_\srr\() // No HARD_MASK pending
+       bne     1f // HARD_MASK is pending
+       // No HARD_MASK pending, clear possible HARD_DIS set by interrupt
+       andi.   r11,r11,(~PACA_IRQ_HARD_DIS)@l
+       stb     r11,PACAIRQHAPPENED(r13)
+       b       .Lfast_kernel_interrupt_return_\srr\()
+
 
-       /* Must clear MSR_EE from _MSR */
+1:     /* Must clear MSR_EE from _MSR */
 #ifdef CONFIG_PPC_BOOK3S
        li      r10,0
        /* Clear valid before changing _MSR */
-- 
2.37.2

Reply via email to