diff options
author | Russell King <rmk+kernel@armlinux.org.uk> | 2020-06-23 19:47:29 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2020-06-24 06:53:28 +0300 |
commit | 2e919bc446faee429ac862a6cdb5e40017051f6b (patch) | |
tree | 80a99ec14173d14095e261890736496170cebe8f /drivers/net/phy/phylink.c | |
parent | c718af2d00a37587b09e5958d142da7569f3d55b (diff) | |
download | linux-2e919bc446faee429ac862a6cdb5e40017051f6b.tar.xz |
net: phylink: ensure manual pause mode configuration takes effect
We have been relying on link events and mac_config() when the manual
pause modes are changed. With recent developments, such as moving
the programming of link state to mac_link_up(), this no longer works.
To ensure that we update the MAC, we must generate a link-down followed
by a link-up event; we can do that by setting mac_link_dropped and
triggering a resolve.
Fixes: 91a208f2185a ("net: phylink: propagate resolved link config via mac_link_up()")
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/phy/phylink.c')
-rw-r--r-- | drivers/net/phy/phylink.c | 27 |
1 files changed, 22 insertions, 5 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 453f9a399bb1..3b7c70e6c5dd 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1463,6 +1463,8 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, struct ethtool_pauseparam *pause) { struct phylink_link_state *config = &pl->link_config; + bool manual_changed; + int pause_state; ASSERT_RTNL(); @@ -1477,15 +1479,15 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, !pause->autoneg && pause->rx_pause != pause->tx_pause) return -EINVAL; - mutex_lock(&pl->state_mutex); - config->pause = 0; + pause_state = 0; if (pause->autoneg) - config->pause |= MLO_PAUSE_AN; + pause_state |= MLO_PAUSE_AN; if (pause->rx_pause) - config->pause |= MLO_PAUSE_RX; + pause_state |= MLO_PAUSE_RX; if (pause->tx_pause) - config->pause |= MLO_PAUSE_TX; + pause_state |= MLO_PAUSE_TX; + mutex_lock(&pl->state_mutex); /* * See the comments for linkmode_set_pause(), wrt the deficiencies * with the current implementation. A solution to this issue would @@ -1502,6 +1504,12 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, linkmode_set_pause(config->advertising, pause->tx_pause, pause->rx_pause); + manual_changed = (config->pause ^ pause_state) & MLO_PAUSE_AN || + (!(pause_state & MLO_PAUSE_AN) && + (config->pause ^ pause_state) & MLO_PAUSE_TXRX_MASK); + + config->pause = pause_state; + if (!pl->phydev && !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) phylink_pcs_config(pl, true, &pl->link_config); @@ -1517,6 +1525,15 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, phy_set_asym_pause(pl->phydev, pause->rx_pause, pause->tx_pause); + /* If the manual pause settings changed, make sure we trigger a + * resolve to update their state; we can not guarantee that the + * link will cycle. + */ + if (manual_changed) { + pl->mac_link_dropped = true; + phylink_run_resolve(pl); + } + return 0; } EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam); |