net: phylink: fix suspend/resume with WoL enabled and link down
authorRussell King (Oracle) <rmk+kernel@armlinux.org.uk>
Wed, 16 Apr 2025 16:16:01 +0000 (17:16 +0100)
committerJakub Kicinski <kuba@kernel.org>
Tue, 22 Apr 2025 23:56:44 +0000 (16:56 -0700)
When WoL is enabled, we update the software state in phylink to
indicate that the link is down, and disable the resolver from
bringing the link back up.

On resume, we attempt to bring the overall state into consistency
by calling the .mac_link_down() method, but this is wrong if the
link was already down, as phylink strictly orders the .mac_link_up()
and .mac_link_down() methods - and this would break that ordering.

Fixes: f97493657c63 ("net: phylink: add suspend/resume support")
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Tested-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Link: https://patch.msgid.link/E1u55Qf-0016RN-PA@rmk-PC.armlinux.org.uk
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/phy/phylink.c

index b68369e2342b270df7dd57761acaf490320d9a44..1bdd5d8bb5b02166298eee4730841f140bfe97da 100644 (file)
@@ -81,6 +81,7 @@ struct phylink {
        unsigned int pcs_state;
 
        bool link_failed;
+       bool suspend_link_up;
        bool major_config_failed;
        bool mac_supports_eee_ops;
        bool mac_supports_eee;
@@ -2545,14 +2546,16 @@ void phylink_suspend(struct phylink *pl, bool mac_wol)
                /* Stop the resolver bringing the link up */
                __set_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state);
 
-               /* Disable the carrier, to prevent transmit timeouts,
-                * but one would hope all packets have been sent. This
-                * also means phylink_resolve() will do nothing.
-                */
-               if (pl->netdev)
-                       netif_carrier_off(pl->netdev);
-               else
+               pl->suspend_link_up = phylink_link_is_up(pl);
+               if (pl->suspend_link_up) {
+                       /* Disable the carrier, to prevent transmit timeouts,
+                        * but one would hope all packets have been sent. This
+                        * also means phylink_resolve() will do nothing.
+                        */
+                       if (pl->netdev)
+                               netif_carrier_off(pl->netdev);
                        pl->old_link_state = false;
+               }
 
                /* We do not call mac_link_down() here as we want the
                 * link to remain up to receive the WoL packets.
@@ -2603,15 +2606,18 @@ void phylink_resume(struct phylink *pl)
        if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) {
                /* Wake-on-Lan enabled, MAC handling */
 
-               /* Call mac_link_down() so we keep the overall state balanced.
-                * Do this under the state_mutex lock for consistency. This
-                * will cause a "Link Down" message to be printed during
-                * resume, which is harmless - the true link state will be
-                * printed when we run a resolve.
-                */
-               mutex_lock(&pl->state_mutex);
-               phylink_link_down(pl);
-               mutex_unlock(&pl->state_mutex);
+               if (pl->suspend_link_up) {
+                       /* Call mac_link_down() so we keep the overall state
+                        * balanced. Do this under the state_mutex lock for
+                        * consistency. This will cause a "Link Down" message
+                        * to be printed during resume, which is harmless -
+                        * the true link state will be printed when we run a
+                        * resolve.
+                        */
+                       mutex_lock(&pl->state_mutex);
+                       phylink_link_down(pl);
+                       mutex_unlock(&pl->state_mutex);
+               }
 
                /* Re-apply the link parameters so that all the settings get
                 * restored to the MAC.