diff options
Diffstat (limited to 'drivers/net/phy/phylink.c')
| -rw-r--r-- | drivers/net/phy/phylink.c | 30 | 
1 files changed, 29 insertions, 1 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index a1464b764d4d..0a0abe8e4be0 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1607,6 +1607,32 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,  	if (config.an_enabled && phylink_is_empty_linkmode(config.advertising))  		return -EINVAL; +	/* If this link is with an SFP, ensure that changes to advertised modes +	 * also cause the associated interface to be selected such that the +	 * link can be configured correctly. +	 */ +	if (pl->sfp_port && pl->sfp_bus) { +		config.interface = sfp_select_interface(pl->sfp_bus, +							config.advertising); +		if (config.interface == PHY_INTERFACE_MODE_NA) { +			phylink_err(pl, +				    "selection of interface failed, advertisement %*pb\n", +				    __ETHTOOL_LINK_MODE_MASK_NBITS, +				    config.advertising); +			return -EINVAL; +		} + +		/* Revalidate with the selected interface */ +		linkmode_copy(support, pl->supported); +		if (phylink_validate(pl, support, &config)) { +			phylink_err(pl, "validation of %s/%s with support %*pb failed\n", +				    phylink_an_mode_str(pl->cur_link_an_mode), +				    phy_modes(config.interface), +				    __ETHTOOL_LINK_MODE_MASK_NBITS, support); +			return -EINVAL; +		} +	} +  	mutex_lock(&pl->state_mutex);  	pl->link_config.speed = config.speed;  	pl->link_config.duplex = config.duplex; @@ -2186,7 +2212,9 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode,  	if (phy_interface_mode_is_8023z(iface) && pl->phydev)  		return -EINVAL; -	changed = !linkmode_equal(pl->supported, support); +	changed = !linkmode_equal(pl->supported, support) || +		  !linkmode_equal(pl->link_config.advertising, +				  config.advertising);  	if (changed) {  		linkmode_copy(pl->supported, support);  		linkmode_copy(pl->link_config.advertising, config.advertising);  | 
