summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2008-03-27 17:42:50 -0700
committerDavid S. Miller <davem@davemloft.net>2008-03-27 17:42:50 -0700
commit50fd4407b8bfbde7c1a0bfe4f24de7df37164342 (patch)
tree57a9f05b130903fb2c6c12412352928bb24dc3a7
parent8eeee8b152ae6bbe181518efaf62ba8e9c613693 (diff)
downloadlinux-50fd4407b8bfbde7c1a0bfe4f24de7df37164342.tar.gz
[NET]: Use local_irq_{save,restore}() in napi_complete().
Based upon a lockdep report.

Since ->poll() can be invoked from netpoll with interrupts
disabled, we must not unconditionally enable interrupts
in napi_complete().

Instead we must use local_irq_{save,restore}().

Noticed by Peter Zijlstra:

<irqs disabled>

  netpoll_poll()
    poll_napi()
      spin_trylock(&napi->poll_lock)
      poll_one_napi()
        napi->poll() := sky2_poll()
          napi_complete()
            local_irq_disable()
            local_irq_enable() <--- *BUG*

  <irq>
    irq_exit()
      do_softirq()
        net_rx_action()
          spin_lock(&napi->poll_lock) <--- Deadlock!

Because we still hold the lock....

Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/netdevice.h6
1 files changed, 4 insertions, 2 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index a2f003239c85..fae6a7ececdb 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -383,9 +383,11 @@ static inline void __napi_complete(struct napi_struct *n)
 
 static inline void napi_complete(struct napi_struct *n)
 {
-	local_irq_disable();
+	unsigned long flags;
+
+	local_irq_save(flags);
 	__napi_complete(n);
-	local_irq_enable();
+	local_irq_restore(flags);
 }
 
 /**