From 338c15e470d818f215d651505dc169d4e92f36a4 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Wed, 22 Sep 2010 18:22:42 +0000 Subject: e1000: fix occasional panic on unload Net drivers in general have an issue where timers fired by mod_timer or work threads with schedule_work are running outside of the rtnl_lock. With no other lock protection these routines are vulnerable to races with driver unload or reset paths. The longer term solution to this might be a redesign with safer locks being taken in the driver to guarantee no reentrance, but for now a safe and effective fix is to take the rtnl_lock in these routines. Signed-off-by: Jesse Brandeburg Tested-by: Jeff Pieper Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/e1000/e1000_main.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 5b4c6c061414..c88439deb7d8 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -521,8 +521,21 @@ void e1000_down(struct e1000_adapter *adapter) e1000_clean_all_rx_rings(adapter); } +void e1000_reinit_safe(struct e1000_adapter *adapter) +{ + while (test_and_set_bit(__E1000_RESETTING, &adapter->flags)) + msleep(1); + rtnl_lock(); + e1000_down(adapter); + e1000_up(adapter); + rtnl_unlock(); + clear_bit(__E1000_RESETTING, &adapter->flags); +} + void e1000_reinit_locked(struct e1000_adapter *adapter) { + /* if rtnl_lock is not held the call path is bogus */ + ASSERT_RTNL(); WARN_ON(in_interrupt()); while (test_and_set_bit(__E1000_RESETTING, &adapter->flags)) msleep(1); @@ -2247,7 +2260,10 @@ static void e1000_update_phy_info_task(struct work_struct *work) struct e1000_adapter, phy_info_task); struct e1000_hw *hw = &adapter->hw; + + rtnl_lock(); e1000_phy_get_info(hw, &adapter->phy_info); + rtnl_unlock(); } /** @@ -2273,6 +2289,7 @@ static void e1000_82547_tx_fifo_stall_task(struct work_struct *work) struct net_device *netdev = adapter->netdev; u32 tctl; + rtnl_lock(); if (atomic_read(&adapter->tx_fifo_stall)) { if ((er32(TDT) == er32(TDH)) && (er32(TDFT) == er32(TDFH)) && @@ -2293,6 +2310,7 @@ static void e1000_82547_tx_fifo_stall_task(struct work_struct *work) mod_timer(&adapter->tx_fifo_stall_timer, jiffies + 1); } } + rtnl_unlock(); } bool e1000_has_link(struct e1000_adapter *adapter) @@ -3160,7 +3178,7 @@ static void e1000_reset_task(struct work_struct *work) struct e1000_adapter *adapter = container_of(work, struct e1000_adapter, reset_task); - e1000_reinit_locked(adapter); + e1000_reinit_safe(adapter); } /** -- cgit v1.2.3-58-ga151