[TG3]: Flush tg3_reset_task()
authorMichael Chan <mchan@broadcom.com>
Fri, 3 Feb 2006 01:29:28 +0000 (17:29 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 3 Feb 2006 01:29:28 +0000 (17:29 -0800)
Make sure tg3_reset_task() is flushed in the close and suspend paths
as noted by Jeff Garzik.

In the close path, calling flush_scheduled_work() may cause deadlock
if linkwatch_event() is on the workqueue. linkwatch_event() will try
to get the rtnl_lock() which is already held by tg3_close(). So
instead, we set a flag in tg3_reset_task() and tg3_close() polls
the flag until it is cleared.

Signed-off-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/tg3.c
drivers/net/tg3.h

index f2d1dafde08773ad74107309b5e74ed9a0b60f16..6fb29ca3fd30195215cef1b7923fefd3db193976 100644 (file)
@@ -3482,6 +3482,17 @@ static void tg3_reset_task(void *_data)
        struct tg3 *tp = _data;
        unsigned int restart_timer;
 
+       tg3_full_lock(tp, 0);
+       tp->tg3_flags |= TG3_FLAG_IN_RESET_TASK;
+
+       if (!netif_running(tp->dev)) {
+               tp->tg3_flags &= ~TG3_FLAG_IN_RESET_TASK;
+               tg3_full_unlock(tp);
+               return;
+       }
+
+       tg3_full_unlock(tp);
+
        tg3_netif_stop(tp);
 
        tg3_full_lock(tp, 1);
@@ -3494,10 +3505,12 @@ static void tg3_reset_task(void *_data)
 
        tg3_netif_start(tp);
 
-       tg3_full_unlock(tp);
-
        if (restart_timer)
                mod_timer(&tp->timer, jiffies + 1);
+
+       tp->tg3_flags &= ~TG3_FLAG_IN_RESET_TASK;
+
+       tg3_full_unlock(tp);
 }
 
 static void tg3_tx_timeout(struct net_device *dev)
@@ -6786,6 +6799,13 @@ static int tg3_close(struct net_device *dev)
 {
        struct tg3 *tp = netdev_priv(dev);
 
+       /* Calling flush_scheduled_work() may deadlock because
+        * linkwatch_event() may be on the workqueue and it will try to get
+        * the rtnl_lock which we are holding.
+        */
+       while (tp->tg3_flags & TG3_FLAG_IN_RESET_TASK)
+               msleep(1);
+
        netif_stop_queue(dev);
 
        del_timer_sync(&tp->timer);
@@ -10880,6 +10900,7 @@ static void __devexit tg3_remove_one(struct pci_dev *pdev)
        if (dev) {
                struct tg3 *tp = netdev_priv(dev);
 
+               flush_scheduled_work();
                unregister_netdev(dev);
                if (tp->regs) {
                        iounmap(tp->regs);
@@ -10901,6 +10922,7 @@ static int tg3_suspend(struct pci_dev *pdev, pm_message_t state)
        if (!netif_running(dev))
                return 0;
 
+       flush_scheduled_work();
        tg3_netif_stop(tp);
 
        del_timer_sync(&tp->timer);
index e8243305f0e807a9433ac9e0762dc5266ff116aa..7f4b7f6ac40dec8085ac567457ce50ddb83038d3 100644 (file)
@@ -2162,6 +2162,7 @@ struct tg3 {
 #define TG3_FLAG_JUMBO_RING_ENABLE     0x00800000
 #define TG3_FLAG_10_100_ONLY           0x01000000
 #define TG3_FLAG_PAUSE_AUTONEG         0x02000000
+#define TG3_FLAG_IN_RESET_TASK         0x04000000
 #define TG3_FLAG_BROKEN_CHECKSUMS      0x10000000
 #define TG3_FLAG_GOT_SERDES_FLOWCTL    0x20000000
 #define TG3_FLAG_SPLIT_MODE            0x40000000