net: phylink: improve clause 45 PHY ksettings_set implementation
authorRussell King <rmk+kernel@armlinux.org.uk>
Fri, 13 Dec 2019 18:22:02 +0000 (18:22 +0000)
committerDavid S. Miller <davem@davemloft.net>
Tue, 17 Dec 2019 21:26:33 +0000 (13:26 -0800)
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>
drivers/net/phy/phylink.c

index 1e0e32c466ee4c38b348a6e0c1ed9ec0a261564e..8f3dc27db95d39ea397f706f868f9f4d7082920d 100644 (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;
 }