• Nam Cao's avatar
    irqchip/sifive-plic: Unmask interrupt in plic_irq_enable() · 6b1e0651
    Nam Cao authored
    It is possible that an interrupt is disabled and masked at the same time.
    When the interrupt is enabled again by enable_irq(), only plic_irq_enable()
    is called, not plic_irq_unmask(). The interrupt remains masked and never
    raises.
    
    An example where interrupt is both disabled and masked is when
    handle_fasteoi_irq() is the handler, and IRQS_ONESHOT is set. The interrupt
    handler:
    
      1. Mask the interrupt
      2. Handle the interrupt
      3. Check if interrupt is still enabled, and unmask it (see
         cond_unmask_eoi_irq())
    
    If another task disables the interrupt in the middle of the above steps,
    the interrupt will not get unmasked, and will remain masked when it is
    enabled in the future.
    
    The problem is occasionally observed when PREEMPT_RT is enabled, because
    PREEMPT_RT adds the IRQS_ONESHOT flag. But PREEMPT_RT only makes the problem
    more likely to appear, the bug has been around since commit a1706a1c
    ("irqchip/sifive-plic: Separate the enable and mask operations").
    
    Fix it by unmasking interrupt in plic_irq_enable().
    
    Fixes: a1706a1c ("irqchip/sifive-plic: Separate the enable and mask operations")
    Signed-off-by: default avatarNam Cao <namcao@linutronix.de>
    Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
    Cc: stable@vger.kernel.org
    Link: https://lore.kernel.org/all/20241003084152.2422969-1-namcao@linutronix.de
    6b1e0651
irq-sifive-plic.c 18.5 KB