Skip to content

Commit

Permalink
net: phylink: improve clause 45 PHY ksettings_set implementation
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Russell King authored and David S. Miller committed Dec 17, 2019
1 parent 6f6dded commit 5d57c32
Showing 1 changed file with 53 additions and 31 deletions.
84 changes: 53 additions & 31 deletions drivers/net/phy/phylink.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down

0 comments on commit 5d57c32

Please sign in to comment.