summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@linux-foundation.org>2007-02-16 15:37:39 -0800
committerJeff Garzik <jeff@garzik.org>2007-02-20 11:18:13 -0500
commit208491d8f92e5aa129acb27e223e75d0173a3edd (patch)
treefaa36a58a552f2a4c36bdc362701e81d7e7d7261
parent8b5b46718113166b5f6bcdf40e67ea867461e209 (diff)
skge: race with workq and RTNL
If a workqueue function that needs RTNL is running when skge_down is called then a deadlock is possible. Fix by only clearing the timer, and handling the flush_scheduled_work on removal. This work queue is only ever used for the old fiber based boards. Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org> Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r--drivers/net/skge.c9
1 files changed, 5 insertions, 4 deletions
diff --git a/drivers/net/skge.c b/drivers/net/skge.c
index e482e7fcbb2b..c3d2e0a2c4e6 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -1419,7 +1419,8 @@ static void xm_link_timer(struct work_struct *work)
mutex_unlock(&hw->phy_mutex);
nochange:
- schedule_delayed_work(&skge->link_thread, LINK_HZ);
+ if (netif_running(dev))
+ schedule_delayed_work(&skge->link_thread, LINK_HZ);
}
static void genesis_mac_init(struct skge_hw *hw, int port)
@@ -2530,7 +2531,7 @@ static int skge_down(struct net_device *dev)
netif_stop_queue(dev);
if (hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC)
- cancel_rearming_delayed_work(&skge->link_thread);
+ cancel_delayed_work(&skge->link_thread);
skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF);
if (hw->chip_id == CHIP_ID_GENESIS)
@@ -3690,6 +3691,8 @@ static void __devexit skge_remove(struct pci_dev *pdev)
if (!hw)
return;
+ flush_scheduled_work();
+
if ((dev1 = hw->dev[1]))
unregister_netdev(dev1);
dev0 = hw->dev[0];
@@ -3704,8 +3707,6 @@ static void __devexit skge_remove(struct pci_dev *pdev)
skge_write16(hw, B0_LED, LED_STAT_OFF);
skge_write8(hw, B0_CTST, CS_RST_SET);
- flush_scheduled_work();
-
free_irq(pdev->irq, hw);
pci_release_regions(pdev);
pci_disable_device(pdev);