summaryrefslogtreecommitdiffstats
path: root/drivers/net/tg3.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/tg3.c')
-rw-r--r--drivers/net/tg3.c26
1 files changed, 24 insertions, 2 deletions
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index f2d1dafde08..6fb29ca3fd3 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -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);