diff options
author | Michael Chan <mchan@broadcom.com> | 2005-08-25 18:34:29 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2005-08-29 19:10:29 -0400 |
commit | afdc08b9f9a7174d7912a160f657f39d46379b5e (patch) | |
tree | 04856840d578c029c8c675130ba93bd607bcaa1e /drivers/net/bnx2.c | |
parent | 2373ce1ca04dd46bf2b8b0f9a799eb2a90da92fb (diff) |
[BNX2]: Fix rtnl deadlock in bnx2_close
This fixes an rtnl deadlock problem when flush_scheduled_work() is
called from bnx2_close(). In rare cases, linkwatch_event() may be on
the workqueue from a previous close of a different device and it will
try to get the rtnl lock which is already held by dev_close().
The fix is to set a flag if we are in the reset task which is run
from the workqueue. bnx2_close() will loop until the flag is cleared.
As suggested by Jeff Garzik, the loop is changed to call msleep(1)
instead of yield() in the original patch.
flush_scheduled_work() is also moved to bnx2_remove_one() before the
netdev is freed.
Signed-off-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/bnx2.c')
-rw-r--r-- | drivers/net/bnx2.c | 15 |
1 files changed, 14 insertions, 1 deletions
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index 3a9d6a8b90a2..635a5856102b 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c | |||
@@ -3975,12 +3975,17 @@ bnx2_reset_task(void *data) | |||
3975 | { | 3975 | { |
3976 | struct bnx2 *bp = data; | 3976 | struct bnx2 *bp = data; |
3977 | 3977 | ||
3978 | if (!netif_running(bp->dev)) | ||
3979 | return; | ||
3980 | |||
3981 | bp->in_reset_task = 1; | ||
3978 | bnx2_netif_stop(bp); | 3982 | bnx2_netif_stop(bp); |
3979 | 3983 | ||
3980 | bnx2_init_nic(bp); | 3984 | bnx2_init_nic(bp); |
3981 | 3985 | ||
3982 | atomic_set(&bp->intr_sem, 1); | 3986 | atomic_set(&bp->intr_sem, 1); |
3983 | bnx2_netif_start(bp); | 3987 | bnx2_netif_start(bp); |
3988 | bp->in_reset_task = 0; | ||
3984 | } | 3989 | } |
3985 | 3990 | ||
3986 | static void | 3991 | static void |
@@ -4172,7 +4177,13 @@ bnx2_close(struct net_device *dev) | |||
4172 | struct bnx2 *bp = dev->priv; | 4177 | struct bnx2 *bp = dev->priv; |
4173 | u32 reset_code; | 4178 | u32 reset_code; |
4174 | 4179 | ||
4175 | flush_scheduled_work(); | 4180 | /* Calling flush_scheduled_work() may deadlock because |
4181 | * linkwatch_event() may be on the workqueue and it will try to get | ||
4182 | * the rtnl_lock which we are holding. | ||
4183 | */ | ||
4184 | while (bp->in_reset_task) | ||
4185 | msleep(1); | ||
4186 | |||
4176 | bnx2_netif_stop(bp); | 4187 | bnx2_netif_stop(bp); |
4177 | del_timer_sync(&bp->timer); | 4188 | del_timer_sync(&bp->timer); |
4178 | if (bp->wol) | 4189 | if (bp->wol) |
@@ -5453,6 +5464,8 @@ bnx2_remove_one(struct pci_dev *pdev) | |||
5453 | struct net_device *dev = pci_get_drvdata(pdev); | 5464 | struct net_device *dev = pci_get_drvdata(pdev); |
5454 | struct bnx2 *bp = dev->priv; | 5465 | struct bnx2 *bp = dev->priv; |
5455 | 5466 | ||
5467 | flush_scheduled_work(); | ||
5468 | |||
5456 | unregister_netdev(dev); | 5469 | unregister_netdev(dev); |
5457 | 5470 | ||
5458 | if (bp->regview) | 5471 | if (bp->regview) |