From: Ian Campbell 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: Ian Campbell Signed-off-by: Nicolas Pitre Signed-off-by: Andrew Morton --- 25-akpm/drivers/net/smc91x.c | 30 ++++++++++++++++++++++++------ 1 files changed, 24 insertions(+), 6 deletions(-) diff -puN drivers/net/smc91x.c~avoid-deadlock-in-smc91x-driver drivers/net/smc91x.c --- 25/drivers/net/smc91x.c~avoid-deadlock-in-smc91x-driver 2004-11-28 00:47:57.877931216 -0800 +++ 25-akpm/drivers/net/smc91x.c 2004-11-28 00:47:57.882930456 -0800 @@ -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_dev /* * 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_devic /* * 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 * 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_d * 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; _