Commit 3d3c4cd5 authored by Oleksij Rempel's avatar Oleksij Rempel Committed by Paolo Abeni
Browse files

net: usb: asix: hold PM usage ref to avoid PM/MDIO + RTNL deadlock



Prevent USB runtime PM (autosuspend) for AX88772* in bind.

usbnet enables runtime PM (autosuspend) by default, so disabling it via
the usb_driver flag is ineffective. On AX88772B, autosuspend shows no
measurable power saving with current driver (no link partner, admin
up/down). The ~0.453 W -> ~0.248 W drop on v6.1 comes from phylib powering
the PHY off on admin-down, not from USB autosuspend.

The real hazard is that with runtime PM enabled, ndo_open() (under RTNL)
may synchronously trigger autoresume (usb_autopm_get_interface()) into
asix_resume() while the USB PM lock is held. Resume paths then invoke
phylink/phylib and MDIO, which also expect RTNL, leading to possible
deadlocks or PM lock vs MDIO wake issues.

To avoid this, keep the device runtime-PM active by taking a usage
reference in ax88772_bind() and dropping it in unbind(). A non-zero PM
usage count blocks runtime suspend regardless of userspace policy
(.../power/control - pm_runtime_allow/forbid), making this approach
robust against sysfs overrides.

Holding a runtime-PM usage ref does not affect system-wide suspend;
system sleep/resume callbacks continue to run as before.

Fixes: 4a2c7217 ("net: usb: asix: ax88772: manage PHY PM from MAC")
Reported-by: default avatarHubert Wiśniewski <hubert.wisniewski.25632@gmail.com>
Closes: https://lore.kernel.org/all/DCGHG5UJT9G3.2K1GHFZ3H87T0@gmail.com


Tested-by: default avatarHubert Wiśniewski <hubert.wisniewski.25632@gmail.com>
Reported-by: default avatarMarek Szyprowski <m.szyprowski@samsung.com>
Closes: https://lore.kernel.org/all/b5ea8296-f981-445d-a09a-2f389d7f6fdd@samsung.com


Cc: stable@vger.kernel.org
Signed-off-by: default avatarOleksij Rempel <o.rempel@pengutronix.de>
Link: https://patch.msgid.link/20251005081203.3067982-1-o.rempel@pengutronix.de


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent b615879d
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -625,6 +625,21 @@ static void ax88772_suspend(struct usbnet *dev)
		   asix_read_medium_status(dev, 1));
}

/* Notes on PM callbacks and locking context:
 *
 * - asix_suspend()/asix_resume() are invoked for both runtime PM and
 *   system-wide suspend/resume. For struct usb_driver the ->resume()
 *   callback does not receive pm_message_t, so the resume type cannot
 *   be distinguished here.
 *
 * - The MAC driver must hold RTNL when calling phylink interfaces such as
 *   phylink_suspend()/resume(). Those calls will also perform MDIO I/O.
 *
 * - Taking RTNL and doing MDIO from a runtime-PM resume callback (while
 *   the USB PM lock is held) is fragile. Since autosuspend brings no
 *   measurable power saving here, we block it by holding a PM usage
 *   reference in ax88772_bind().
 */
static int asix_suspend(struct usb_interface *intf, pm_message_t message)
{
	struct usbnet *dev = usb_get_intfdata(intf);
@@ -919,6 +934,13 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
	if (ret)
		goto initphy_err;

	/* Keep this interface runtime-PM active by taking a usage ref.
	 * Prevents runtime suspend while bound and avoids resume paths
	 * that could deadlock (autoresume under RTNL while USB PM lock
	 * is held, phylink/MDIO wants RTNL).
	 */
	pm_runtime_get_noresume(&intf->dev);

	return 0;

initphy_err:
@@ -948,6 +970,8 @@ static void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf)
	phylink_destroy(priv->phylink);
	ax88772_mdio_unregister(priv);
	asix_rx_fixup_common_free(dev->driver_priv);
	/* Drop the PM usage ref taken in bind() */
	pm_runtime_put(&intf->dev);
}

static void ax88178_unbind(struct usbnet *dev, struct usb_interface *intf)
@@ -1600,6 +1624,11 @@ static struct usb_driver asix_driver = {
	.resume =	asix_resume,
	.reset_resume =	asix_resume,
	.disconnect =	usbnet_disconnect,
	/* usbnet enables autosuspend by default (supports_autosuspend=1).
	 * We keep runtime-PM active for AX88772* by taking a PM usage
	 * reference in ax88772_bind() (pm_runtime_get_noresume()) and
	 * dropping it in unbind(), which effectively blocks autosuspend.
	 */
	.supports_autosuspend = 1,
	.disable_hub_initiated_lpm = 1,
};