diff options
author | Stephen Hemminger <shemminger@linux-foundation.org> | 2007-02-16 18:37:39 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-02-20 11:18:13 -0500 |
commit | 208491d8f92e5aa129acb27e223e75d0173a3edd (patch) | |
tree | faa36a58a552f2a4c36bdc362701e81d7e7d7261 /drivers/net/skge.c | |
parent | 8b5b46718113166b5f6bcdf40e67ea867461e209 (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>
Diffstat (limited to 'drivers/net/skge.c')
-rw-r--r-- | drivers/net/skge.c | 9 |
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) | |||
1419 | mutex_unlock(&hw->phy_mutex); | 1419 | mutex_unlock(&hw->phy_mutex); |
1420 | 1420 | ||
1421 | nochange: | 1421 | nochange: |
1422 | schedule_delayed_work(&skge->link_thread, LINK_HZ); | 1422 | if (netif_running(dev)) |
1423 | schedule_delayed_work(&skge->link_thread, LINK_HZ); | ||
1423 | } | 1424 | } |
1424 | 1425 | ||
1425 | static void genesis_mac_init(struct skge_hw *hw, int port) | 1426 | static void genesis_mac_init(struct skge_hw *hw, int port) |
@@ -2530,7 +2531,7 @@ static int skge_down(struct net_device *dev) | |||
2530 | 2531 | ||
2531 | netif_stop_queue(dev); | 2532 | netif_stop_queue(dev); |
2532 | if (hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC) | 2533 | if (hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC) |
2533 | cancel_rearming_delayed_work(&skge->link_thread); | 2534 | cancel_delayed_work(&skge->link_thread); |
2534 | 2535 | ||
2535 | skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF); | 2536 | skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF); |
2536 | if (hw->chip_id == CHIP_ID_GENESIS) | 2537 | if (hw->chip_id == CHIP_ID_GENESIS) |
@@ -3690,6 +3691,8 @@ static void __devexit skge_remove(struct pci_dev *pdev) | |||
3690 | if (!hw) | 3691 | if (!hw) |
3691 | return; | 3692 | return; |
3692 | 3693 | ||
3694 | flush_scheduled_work(); | ||
3695 | |||
3693 | if ((dev1 = hw->dev[1])) | 3696 | if ((dev1 = hw->dev[1])) |
3694 | unregister_netdev(dev1); | 3697 | unregister_netdev(dev1); |
3695 | dev0 = hw->dev[0]; | 3698 | dev0 = hw->dev[0]; |
@@ -3704,8 +3707,6 @@ static void __devexit skge_remove(struct pci_dev *pdev) | |||
3704 | skge_write16(hw, B0_LED, LED_STAT_OFF); | 3707 | skge_write16(hw, B0_LED, LED_STAT_OFF); |
3705 | skge_write8(hw, B0_CTST, CS_RST_SET); | 3708 | skge_write8(hw, B0_CTST, CS_RST_SET); |
3706 | 3709 | ||
3707 | flush_scheduled_work(); | ||
3708 | |||
3709 | free_irq(pdev->irq, hw); | 3710 | free_irq(pdev->irq, hw); |
3710 | pci_release_regions(pdev); | 3711 | pci_release_regions(pdev); |
3711 | pci_disable_device(pdev); | 3712 | pci_disable_device(pdev); |