diff options
author | Michael Chan <mchan@broadcom.com> | 2006-02-02 20:29:28 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-02-02 20:29:28 -0500 |
commit | 7faa006f94fda56a587242b2a22fa19abf840222 (patch) | |
tree | b1354b391829bc3f1d83fd3121167318f8472147 /drivers/net/tg3.c | |
parent | 3f330317ab4973178423aba750d6d0ca5ce0024a (diff) |
[TG3]: Flush tg3_reset_task()
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>
Diffstat (limited to 'drivers/net/tg3.c')
-rw-r--r-- | drivers/net/tg3.c | 26 |
1 files changed, 24 insertions, 2 deletions
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index f2d1dafde087..6fb29ca3fd30 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c | |||
@@ -3482,6 +3482,17 @@ static void tg3_reset_task(void *_data) | |||
3482 | struct tg3 *tp = _data; | 3482 | struct tg3 *tp = _data; |
3483 | unsigned int restart_timer; | 3483 | unsigned int restart_timer; |
3484 | 3484 | ||
3485 | tg3_full_lock(tp, 0); | ||
3486 | tp->tg3_flags |= TG3_FLAG_IN_RESET_TASK; | ||
3487 | |||
3488 | if (!netif_running(tp->dev)) { | ||
3489 | tp->tg3_flags &= ~TG3_FLAG_IN_RESET_TASK; | ||
3490 | tg3_full_unlock(tp); | ||
3491 | return; | ||
3492 | } | ||
3493 | |||
3494 | tg3_full_unlock(tp); | ||
3495 | |||
3485 | tg3_netif_stop(tp); | 3496 | tg3_netif_stop(tp); |
3486 | 3497 | ||
3487 | tg3_full_lock(tp, 1); | 3498 | tg3_full_lock(tp, 1); |
@@ -3494,10 +3505,12 @@ static void tg3_reset_task(void *_data) | |||
3494 | 3505 | ||
3495 | tg3_netif_start(tp); | 3506 | tg3_netif_start(tp); |
3496 | 3507 | ||
3497 | tg3_full_unlock(tp); | ||
3498 | |||
3499 | if (restart_timer) | 3508 | if (restart_timer) |
3500 | mod_timer(&tp->timer, jiffies + 1); | 3509 | mod_timer(&tp->timer, jiffies + 1); |
3510 | |||
3511 | tp->tg3_flags &= ~TG3_FLAG_IN_RESET_TASK; | ||
3512 | |||
3513 | tg3_full_unlock(tp); | ||
3501 | } | 3514 | } |
3502 | 3515 | ||
3503 | static void tg3_tx_timeout(struct net_device *dev) | 3516 | static void tg3_tx_timeout(struct net_device *dev) |
@@ -6786,6 +6799,13 @@ static int tg3_close(struct net_device *dev) | |||
6786 | { | 6799 | { |
6787 | struct tg3 *tp = netdev_priv(dev); | 6800 | struct tg3 *tp = netdev_priv(dev); |
6788 | 6801 | ||
6802 | /* Calling flush_scheduled_work() may deadlock because | ||
6803 | * linkwatch_event() may be on the workqueue and it will try to get | ||
6804 | * the rtnl_lock which we are holding. | ||
6805 | */ | ||
6806 | while (tp->tg3_flags & TG3_FLAG_IN_RESET_TASK) | ||
6807 | msleep(1); | ||
6808 | |||
6789 | netif_stop_queue(dev); | 6809 | netif_stop_queue(dev); |
6790 | 6810 | ||
6791 | del_timer_sync(&tp->timer); | 6811 | del_timer_sync(&tp->timer); |
@@ -10880,6 +10900,7 @@ static void __devexit tg3_remove_one(struct pci_dev *pdev) | |||
10880 | if (dev) { | 10900 | if (dev) { |
10881 | struct tg3 *tp = netdev_priv(dev); | 10901 | struct tg3 *tp = netdev_priv(dev); |
10882 | 10902 | ||
10903 | flush_scheduled_work(); | ||
10883 | unregister_netdev(dev); | 10904 | unregister_netdev(dev); |
10884 | if (tp->regs) { | 10905 | if (tp->regs) { |
10885 | iounmap(tp->regs); | 10906 | iounmap(tp->regs); |
@@ -10901,6 +10922,7 @@ static int tg3_suspend(struct pci_dev *pdev, pm_message_t state) | |||
10901 | if (!netif_running(dev)) | 10922 | if (!netif_running(dev)) |
10902 | return 0; | 10923 | return 0; |
10903 | 10924 | ||
10925 | flush_scheduled_work(); | ||
10904 | tg3_netif_stop(tp); | 10926 | tg3_netif_stop(tp); |
10905 | 10927 | ||
10906 | del_timer_sync(&tp->timer); | 10928 | del_timer_sync(&tp->timer); |