FAILED: patch "[PATCH] usbnet: smsc95xx: Fix deadlock on runtime resume" failed to apply to 4.9-stable tree
[email protected]
maintainerThe patch below does not apply to the 4.9-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 7b960c967f2aa01ab8f45c5a0bd78e754cffdeee Mon Sep 17 00:00:00 2001
From: Lukas Wunner <[email protected]>
Date: Fri, 1 Jul 2022 22:47:51 +0200
Subject: [PATCH] usbnet: smsc95xx: Fix deadlock on runtime resume
Commit 05b35e7eb9a1 ("smsc95xx: add phylib support") amended smsc95xx_resume() to call phy_init_hw(). That function waits for the device to runtime resume even though it is placed in the runtime resume path, causing a deadlock.
The problem is that phy_init_hw() calls down to smsc95xx_mdiobus_read(), which never uses the _nopm variant of usbnet_read_cmd().
Commit b4df480f68ae ("usbnet: smsc95xx: add reset_resume function with reset operation") causes a similar deadlock on resume if the device was already runtime suspended when entering system sleep:
That's because the commit introduced smsc95xx_reset_resume(), which calls down to smsc95xx_reset(), which neglects to use _nopm accessors.
Fix by auto-detecting whether a device access is performed by the suspend/resume task_struct and use the _nopm variant if so. This works because the PM core guarantees that suspend/resume callbacks are run in task context.
Stacktrace for posterity:
INFO: task kworker/2:1:49 blocked for more than 122 seconds. Workqueue: usb_hub_wq hub_event schedule rpm_resume __pm_runtime_resume usb_autopm_get_interface usbnet_read_cmd __smsc95xx_read_reg __smsc95xx_phy_wait_not_busy __smsc95xx_mdio_read smsc95xx_mdiobus_read __mdiobus_read mdiobus_read smsc_phy_reset phy_init_hw smsc95xx_resume usb_resume_interface usb_resume_both usb_runtime_resume __rpm_callback rpm_callback rpm_resume __pm_runtime_resume usb_autoresume_device hub_event process_one_work
drivers/net/usb/smsc95xx.c +20 -6
@@ -71,6 +71,7 @@ struct smsc95xx_priv {struct fwnode_handle *irqfwnode;struct mii_bus *mdiobus;struct phy_device *phydev; +struct task_struct *pm_task; };static bool turbo_mode = true;
@@ -80,13 +81,14 @@ MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction");static int __must_check __smsc95xx_read_reg (struct usbnet *dev,u32 index,u32 *data,int in_pm) { +struct smsc95xx_priv *pdata = dev->driver_priv;u32 buf;int ret;int (*fn)(struct usbnet *,u8 ,u8 ,u16 ,u16 ,void *,u16 );BUG_ON (!dev); -if (!in_pm) +if (current != pdata->pm_task) fn = usbnet_read_cmd;else fn = usbnet_read_cmd_nopm;
@@ -110,13 +112,14 @@ static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index,static int __must_check __smsc95xx_write_reg (struct usbnet *dev,u32 index,u32 data,int in_pm) { +struct smsc95xx_priv *pdata = dev->driver_priv;u32 buf;int ret;int (*fn)(struct usbnet *,u8 ,u8 ,u16 ,u16 ,const void *,u16 );BUG_ON (!dev); -if (!in_pm) +if (current != pdata->pm_task) fn = usbnet_write_cmd;else fn = usbnet_write_cmd_nopm;
@@ -1490,9 +1493,12 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)u32 val, link_up;int ret; + pdata->pm_task = current; + ret =usbnet_suspend (intf, message);if (ret <0 ) {netdev_warn (dev->net,"usbnet_suspend error\n" ); + pdata->pm_task =NULL ;return ret; }
@@ -1732,6 +1738,7 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)if (ret &&PMSG_IS_AUTO (message))usbnet_resume (intf); + pdata->pm_task =NULL ;return ret; }
@@ -1752,29 +1759,31 @@ static int smsc95xx_resume(struct usb_interface *intf)/* do this first to ensure it's cleared even in error case */ pdata->suspend_flags =0 ; + pdata->pm_task = current; +if (suspend_flags &SUSPEND_ALLMODES ) {/* clear wake-up sources */ ret =smsc95xx_read_reg_nopm (dev,WUCSR , &val);if (ret <0 ) -return ret; + goto done; val &= ~(WUCSR_WAKE_EN_ |WUCSR_MPEN_ ); ret =smsc95xx_write_reg_nopm (dev,WUCSR , val);if (ret <0 ) -return ret; + goto done;/* clear wake-up status */ ret =smsc95xx_read_reg_nopm (dev,PM_CTRL , &val);if (ret <0 ) -return ret; + goto done; val &= ~PM_CTL_WOL_EN_ ; val |=PM_CTL_WUPS_ ; ret =smsc95xx_write_reg_nopm (dev,PM_CTRL , val);if (ret <0 ) -return ret; + goto done; }phy_init_hw (pdata->phydev);
@@ -1783,15 +1792,20 @@ static int smsc95xx_resume(struct usb_interface *intf)if (ret <0 )netdev_warn (dev->net,"usbnet_resume error\n" ); +done: + pdata->pm_task =NULL ;return ret; }static int smsc95xx_reset_resume (struct usb_interface *intf) {struct usbnet *dev =usb_get_intfdata (intf); +struct smsc95xx_priv *pdata = dev->driver_priv;int ret; + pdata->pm_task = current; ret =smsc95xx_reset (dev); + pdata->pm_task =NULL ;if (ret <0 )return ret;