Skip to content
  • Ingo Molnar's avatar
    [PATCH] lock validator: fix ns83820.c irq-flags bug · 3a10cceb
    Ingo Molnar authored
    
    
    Barry K. Nathan reported the following lockdep warning:
    
    [  197.343948] BUG: warning at kernel/lockdep.c:1856/trace_hardirqs_on()
    [  197.345928]  [<c010329b>] show_trace_log_lvl+0x5b/0x105
    [  197.346359]  [<c0103896>] show_trace+0x1b/0x20
    [  197.346759]  [<c01038ed>] dump_stack+0x1f/0x24
    [  197.347159]  [<c012efa2>] trace_hardirqs_on+0xfb/0x185
    [  197.348873]  [<c029b009>] _spin_unlock_irq+0x24/0x2d
    [  197.350620]  [<e09034e8>] do_tx_done+0x171/0x179 [ns83820]
    [  197.350895]  [<e090445c>] ns83820_irq+0x149/0x20b [ns83820]
    [  197.351166]  [<c013b4b8>] handle_IRQ_event+0x1d/0x52
    [  197.353216]  [<c013c6c2>] handle_level_irq+0x97/0xe1
    [  197.355157]  [<c01048c3>] do_IRQ+0x8b/0xac
    [  197.355612]  [<c0102d9d>] common_interrupt+0x25/0x2c
    
    this is caused because the ns83820 driver re-enables irq flags
    in hardirq context.
    
    While legal in theory, in practice it should only be done if the
    hardware is really old and has some very high overhead in its ISR.
    (such as PIO IDE)
    
    For modern hardware, running ISRs with irqs enabled is discouraged,
    because 1) new hardware is fast enough to not cause latency problems
    2) allowing the nesting of hardware interrupts only 'spreads out'
    the handling of the current ISR, causing extra cachemisses that would
    otherwise not happen. Furthermore, on architectures where ISRs share
    the kernel stacks, enabling interrupts in ISRs introduces a much
    higher kernel-stack-nesting and thus kernel-stack-overflow risk.
    3) not managing irq-flags via the _irqsave / _irqrestore variants
    is dangerous: it's easy to forget whether one function nests inside
    another, and irq flags might be mismanaged.
    
    In the few cases where re-enabling interrupts in an ISR is considered
    useful (and unavoidable), it has to be taught to the lock validator
    explicitly (because the lock validator needs the "no ISR ever enables
    hardirqs" artificial simplification to keep the IRQ/softirq locking
    dependencies manageable).
    
    This teaching is done via the explicit use local_irq_enable_in_hardirq().
    On a stock kernel this maps to local_irq_enable(). If the lock validator
    is enabled then this does not enable interrupts.
    
    Now, the analysis of drivers/net/ns83820.c's irq flags use: the
    irq-enabling in irq context seems intentional, but i dont think it's
    justified. Furthermore, the driver suffers from problem #3 above too,
    in ns83820_tx_timeout() it disables irqs via local_irq_save(), but
    then it calls do_tx_done() which does a spin_unlock_irq(),
    re-enabling for a function that does not expect it! While currently
    this bug seems harmless (only some debug printout seems to be
    affected by it), it's nevertheless something to be fixed.
    
    So this patch makes the ns83820 ISR irq-flags-safe, and cleans up
    do_tx_done() use and locking to avoid the ns83820_tx_timeout() bug.
    
    From: Arjan van de Ven <arjan@linux.intel.com>
    
      ns83820_mib_isr takes the misc_lock in IRQ context.  All other places that
      do this in the ISR already use _irqsave versions, make this consistent at
      least.  At some point in the future someone should audit the driver to see
      if all _irqsave's in the ISR can go away, this is generally an iffy/fragile
      proposition though; for now get it safe, simple and consistent.
    
    From: Arjan van de Ven <arjan@linux.intel.com>
    
    ok this is a real driver deadlock:
    
    The ns83820 driver enabled interrupts (by unlocking the misc_lock with
    _irq) while still holding the rx_info.lock, which is required to be irq
    safe since it's used in the ISR like this:
                    writel(1, dev->base + IER);
                    spin_unlock_irq(&dev->misc_lock);
                    kick_rx(ndev);
                    spin_unlock_irq(&dev->rx_info.lock);
    
    This is can cause a deadlock if an irq was pending at the first
    spin_unlock_irq already, or if one would hit during kick_rx().
    Simply remove the first _irq solves this
    
    Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
    Cc: Benjamin LaHaise <bcrl@kvack.org>
    Cc: Jeff Garzik <jeff@garzik.org>
    Signed-off-by: default avatarArjan van de Ven <arjan@linux.intel.com>
    Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
    Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
    3a10cceb