• Nicholas Piggin's avatar
    powerpc/64s: prevent recursive replay_soft_interrupts causing superfluous interrupt · 4025c784
    Nicholas Piggin authored
    When an asynchronous interrupt calls irq_exit, it checks for softirqs
    that may have been created, and runs them. Running softirqs enables
    local irqs, which can replay pending interrupts causing recursion in
    replay_soft_interrupts. This abridged trace shows how this can occur:
    
    ! NIP replay_soft_interrupts
      LR  interrupt_exit_kernel_prepare
      Call Trace:
        interrupt_exit_kernel_prepare (unreliable)
        interrupt_return
      --- interrupt: ea0 at __rb_reserve_next
      NIP __rb_reserve_next
      LR __rb_reserve_next
      Call Trace:
        ring_buffer_lock_reserve
        trace_function
        function_trace_call
        ftrace_call
        __do_softirq
        irq_exit
        timer_interrupt
    !   replay_soft_interrupts
        interrupt_exit_kernel_prepare
        interrupt_return
      --- interrupt: ea0 at arch_local_irq_restore
    
    This can not be prevented easily, because softirqs must not block hard
    irqs, so it has to be dealt with.
    
    The recursion is bounded by design in the softirq code because softirq
    replay disables softirqs and loops around again to check for new
    softirqs created while it ran, so that's not a problem.
    
    However it does mess up interrupt replay state, causing superfluous
    interrupts when the second replay_soft_interrupts clears a pending
    interrupt, leaving it still set in the first call in the 'happened'
    local variable.
    
    Fix this by not caching a copy of irqs_happened across interrupt
    handler calls.
    
    Fixes: 3282a3da ("powerpc/64: Implement soft interrupt replay in C")
    Signed-off-by: default avatarNicholas Piggin <npiggin@gmail.com>
    Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
    Link: https://lore.kernel.org/r/20210123061244.2076145-1-npiggin@gmail.com
    4025c784
irq.c 20.6 KB