From: Michael Chan Date: Thu, 25 Aug 2005 22:34:29 +0000 (-0700) Subject: [BNX2]: Fix rtnl deadlock in bnx2_close X-Git-Tag: v2.6.14-rc1~1035^2~25 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=afdc08b9f9a7174d7912a160f657f39d46379b5e;p=linux-2.6-omap-h63xx.git [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 Signed-off-by: David S. Miller --- diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index 3a9d6a8b90a..635a5856102 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -3975,12 +3975,17 @@ bnx2_reset_task(void *data) { struct bnx2 *bp = data; + if (!netif_running(bp->dev)) + return; + + bp->in_reset_task = 1; bnx2_netif_stop(bp); bnx2_init_nic(bp); atomic_set(&bp->intr_sem, 1); bnx2_netif_start(bp); + bp->in_reset_task = 0; } static void @@ -4172,7 +4177,13 @@ bnx2_close(struct net_device *dev) struct bnx2 *bp = dev->priv; u32 reset_code; - flush_scheduled_work(); + /* 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 (bp->in_reset_task) + msleep(1); + bnx2_netif_stop(bp); del_timer_sync(&bp->timer); if (bp->wol) @@ -5453,6 +5464,8 @@ bnx2_remove_one(struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); struct bnx2 *bp = dev->priv; + flush_scheduled_work(); + unregister_netdev(dev); if (bp->regview) diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h index 8214a2853d0..63b94ca0018 100644 --- a/drivers/net/bnx2.h +++ b/drivers/net/bnx2.h @@ -3874,6 +3874,7 @@ struct bnx2 { int timer_interval; struct timer_list timer; struct work_struct reset_task; + int in_reset_task; /* Used to synchronize phy accesses. */ spinlock_t phy_lock;