Commit d20a8fb5 authored by Ian Campbell's avatar Ian Campbell Committed by Linus Torvalds

[PATCH] Avoid deadlock in smc91x driver

This patch avoids a deadlock on rtnl_sem in smc_close() when bringing down
an smc91x interface.  The semaphore is already held by devinet_ioctl() and
the pending work queue contains linkwatch_event() (scheduled by
netif_carrier_off()) which also wants rtnl_sem hence it is unsafe to call
flush_scheduled_work().

The solution is to track whether we have any pending work of our own and
wait for that instead of flushing the entire queue.

I also fixed a typo 'ence' -> 'Hence' and renamed smc_detect_phy to
smc_phy_detect in order to follow the same pattern as the other smc_phy_*
functions.
Signed-off-by: default avatarIan Campbell <icampbell@arcom.com>
Signed-off-by: default avatarNicolas Pitre <nico@cam.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent ea0f86b0
......@@ -203,7 +203,10 @@ struct smc_local {
u32 msg_enable;
u32 phy_type;
struct mii_if_info mii;
/* work queue */
struct work_struct phy_configure;
int work_pending;
spinlock_t lock;
......@@ -903,7 +906,7 @@ static void smc_phy_write(struct net_device *dev, int phyaddr, int phyreg,
/*
* Finds and reports the PHY address
*/
static void smc_detect_phy(struct net_device *dev)
static void smc_phy_detect(struct net_device *dev)
{
struct smc_local *lp = netdev_priv(dev);
int phyaddr;
......@@ -1155,6 +1158,7 @@ static void smc_phy_configure(void *data)
smc_phy_configure_exit:
spin_unlock_irq(&lp->lock);
lp->work_pending = 0;
}
/*
......@@ -1350,10 +1354,13 @@ static void smc_timeout(struct net_device *dev)
/*
* Reconfiguring the PHY doesn't seem like a bad idea here, but
* smc_phy_configure() calls msleep() which calls schedule_timeout()
* which calls schedule(). Ence we use a work queue.
* which calls schedule(). Hence we use a work queue.
*/
if (lp->phy_type != 0)
schedule_work(&lp->phy_configure);
if (lp->phy_type != 0) {
if (schedule_work(&lp->phy_configure)) {
lp->work_pending = 1;
}
}
/* We can accept TX packets again */
dev->trans_start = jiffies;
......@@ -1537,7 +1544,18 @@ static int smc_close(struct net_device *dev)
smc_shutdown(dev);
if (lp->phy_type != 0) {
flush_scheduled_work();
/* 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)
schedule();
smc_phy_powerdown(dev, lp->mii.phy_id);
}
......@@ -1904,7 +1922,7 @@ static int __init smc_probe(struct net_device *dev, unsigned long ioaddr)
* Locate the phy, if any.
*/
if (lp->version >= (CHIP_91100 << 4))
smc_detect_phy(dev);
smc_phy_detect(dev);
/* Set default parameters */
lp->msg_enable = NETIF_MSG_LINK;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment