net: phylink: improve clause 45 PHY ksettings_set implementation

While testing ethtool with the Methode DM7052 module, it was noticed
that attempting to set the advertising mask results in the mask being
truncated to the support offered by the currently chosen PHY interface
mode.

When a PHY dynamically changes the PHY interface mode, limiting the
advertising mask in this way is not correct - if the PHY happened to
negotiate 10GBASE-T, and selected 10GBASE-R as the host interface, we
don't want to restrict the advertisement to just 10GBASE-* modes.

Rework setting the advertisement to take account of this; do not pass
the requested advertisement through phylink_validate(), but rely on
the advertisement restriction (supported mask) set when the PHY was
initially setup.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Russell King 2019-12-13 18:22:02 +00:00 committed by David S. Miller
parent 6f6dded138
commit 5d57c32758
1 changed files with 53 additions and 31 deletions

View File

@ -1230,44 +1230,66 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
__set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising);
}
if (phylink_validate(pl, support, &config))
return -EINVAL;
/* If autonegotiation is enabled, we must have an advertisement */
if (config.an_enabled && phylink_is_empty_linkmode(config.advertising))
return -EINVAL;
our_kset = *kset;
linkmode_copy(our_kset.link_modes.advertising, config.advertising);
our_kset.base.speed = config.speed;
our_kset.base.duplex = config.duplex;
/* If we have a PHY, configure the phy */
if (pl->phydev) {
/* If we have a PHY, we process the kset change via phylib.
* phylib will call our link state function if the PHY
* parameters have changed, which will trigger a resolve
* and update the MAC configuration.
*/
our_kset = *kset;
linkmode_copy(our_kset.link_modes.advertising,
config.advertising);
our_kset.base.speed = config.speed;
our_kset.base.duplex = config.duplex;
ret = phy_ethtool_ksettings_set(pl->phydev, &our_kset);
if (ret)
return ret;
}
mutex_lock(&pl->state_mutex);
/* Configure the MAC to match the new settings */
linkmode_copy(pl->link_config.advertising, our_kset.link_modes.advertising);
pl->link_config.interface = config.interface;
pl->link_config.speed = our_kset.base.speed;
pl->link_config.duplex = our_kset.base.duplex;
pl->link_config.an_enabled = our_kset.base.autoneg != AUTONEG_DISABLE;
mutex_lock(&pl->state_mutex);
/* Save the new configuration */
linkmode_copy(pl->link_config.advertising,
our_kset.link_modes.advertising);
pl->link_config.interface = config.interface;
pl->link_config.speed = our_kset.base.speed;
pl->link_config.duplex = our_kset.base.duplex;
pl->link_config.an_enabled = our_kset.base.autoneg !=
AUTONEG_DISABLE;
mutex_unlock(&pl->state_mutex);
} else {
/* For a fixed link, this isn't able to change any parameters,
* which just leaves inband mode.
*/
if (phylink_validate(pl, support, &config))
return -EINVAL;
/* If we have a PHY, phylib will call our link state function if the
* mode has changed, which will trigger a resolve and update the MAC
* configuration. For a fixed link, this isn't able to change any
* parameters, which just leaves inband mode.
*/
if (pl->cur_link_an_mode == MLO_AN_INBAND &&
!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) {
phylink_mac_config(pl, &pl->link_config);
phylink_mac_an_restart(pl);
/* If autonegotiation is enabled, we must have an advertisement */
if (config.an_enabled &&
phylink_is_empty_linkmode(config.advertising))
return -EINVAL;
mutex_lock(&pl->state_mutex);
linkmode_copy(pl->link_config.advertising, config.advertising);
pl->link_config.interface = config.interface;
pl->link_config.speed = config.speed;
pl->link_config.duplex = config.duplex;
pl->link_config.an_enabled = kset->base.autoneg !=
AUTONEG_DISABLE;
if (pl->cur_link_an_mode == MLO_AN_INBAND &&
!test_bit(PHYLINK_DISABLE_STOPPED,
&pl->phylink_disable_state)) {
/* If in 802.3z mode, this updates the advertisement.
*
* If we are in SGMII mode without a PHY, there is no
* advertisement; the only thing we have is the pause
* modes which can only come from a PHY.
*/
phylink_mac_config(pl, &pl->link_config);
phylink_mac_an_restart(pl);
}
mutex_unlock(&pl->state_mutex);
}
mutex_unlock(&pl->state_mutex);
return 0;
}