A MAC which cannot restore its hardware after system sleep may need to close the network device instead of calling phylink_resume(). Without MAC Wake-on-LAN, phylink_suspend() has already stopped the PHY and PCS, so another phylink_stop() repeats their shutdown. With MAC Wake-on-LAN, the saved link-up state still needs a matching mac_link_down(), and leaving PHYLINK_DISABLE_MAC_WOL set prevents a subsequent start from resolving the link. Allow phylink_stop() to finish shutdown directly from either suspended state. Leave an already stopped instance alone. For MAC Wake-on-LAN, stop resolution, drain pending resolve work, complete the deferred link-down and clear the WoL disable bit while retaining the stopped bit. This does not restart the PHY, reconfigure the MAC or bring its link up. Fixes: f97493657c63 ("net: phylink: add suspend/resume support") Signed-off-by: James Hilliard --- drivers/net/phy/phylink.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index a1458da8111b..8f9278112623 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -2495,11 +2495,17 @@ EXPORT_SYMBOL_GPL(phylink_start); * * This will synchronously bring down the link if the link is not already * down (in other words, it will trigger a mac_link_down() method call.) + * It may also be called after phylink_suspend() if the MAC cannot resume + * and the network device must be closed instead. */ void phylink_stop(struct phylink *pl) { ASSERT_RTNL(); + /* phylink_suspend() already stops the link without MAC WoL. */ + if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) + return; + if (pl->sfp_bus) sfp_upstream_stop(pl->sfp_bus); if (pl->phydev) @@ -2512,6 +2518,16 @@ void phylink_stop(struct phylink *pl) phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED); + if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) { + /* Finish the link-down deferred by MAC WoL, without restarting. */ + flush_work(&pl->resolve); + mutex_lock(&pl->state_mutex); + if (pl->suspend_link_up) + phylink_link_down(pl); + __clear_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state); + mutex_unlock(&pl->state_mutex); + } + pl->pcs_state = PCS_STATE_DOWN; phylink_pcs_disable(pl->pcs); -- 2.53.0