Commit 44c445c3 authored by Vincenzo Maffione's avatar Vincenzo Maffione Committed by Jeff Kirsher

e1000: fix race condition between e1000_down() and e1000_watchdog

This patch fixes a race condition that can result into the interface being
up and carrier on, but with transmits disabled in the hardware.
The bug may show up by repeatedly IFF_DOWN+IFF_UP the interface, which
allows e1000_watchdog() interleave with e1000_down().

    CPU x                           CPU y
    --------------------------------------------------------------------
    e1000_down():
        netif_carrier_off()
                                    e1000_watchdog():
                                        if (carrier == off) {
                                            netif_carrier_on();
                                            enable_hw_transmit();
                                        }
        disable_hw_transmit();
                                    e1000_watchdog():
                                        /* carrier on, do nothing */
Signed-off-by: default avatarVincenzo Maffione <v.maffione@gmail.com>
Tested-by: default avatarAaron Brown <aaron.f.brown@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 78e0ea67
...@@ -520,8 +520,6 @@ void e1000_down(struct e1000_adapter *adapter) ...@@ -520,8 +520,6 @@ void e1000_down(struct e1000_adapter *adapter)
struct net_device *netdev = adapter->netdev; struct net_device *netdev = adapter->netdev;
u32 rctl, tctl; u32 rctl, tctl;
netif_carrier_off(netdev);
/* disable receives in the hardware */ /* disable receives in the hardware */
rctl = er32(RCTL); rctl = er32(RCTL);
ew32(RCTL, rctl & ~E1000_RCTL_EN); ew32(RCTL, rctl & ~E1000_RCTL_EN);
...@@ -537,6 +535,15 @@ void e1000_down(struct e1000_adapter *adapter) ...@@ -537,6 +535,15 @@ void e1000_down(struct e1000_adapter *adapter)
E1000_WRITE_FLUSH(); E1000_WRITE_FLUSH();
msleep(10); msleep(10);
/* Set the carrier off after transmits have been disabled in the
* hardware, to avoid race conditions with e1000_watchdog() (which
* may be running concurrently to us, checking for the carrier
* bit to decide whether it should enable transmits again). Such
* a race condition would result into transmission being disabled
* in the hardware until the next IFF_DOWN+IFF_UP cycle.
*/
netif_carrier_off(netdev);
napi_disable(&adapter->napi); napi_disable(&adapter->napi);
e1000_irq_disable(adapter); e1000_irq_disable(adapter);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment