]> err.no Git - linux-2.6/commitdiff
skge: race with workq and RTNL
authorStephen Hemminger <shemminger@linux-foundation.org>
Fri, 16 Feb 2007 23:37:39 +0000 (15:37 -0800)
committerJeff Garzik <jeff@garzik.org>
Tue, 20 Feb 2007 16:18:13 +0000 (11:18 -0500)
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>
drivers/net/skge.c

index e482e7fcbb2be36117ee646d4699fb87d49c141d..c3d2e0a2c4e62ea7a11163889a7507f5f0843549 100644 (file)
@@ -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);