* [PATCH net-next] net: usb: lan78xx: use phylink for Wake-on-LAN @ 2026-11-08 18:01 Prashant Sengar 2026-11-09 18:02 ` sashiko-bot 2026-11-09 18:43 ` netdev-bot+sashiko 0 siblings, 2 replies; 4+ messages in thread From: Prashant Sengar @ 2026-11-08 18:01 UTC (permalink / raw) To: netdev; +Cc: Thangaraj.S, UNGLinuxDriver, andrew+netdev, linux, linux-usb Use phylink's Wake-on-LAN helpers to handle PHY and MAC wakeup capabilities, allowing MAC-based WoL configuration when no PHY is attached. Add a MAC WoL callback and advertise the supported MAC wakeup modes through phylink. Synchronize the effective WoL settings with the driver state used during suspend. Preserve the default magic-packet wakeup configuration and restore the previous USB wakeup state if WoL configuration fails. Tested with QEMU Raw Gadget emulating a PHY-less LAN7801. Verified WoL configuration, unsupported mode rejection, and USB wakeup state transitions. Signed-off-by: Prashant Sengar <prashantsengar25031@gmail.com> --- drivers/net/usb/lan78xx.c | 84 +++++++++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 17 deletions(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index d73002b2c21a..7d67dd150271 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1765,13 +1765,6 @@ static void lan78xx_get_wol(struct net_device *netdev, struct lan78xx_net *dev = netdev_priv(netdev); int ret; u32 buf; - struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); - - if (!netdev->phydev) { - wol->supported = 0; - wol->wolopts = 0; - return; - } if (usb_autopm_get_interface(dev->intf) < 0) return; @@ -1783,8 +1776,7 @@ static void lan78xx_get_wol(struct net_device *netdev, wol->wolopts = 0; } else { if (buf & USB_CFG_RMT_WKP_) { - wol->supported = WAKE_ALL; - wol->wolopts = pdata->wol; + phylink_ethtool_get_wol(dev->phylink, wol); } else { wol->supported = 0; wol->wolopts = 0; @@ -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; exit_pm_put: usb_autopm_put_interface(dev->intf); @@ -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, }; /** @@ -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); if (IS_ERR(phylink)) @@ -4671,6 +4699,10 @@ static void lan78xx_stat_monitor(struct timer_list *t) static int lan78xx_probe(struct usb_interface *intf, const struct usb_device_id *id) { + struct ethtool_wolinfo wol = { + .cmd = ETHTOOL_SWOL, + .wolopts = WAKE_MAGIC, + }; struct usb_host_endpoint *ep_blkin, *ep_blkout, *ep_intr; struct lan78xx_net *dev; struct net_device *netdev; @@ -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); if (ret != 0) { netif_err(dev, probe, netdev, "couldn't register the device\n"); -- 2.55.0 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net-next] net: usb: lan78xx: use phylink for Wake-on-LAN 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 2026-11-09 18:43 ` netdev-bot+sashiko 1 sibling, 0 replies; 4+ messages in thread From: sashiko-bot @ 2026-11-09 18:02 UTC (permalink / raw) To: Prashant Sengar; +Cc: linux-usb 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 ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net-next] net: usb: lan78xx: use phylink for Wake-on-LAN 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 @ 2026-11-09 18:43 ` netdev-bot+sashiko 2026-11-10 18:55 ` Prashant Sengar 1 sibling, 1 reply; 4+ messages in thread From: netdev-bot+sashiko @ 2026-11-09 18:43 UTC (permalink / raw) To: prashantsengar25031 Cc: netdev, Thangaraj.S, UNGLinuxDriver, andrew+netdev, linux, linux-usb, kuba Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider. Critical: 0 · High: 1 · Medium: 2 · Low: 2 If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough). - [High] Adding `.mac_wol_set` makes phylink_mac_supports_wol() true, so phylink_suspend() ignores the driver's `mac_wol=false` argument and… - [Medium] This patch removes WAKE_PHY (`ethtool -s ethX wol p`) for lan78xx devices that have a PHY, and the commit message does not… - [Medium] The commit message says probe 'preserves' the default magic- packet configuration, but the behaviour changes. - [Low] `pdata->wol` has writers that disagree on what it means. - [Low] If phylink_ethtool_set_wol() fails in lan78xx_probe(), for example because an external PHY's set_wol returns an error other than… -- net: usb: lan78xx: use phylink for Wake-on-LAN This patch moves the lan78xx ethtool WoL handlers to phylink_ethtool_get_wol() and phylink_ethtool_set_wol(). It adds a mac_wol_set callback and advertises MAC wake modes through phylink_config. At probe, it now sends a default WAKE_MAGIC configuration through phylink and sets netdev->ethtool->wol_enabled. > 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 [ ... ] > @@ -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; [Severity: Low] What is pdata->wol supposed to hold? The mac_wol_set kernel-doc in include/linux/phylink.h says options the PHY can handle will already have been removed from wolopts, so this stores the MAC-only set. Both callers of phylink_ethtool_set_wol() overwrite pdata->wol right afterwards with the merged PHY+MAC set: lan78xx_set_wol() { ... ret = phylink_ethtool_set_wol(dev->phylink, wol); ... phylink_ethtool_get_wol(dev->phylink, &actual); pdata->wol = actual.wolopts; } and phylink_ethtool_get_wol() does: wol->wolopts |= pl->wolopts_mac; So is this store always dead? lan78xx_suspend() passes the merged value to lan78xx_set_suspend(). phylink_suspend() decides from the MAC-only set instead: mac_wol = !!pl->wolopts_mac; Take an external PHY on LAN7801 whose driver implements get_wol and handles WAKE_MAGIC. There, wolopts_mac would be 0, so phylink calls phylink_stop(), and lan78xx_mac_link_down() stops RX/TX and resets the MAC. The driver would still arm MAC magic-packet detection (WUCSR_MPEN_, SUS_MODE_3) for a mode the PHY handles. It also would not set PMT_CTL_PHY_WAKE_EN_ to pass on the PHY's wake. Can the driver and phylink end up disagreeing about who handles WoL and whether the MAC needs to keep receiving? > + > + 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 setting mac_wol_set leave the link permanently down after a normal system resume? With mac_wol_set set, phylink_mac_supports_wol() returns true, and phylink_suspend() ignores the mac_wol argument from the driver: phylink_suspend() { ... if (phylink_mac_supports_wol(pl)) mac_wol = !!pl->wolopts_mac; if (mac_wol && (!pl->netdev || pl->netdev->ethtool->wol_enabled)) { ... __set_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state); ... } By default, the new probe code sets pl->wolopts_mac = WAKE_MAGIC (lan88xx has no get_wol, so all of it goes to the MAC) and sets wol_enabled to true. So with the interface open, every system suspend through lan78xx_suspend()->phylink_suspend(dev->phylink, false) takes this branch. It sets PHYLINK_DISABLE_MAC_WOL and turns carrier off, but does not stop phylink or phylib. Only phylink_resume() clears that bit, and lan78xx calls it only from lan78xx_reset_resume(). lan78xx_resume() is used when the device keeps power across sleep, and it has no phylink call. After that, phylink_resolve() sees a non-zero phylink_disable_state and forces the link down: if (pl->phylink_disable_state) { pl->link_failed = false; link_state = pl->link_config; link_state.link = false; Before this patch, ip link set down/up recovered from this state, because phylink_stop() sets STOPPED and phylink_start() clears it. phylink_start() only clears PHYLINK_DISABLE_STOPPED, so the MAC_WOL bit now survives ifdown/ifup and ethtool wol d. Won't the interface stay without carrier until a reset_resume or a driver rebind? On the reset_resume path, lan78xx_reset() hard-resets the PHY through PMT_CTL_PHY_RST_. Before, phylink_resume()->phylink_start()->phy_start() brought phylib back up and re-ran aneg configuration. Now phylink_resume() takes the MAC-WoL branch (mac_initial_config plus a re-resolve). phylib was never halted, so it doesn't reprogram the freshly reset PHY. Are user-configured advertisement or forced link modes lost in that case? Runtime autosuspend doesn't trigger this, since lan78xx_open() holds a usb_autopm reference until lan78xx_stop(). The system suspend path alone is enough. > }; [ ... ] > @@ -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; [Severity: Medium] Is WAKE_PHY meant to be left out here? The driver still includes WAKE_PHY in WAKE_ALL, and lan78xx_set_suspend() still handles it: if (wol & WAKE_PHY) { temp_pmt_ctl |= PMT_CTL_PHY_WAKE_EN_; Before the patch, lan78xx_get_wol() reported supported = WAKE_ALL. Now the supported mask comes from phylink_ethtool_get_wol(), which is the PHY driver's get_wol result OR'd with wol_mac_support. The internal PHY uses the lan88xx driver in drivers/net/phy/microchip.c. It has set_wol but no get_wol, so it adds nothing, and WAKE_PHY is never advertised. ethnl_set_wol() then rejects it: if (wol.wolopts & ~wol.supported) { The new wol->wolopts & ~actual.supported check in lan78xx_set_wol() also rejects it with -EINVAL. So ethtool -s ethX wol p, wol pg, and systemd-networkd WakeOnLan=phy all worked before and now fail. The PMT_CTL_PHY_WAKE_EN_ path in lan78xx_set_suspend() can no longer be reached. The commit message doesn't mention this change. The PHY-less LAN7801 testing wouldn't exercise this path. Should WAKE_PHY be added to wol_mac_support, since the MAC implements it? > + pc->wol_phy_legacy = true; > + [ ... ] > @@ -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; [Severity: Medium] The commit message says: Preserve the default magic-packet wakeup configuration Does this code change the default PHY power behaviour, though? Before the patch, lan78xx_bind() only set pdata->wol = WAKE_MAGIC. The PHY was never told about WoL, and netdev->ethtool->wol_enabled stayed false. With wol_phy_legacy = true, phylink_phy_supports_wol() returns true whenever a phydev is attached, so this call reaches phy_ethtool_set_wol(). lan88xx_set_wol() then sets priv->wolopts = WAKE_MAGIC, and external PHYs on LAN7801 get their WoL hardware programmed. With wol_enabled forced to true, phy_suspend() returns -EBUSY: phydev->wol_enabled = phy_may_wakeup(phydev) || (netdev && netdev->ethtool->wol_enabled); if (phydev->wol_enabled && !(phydrv->flags & PHY_ALWAYS_CALL_SUSPEND)) return -EBUSY; and lan88xx_suspend() skips genphy_suspend() while priv->wolopts is set: if (!priv->wolopts) genphy_suspend(phydev); So the PHY would no longer be powered down when phylink_stop()->phy_stop() halts it, including on a plain ip link set down. In that case the !dev_open branch of lan78xx_suspend() disables all wake sources anyway, so keeping the PHY powered doesn't seem to help. Is this new default intended for every lan78xx device with a PHY? > + } > + > + rtnl_unlock(); > + > + if (ret) > + netdev_warn(netdev, "failed to initialize WoL: %d\n", ret); [Severity: Low] What happens to pdata->wol if phylink_ethtool_set_wol() fails here? One example is an external PHY on LAN7801 whose set_wol returns an error other than -EOPNOTSUPP, such as an MDIO or USB control transfer failure. pdata->wol keeps the WAKE_MAGIC value set in lan78xx_bind(). Meanwhile pl->wolopts_mac stays 0 and netdev->ethtool->wol_enabled stays false. lan78xx_get_wol() would then report no active WoL through phylink, but lan78xx_suspend() still does: ret = lan78xx_set_suspend(dev, pdata->wol); and arms magic-packet wake (WUCSR_MPEN_). Should pdata->wol be reset on this failure path so it matches what phylink reports? This can't happen with the internal lan88xx PHY, whose set_wol always returns 0. -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261108180137.3488023-1-prashantsengar25031%40gmail.com ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net-next] net: usb: lan78xx: use phylink for Wake-on-LAN 2026-11-09 18:43 ` netdev-bot+sashiko @ 2026-11-10 18:55 ` Prashant Sengar 0 siblings, 0 replies; 4+ messages in thread From: Prashant Sengar @ 2026-11-10 18:55 UTC (permalink / raw) To: netdev-bot+sashiko Cc: netdev, Thangaraj.S, UNGLinuxDriver, andrew+netdev, linux, linux-usb, kuba Thanks for the detailed review. The review identified valid concerns with PHY/MAC WoL ownership, supported modes, and the default configuration. I'll address these in a v2. I also noted that the normal phylink resume issue has been fixed upstream by commit be9587c47db1. I'll account for that fix and check the remaining reset-resume interaction. pw-bot: cr On Tue, Nov 10, 2026 at 12:13 AM <netdev-bot+sashiko@kernel.org> wrote: > > Thank you for your contribution! Sashiko AI review found 5 potential > issue(s) to consider. > > Critical: 0 · High: 1 · Medium: 2 · Low: 2 > > If you believe none of the issues are legitimate, please reply to each > issue of Medium severity or higher and explain why the AI is wrong. > If any of them are legitimate and you plan to respin, please let patchwork > know by including "pw-bot: cr" as a separate line at the end of your reply > (one such reply per series is enough). > > - [High] Adding `.mac_wol_set` makes phylink_mac_supports_wol() true, so > phylink_suspend() ignores the driver's `mac_wol=false` argument and… > - [Medium] This patch removes WAKE_PHY (`ethtool -s ethX wol p`) for > lan78xx devices that have a PHY, and the commit message does not… > - [Medium] The commit message says probe 'preserves' the default magic- > packet configuration, but the behaviour changes. > - [Low] `pdata->wol` has writers that disagree on what it means. > - [Low] If phylink_ethtool_set_wol() fails in lan78xx_probe(), for > example because an external PHY's set_wol returns an error other than… > > -- > > net: usb: lan78xx: use phylink for Wake-on-LAN > > This patch moves the lan78xx ethtool WoL handlers to > phylink_ethtool_get_wol() and phylink_ethtool_set_wol(). It adds a > mac_wol_set callback and advertises MAC wake modes through > phylink_config. At probe, it now sends a default WAKE_MAGIC configuration > through phylink and sets netdev->ethtool->wol_enabled. > > > 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 > > [ ... ] > > > @@ -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; > > [Severity: Low] > What is pdata->wol supposed to hold? The mac_wol_set kernel-doc in > include/linux/phylink.h says options the PHY can handle will already have > been removed from wolopts, so this stores the MAC-only set. > > Both callers of phylink_ethtool_set_wol() overwrite pdata->wol right > afterwards with the merged PHY+MAC set: > > lan78xx_set_wol() { > ... > ret = phylink_ethtool_set_wol(dev->phylink, wol); > ... > phylink_ethtool_get_wol(dev->phylink, &actual); > pdata->wol = actual.wolopts; > } > > and phylink_ethtool_get_wol() does: > > wol->wolopts |= pl->wolopts_mac; > > So is this store always dead? > > lan78xx_suspend() passes the merged value to lan78xx_set_suspend(). > phylink_suspend() decides from the MAC-only set instead: > > mac_wol = !!pl->wolopts_mac; > > Take an external PHY on LAN7801 whose driver implements get_wol and > handles WAKE_MAGIC. There, wolopts_mac would be 0, so phylink calls > phylink_stop(), and lan78xx_mac_link_down() stops RX/TX and resets the > MAC. > > The driver would still arm MAC magic-packet detection (WUCSR_MPEN_, > SUS_MODE_3) for a mode the PHY handles. It also would not set > PMT_CTL_PHY_WAKE_EN_ to pass on the PHY's wake. > > Can the driver and phylink end up disagreeing about who handles WoL and > whether the MAC needs to keep receiving? > > > + > > + 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 setting mac_wol_set leave the link permanently down after a normal > system resume? > > With mac_wol_set set, phylink_mac_supports_wol() returns true, and > phylink_suspend() ignores the mac_wol argument from the driver: > > phylink_suspend() { > ... > if (phylink_mac_supports_wol(pl)) > mac_wol = !!pl->wolopts_mac; > > if (mac_wol && (!pl->netdev || pl->netdev->ethtool->wol_enabled)) { > ... > __set_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state); > ... > } > > By default, the new probe code sets pl->wolopts_mac = WAKE_MAGIC (lan88xx > has no get_wol, so all of it goes to the MAC) and sets wol_enabled to > true. > > So with the interface open, every system suspend through > lan78xx_suspend()->phylink_suspend(dev->phylink, false) takes this > branch. It sets PHYLINK_DISABLE_MAC_WOL and turns carrier off, but does > not stop phylink or phylib. > > Only phylink_resume() clears that bit, and lan78xx calls it only from > lan78xx_reset_resume(). lan78xx_resume() is used when the device keeps > power across sleep, and it has no phylink call. > > After that, phylink_resolve() sees a non-zero phylink_disable_state and > forces the link down: > > if (pl->phylink_disable_state) { > pl->link_failed = false; > link_state = pl->link_config; > link_state.link = false; > > Before this patch, ip link set down/up recovered from this state, because > phylink_stop() sets STOPPED and phylink_start() clears it. phylink_start() > only clears PHYLINK_DISABLE_STOPPED, so the MAC_WOL bit now survives > ifdown/ifup and ethtool wol d. > > Won't the interface stay without carrier until a reset_resume or a driver > rebind? > > On the reset_resume path, lan78xx_reset() hard-resets the PHY through > PMT_CTL_PHY_RST_. Before, phylink_resume()->phylink_start()->phy_start() > brought phylib back up and re-ran aneg configuration. > > Now phylink_resume() takes the MAC-WoL branch (mac_initial_config plus a > re-resolve). phylib was never halted, so it doesn't reprogram the freshly > reset PHY. > > Are user-configured advertisement or forced link modes lost in that case? > > Runtime autosuspend doesn't trigger this, since lan78xx_open() holds a > usb_autopm reference until lan78xx_stop(). The system suspend path alone > is enough. > > > }; > > [ ... ] > > > @@ -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; > > [Severity: Medium] > Is WAKE_PHY meant to be left out here? The driver still includes WAKE_PHY > in WAKE_ALL, and lan78xx_set_suspend() still handles it: > > if (wol & WAKE_PHY) { > temp_pmt_ctl |= PMT_CTL_PHY_WAKE_EN_; > > Before the patch, lan78xx_get_wol() reported supported = WAKE_ALL. Now > the supported mask comes from phylink_ethtool_get_wol(), which is the PHY > driver's get_wol result OR'd with wol_mac_support. > > The internal PHY uses the lan88xx driver in drivers/net/phy/microchip.c. > It has set_wol but no get_wol, so it adds nothing, and WAKE_PHY is never > advertised. > > ethnl_set_wol() then rejects it: > > if (wol.wolopts & ~wol.supported) { > > The new wol->wolopts & ~actual.supported check in lan78xx_set_wol() also > rejects it with -EINVAL. > > So ethtool -s ethX wol p, wol pg, and systemd-networkd WakeOnLan=phy all > worked before and now fail. The PMT_CTL_PHY_WAKE_EN_ path in > lan78xx_set_suspend() can no longer be reached. > > The commit message doesn't mention this change. The PHY-less LAN7801 > testing wouldn't exercise this path. > > Should WAKE_PHY be added to wol_mac_support, since the MAC implements it? > > > + pc->wol_phy_legacy = true; > > + > > [ ... ] > > > @@ -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; > > [Severity: Medium] > The commit message says: > > Preserve the default magic-packet wakeup configuration > > Does this code change the default PHY power behaviour, though? > > Before the patch, lan78xx_bind() only set pdata->wol = WAKE_MAGIC. The > PHY was never told about WoL, and netdev->ethtool->wol_enabled stayed > false. > > With wol_phy_legacy = true, phylink_phy_supports_wol() returns true > whenever a phydev is attached, so this call reaches > phy_ethtool_set_wol(). lan88xx_set_wol() then sets > priv->wolopts = WAKE_MAGIC, and external PHYs on LAN7801 get their WoL > hardware programmed. > > With wol_enabled forced to true, phy_suspend() returns -EBUSY: > > phydev->wol_enabled = phy_may_wakeup(phydev) || > (netdev && netdev->ethtool->wol_enabled); > if (phydev->wol_enabled && !(phydrv->flags & PHY_ALWAYS_CALL_SUSPEND)) > return -EBUSY; > > and lan88xx_suspend() skips genphy_suspend() while priv->wolopts is set: > > if (!priv->wolopts) > genphy_suspend(phydev); > > So the PHY would no longer be powered down when > phylink_stop()->phy_stop() halts it, including on a plain ip link set > down. In that case the !dev_open branch of lan78xx_suspend() disables all > wake sources anyway, so keeping the PHY powered doesn't seem to help. > > Is this new default intended for every lan78xx device with a PHY? > > > + } > > + > > + rtnl_unlock(); > > + > > + if (ret) > > + netdev_warn(netdev, "failed to initialize WoL: %d\n", ret); > > [Severity: Low] > What happens to pdata->wol if phylink_ethtool_set_wol() fails here? One > example is an external PHY on LAN7801 whose set_wol returns an error > other than -EOPNOTSUPP, such as an MDIO or USB control transfer failure. > > pdata->wol keeps the WAKE_MAGIC value set in lan78xx_bind(). Meanwhile > pl->wolopts_mac stays 0 and netdev->ethtool->wol_enabled stays false. > > lan78xx_get_wol() would then report no active WoL through phylink, but > lan78xx_suspend() still does: > > ret = lan78xx_set_suspend(dev, pdata->wol); > > and arms magic-packet wake (WUCSR_MPEN_). > > Should pdata->wol be reset on this failure path so it matches what > phylink reports? This can't happen with the internal lan88xx PHY, whose > set_wol always returns 0. > > -- > Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261108180137.3488023-1-prashantsengar25031%40gmail.com ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-11-10 18:55 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 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 2026-11-09 18:43 ` netdev-bot+sashiko 2026-11-10 18:55 ` Prashant Sengar
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox