Linux USB
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Prashant Sengar" <prashantsengar25031@gmail.com>
Cc: linux-usb@vger.kernel.org
Subject: Re: [PATCH net-next] net: usb: lan78xx: use phylink for Wake-on-LAN
Date: Mon, 09 Nov 2026 18:02:05 +0000	[thread overview]
Message-ID: <sashiko-outbox-205365@kernel.org> (raw)
In-Reply-To: <20261108180137.3488023-1-prashantsengar25031@gmail.com>

Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [High] net: usb: lan78xx: phylink MAC WoL breaks suspend and
  lan78xx_resume()
- [Medium] net: usb: lan78xx: missing WAKE_PHY in
  lan78xx_phylink_setup() breaks PHY WoL
- [Medium] net: usb: lan78xx: pdata->wol overwrite conflicts with
  phylink PHY WoL
- [Medium] net: usb: lan78xx: inconsistent WoL state in lan78xx_probe()
  and lan78xx_set_wol()
It also noticed 1 related pre-existing issue this patch does not
introduce:
- [High] net: usb: lan78xx: missing rtnl_lock() in lan78xx_phy_uninit()
  [1]
--

commit 2aa41b74c1faf1ee028bdb9727321bf174890b23
Author: Prashant Sengar <prashantsengar25031@gmail.com>

net: usb: lan78xx: use phylink for Wake-on-LAN

This commit converts lan78xx Wake-on-LAN handling to use phylink helpers
and adds a phylink .mac_wol_set callback so MAC-based Wake-on-LAN can be
configured with or without an attached PHY.

> diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
> index d73002b2c21ac..7d67dd150271b 100644
> --- a/drivers/net/usb/lan78xx.c
> +++ b/drivers/net/usb/lan78xx.c
[ ... ]
> @@ -1799,25 +1791,45 @@ static int lan78xx_set_wol(struct net_device *netdev,
>  {
>  	struct lan78xx_net *dev = netdev_priv(netdev);
>  	struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
> +	struct ethtool_wolinfo actual = { .cmd = ETHTOOL_GWOL };
> +	bool old_wakeup, enable;
> +	int rollback_ret;
>  	int ret;
>  
>  	if (wol->wolopts & ~WAKE_ALL)
>  		return -EINVAL;
>  
> -	if (!netdev->phydev)
> -		return -EOPNOTSUPP;
> -
>  	ret = usb_autopm_get_interface(dev->intf);
>  	if (ret < 0)
>  		return ret;
>  
> -	pdata->wol = wol->wolopts;
> -
> -	ret = device_set_wakeup_enable(&dev->udev->dev, (bool)wol->wolopts);
> -	if (ret < 0)
> +	phylink_ethtool_get_wol(dev->phylink, &actual);
> +	if (wol->wolopts & ~actual.supported) {
> +		ret = -EINVAL;
>  		goto exit_pm_put;
> +	}
> +
> +	old_wakeup = device_may_wakeup(&dev->udev->dev);
> +	enable = !!wol->wolopts;
>  
> -	ret = phy_ethtool_set_wol(netdev->phydev, wol);
> +	if (old_wakeup != enable) {
> +		ret = device_set_wakeup_enable(&dev->udev->dev, enable);
> +		if (ret < 0)
> +			goto exit_pm_put;
> +	}
> +
> +	ret = phylink_ethtool_set_wol(dev->phylink, wol);
> +	if (ret < 0) {
> +		if (old_wakeup != enable) {
> +			rollback_ret = device_set_wakeup_enable(&dev->udev->dev, old_wakeup);
> +			if (rollback_ret)
> +				netdev_warn(dev->net, "USB wakeup rollback failed: %d\n",
> +					    rollback_ret);
> +		}
> +		goto exit_pm_put;
> +	}
> +	phylink_ethtool_get_wol(dev->phylink, &actual);
> +	pdata->wol = actual.wolopts;

[Severity: Medium]
Does overwriting pdata->wol with actual.wolopts here (and in
lan78xx_probe()) desynchronize pdata->wol from phylink's pl->wolopts_mac
when an attached PHY handles Wake-on-LAN?

With pc->wol_phy_legacy set, phylink_ethtool_set_wol() first passes the
requested options to phy_ethtool_set_wol() and strips any bits reported by
phy_ethtool_get_wol() before calling lan78xx_mac_wol_set() and recording
pl->wolopts_mac.  On a LAN7801 with an external RGMII PHY whose driver
implements .set_wol and .get_wol for WAKE_MAGIC, phylink delegates
WAKE_MAGIC to the PHY and sets pl->wolopts_mac to 0, but reading back
phylink_ethtool_get_wol() overwrites pdata->wol with the combined PHY+MAC
mask (WAKE_MAGIC).  On suspend, phylink_suspend() then sees
pl->wolopts_mac == 0 and stops the MAC via phylink_stop(), while
lan78xx_set_suspend() programs WUCSR_MPEN_ on the stopped MAC without
enabling PMT_CTL_PHY_WAKE_EN_ for the PHY wakeup interrupt.

In addition, checking wol->wolopts & ~WAKE_ALL at the top of
lan78xx_set_wol() rejects PHY-supported modes outside WAKE_ALL (such as
WAKE_MAGICSECURE or WAKE_FILTER) even when lan78xx_get_wol() advertises
them via phylink_ethtool_get_wol().

> @@ -2617,12 +2629,24 @@ static int lan78xx_mac_enable_tx_lpi(struct phylink_config *config, u32 timer,
>  	return lan78xx_mac_eee_enable(dev, true);
>  }
>  
> +static int lan78xx_mac_wol_set(struct phylink_config *config,
> +			       u32 wolopts, const u8 *sopass)
> +{
> +	struct lan78xx_net *dev = netdev_priv(to_net_dev(config->dev));
> +	struct lan78xx_priv *pdata = (struct lan78xx_priv *)dev->data[0];
> +
> +	pdata->wol = wolopts;
> +
> +	return 0;
> +}
> +
>  static const struct phylink_mac_ops lan78xx_phylink_mac_ops = {
>  	.mac_config = lan78xx_mac_config,
>  	.mac_link_down = lan78xx_mac_link_down,
>  	.mac_link_up = lan78xx_mac_link_up,
>  	.mac_disable_tx_lpi = lan78xx_mac_disable_tx_lpi,
>  	.mac_enable_tx_lpi = lan78xx_mac_enable_tx_lpi,
> +	.mac_wol_set = lan78xx_mac_wol_set,
>  };

[Severity: High]
Does populating .mac_wol_set break link recovery across lan78xx_suspend()
and lan78xx_resume()?

Once .mac_wol_set is implemented, phylink_mac_supports_wol() returns true
and phylink_suspend() ignores its mac_wol parameter in favor of
!!pl->wolopts_mac:

drivers/net/phy/phylink.c:phylink_suspend() {
	ASSERT_RTNL();

	if (phylink_mac_supports_wol(pl))
		mac_wol = !!pl->wolopts_mac;

	if (mac_wol) {
		/* Wake-on-Lan enabled, MAC handling */
		mutex_lock(&pl->state_mutex);
		/* Stop theWatchdog, but keep link up */
		__set_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state);
		mutex_unlock(&pl->state_mutex);

		netif_carrier_off(pl->netdev);
	} else {
		phylink_stop(pl);
	}
}

Because lan78xx_probe() enables WAKE_MAGIC by default, phylink_suspend() in
lan78xx_suspend() (which runs on both runtime autosuspend after the 10s
idle delay and system suspend) now sets PHYLINK_DISABLE_MAC_WOL and drops
carrier instead of calling phylink_stop().  However, lan78xx_resume() never
calls phylink_resume() (only lan78xx_reset_resume() does), and
PHYLINK_DISABLE_MAC_WOL is only cleared by phylink_resume().  After the
first runtime autoresume or non-reset system resume, PHYLINK_DISABLE_MAC_WOL
remains permanently set, leaving carrier off, dropping deferred TX packets
in lan78xx_submit_deferred_urbs(), and preventing phylink_resolve() from
bringing the link back up even if the interface is cycled through
lan78xx_stop() and lan78xx_open().

Skipping phylink_stop() during lan78xx_suspend() also leaves the phylib
state machine running in PHY_POLL mode on LAN7800/LAN7850 (since
pc->mac_managed_pm is true), so phy_state_machine() fires 1 second into USB
runtime autosuspend and calls lan78xx_mdiobus_read() ->
usb_autopm_get_interface(), waking the device back up.

> @@ -2848,6 +2872,10 @@ static int lan78xx_phylink_setup(struct lan78xx_net *dev)
>  	       dev->phylink_config.supported_interfaces,
>  	       sizeof(dev->phylink_config.lpi_interfaces));
>  
> +	pc->wol_mac_support = WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST |
> +		      WAKE_MCAST | WAKE_ARP;
> +	pc->wol_phy_legacy = true;
> +
>  	phylink = phylink_create(pc, dev->net->dev.fwnode,
>  				 dev->interface, &lan78xx_phylink_mac_ops);

[Severity: Medium]
Does omitting WAKE_PHY from pc->wol_mac_support regress link-change
Wake-on-LAN on LAN7800 and LAN7850?

Previously lan78xx_get_wol() advertised WAKE_ALL and lan78xx_set_wol()
accepted WAKE_PHY, which lan78xx_set_suspend() uses to program
PMT_CTL_PHY_WAKE_EN_ in the MAC's PMT_CTL register.  The integrated PHY on
LAN7800 and LAN7850 uses the Microchip LAN88xx driver
(drivers/net/phy/microchip.c), which implements .set_wol = lan88xx_set_wol
but does not implement .get_wol.  Because phy_ethtool_get_wol() leaves
wol->supported untouched when .get_wol is NULL, phylink_ethtool_get_wol()
only reports pc->wol_mac_support, so WAKE_PHY is no longer advertised by
lan78xx_get_wol() and is rejected with -EINVAL by lan78xx_set_wol().

[ ... ]

> @@ -4803,6 +4835,24 @@ static int lan78xx_probe(struct usb_interface *intf,
>  	if (ret < 0)
>  		goto free_urbs;
>  
> +	/* Preserve the default magic-packet wakeup configuration. */
> +	rtnl_lock();
> +
> +	ret = phylink_ethtool_set_wol(dev->phylink, &wol);
> +	if (!ret) {
> +		struct lan78xx_priv *pdata = (struct lan78xx_priv *)dev->data[0];
> +
> +		wol.cmd = ETHTOOL_GWOL;
> +		phylink_ethtool_get_wol(dev->phylink, &wol);
> +		pdata->wol = wol.wolopts;
> +		netdev->ethtool->wol_enabled = !!wol.wolopts;
> +	}
> +
> +	rtnl_unlock();
> +
> +	if (ret)
> +		netdev_warn(netdev, "failed to initialize WoL: %d\n", ret);
> +
>  	ret = register_netdev(netdev);

[Severity: Medium]
Can this leave the driver's Wake-on-LAN state inconsistent with the
hardware and USB wakeup settings?

First, lan78xx_bind() pre-sets pdata->wol = WAKE_MAGIC, and after
register_netdev() succeeds, lan78xx_probe() unconditionally calls
device_set_wakeup_enable(&udev->dev, true).  If phylink_ethtool_set_wol()
fails here, phylink and netdev->ethtool->wol_enabled treat WoL as disabled,
while pdata->wol remains WAKE_MAGIC and udev->dev wakeup is still enabled;
calling device_set_wakeup_enable() after register_netdev() also races with
userspace changing WoL immediately after netdev registration.

Second, lan78xx_get_wol() checks USB_CFG_RMT_WKP_ in USB_CFG0 and reports
wol->supported = 0 and wol->wolopts = 0 when clear, but neither
lan78xx_probe() nor lan78xx_set_wol() checks USB_CFG_RMT_WKP_.  When
USB_CFG_RMT_WKP_ is not set, lan78xx_probe() and lan78xx_set_wol() still
enable WoL in phylink, pdata->wol, and udev->dev while lan78xx_get_wol()
reports WoL as unsupported and disabled.

[Severity: High]
This problem wasn't introduced by this patch, but if register_netdev()
fails right below (or if lan78xx_phy_init() fails after
lan78xx_phylink_setup()), the error path calls lan78xx_phy_uninit() ->
phylink_disconnect_phy() without holding rtnl_lock() [1], triggering
ASSERT_RTNL() in phylink_disconnect_phy().

[1] https://sashiko.dev/#/bug/linux-f22d0679-ab9c-4543-b756-3c93ec69c79d

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261108180137.3488023-1-prashantsengar25031@gmail.com?part=1

  reply	other threads:[~2026-11-09 18:02 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-08 18:01 [PATCH net-next] net: usb: lan78xx: use phylink for Wake-on-LAN Prashant Sengar
2026-11-09 18:02 ` sashiko-bot [this message]
2026-11-09 18:43 ` netdev-bot+sashiko
2026-11-10 18:55   ` Prashant Sengar

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-205365@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=prashantsengar25031@gmail.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox