net: Eliminate flush_scheduled_work() calls while RTNL is held.

If the RTNL is held when we invoke flush_scheduled_work() we could
deadlock.  One such case is linkwatch, it is a work struct which tries
to grab the RTNL semaphore.

The most common case are net driver ->stop() methods.  The
simplest conversion is to instead use cancel_{delayed_}work_sync()
explicitly on the various work struct the driver uses.

This is an OK transformation because these work structs are doing
things like resetting the chip, restarting link negotiation, and so
forth.  And if we're bringing down the device, we're about to turn the
chip off and reset it anways.  So if we cancel a pending work event,
that's fine here.

Some drivers were working around this deadlock by using a msleep()
polling loop of some sort, and those cases are converted to instead
use cancel_{delayed_}work_sync() as well.

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller
2008-06-12 02:22:02 -07:00
parent 7afb380db4
commit 4bb073c0e3
9 changed files with 21 additions and 47 deletions

View File

@@ -1016,15 +1016,8 @@ static void smc_phy_powerdown(struct net_device *dev)
/* We need to ensure that no calls to smc_phy_configure are
pending.
flush_scheduled_work() cannot be called because we are
running with the netlink semaphore held (from
devinet_ioctl()) and the pending work queue contains
linkwatch_event() (scheduled by netif_carrier_off()
above). linkwatch_event() also wants the netlink semaphore.
*/
while(lp->work_pending)
yield();
cancel_work_sync(&lp->phy_configure);
bmcr = smc_phy_read(dev, phy, MII_BMCR);
smc_phy_write(dev, phy, MII_BMCR, bmcr | BMCR_PDOWN);
@@ -1161,7 +1154,6 @@ static void smc_phy_configure(struct work_struct *work)
smc_phy_configure_exit:
SMC_SELECT_BANK(lp, 2);
spin_unlock_irq(&lp->lock);
lp->work_pending = 0;
}
/*
@@ -1389,11 +1381,8 @@ static void smc_timeout(struct net_device *dev)
* smc_phy_configure() calls msleep() which calls schedule_timeout()
* which calls schedule(). Hence we use a work queue.
*/
if (lp->phy_type != 0) {
if (schedule_work(&lp->phy_configure)) {
lp->work_pending = 1;
}
}
if (lp->phy_type != 0)
schedule_work(&lp->phy_configure);
/* We can accept TX packets again */
dev->trans_start = jiffies;