From: Andrew Lunn <andrew@lunn.ch> Date: 2021-10-24 19:48:31
Walter Stoll [off-list ref] reported a race condition
between "ethtool -s eth0 speed 100 duplex full autoneg off" and phylib
reading the current status from the PHY. Both ksetting_get and
ksetting_set fail the take the phydev mutex, and as a result, there is
a small window of time where the phydev members are not self
consistent.
Patch 1 fixes phy_ethtool_ksettings_get by adding the needed lock.
Patches 2 and 3 move code around and perform to refactoring, to allow
patch 4 to fix phy_ethtool_ksettings_set by added the lock.
Thanks go to Walter for the detailed origional report, suggested fix,
and testing of the proposed patches.
Andrew Lunn (4):
phy: phy_ethtool_ksettings_get: Lock the phy for consistency
phy: phy_ethtool_ksettings_set: Move after phy_start_aneg
phy: phy_start_aneg: Add an unlocked version
phy: phy_ethtool_ksettings_set: Lock the PHY while changing settings
drivers/net/phy/phy.c | 140 ++++++++++++++++++++++++------------------
1 file changed, 81 insertions(+), 59 deletions(-)
--
2.33.0
From: Andrew Lunn <andrew@lunn.ch> Date: 2021-10-24 19:48:27
There is a race condition where the PHY state machine can change
members of the phydev structure at the same time userspace requests a
change via ethtool. To prevent this, have phy_ethtool_ksettings_set
take the PHY lock.
Fixes: 2d55173e71b0 ("phy: add generic function to support ksetting support")
Reported-by: Walter Stoll <redacted>
Suggested-by: Walter Stoll <redacted>
Tested-by: Walter Stoll <redacted>
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/phy/phy.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
From: Andrew Lunn <andrew@lunn.ch> Date: 2021-10-24 19:48:31
The PHY structure should be locked while copying information out if
it, otherwise there is no guarantee of self consistency. Without the
lock the PHY state machine could be updating the structure.
Fixes: 2d55173e71b0 ("phy: add generic function to support ksetting support")
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/phy/phy.c | 2 ++
1 file changed, 2 insertions(+)
From: Andrew Lunn <andrew@lunn.ch> Date: 2021-10-24 19:48:31
Split phy_start_aneg into a wrapper which takes the PHY lock, and a
helper doing the real work. This will be needed when
phy_ethtook_ksettings_set takes the lock.
Fixes: 2d55173e71b0 ("phy: add generic function to support ksetting support")
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/phy/phy.c | 30 ++++++++++++++++++++++++------
1 file changed, 24 insertions(+), 6 deletions(-)
From: Andrew Lunn <andrew@lunn.ch> Date: 2021-10-24 19:48:33
This allows it to make use of a helper which assume the PHY is already
locked.
Fixes: 2d55173e71b0 ("phy: add generic function to support ksetting support")
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/phy/phy.c | 106 +++++++++++++++++++++---------------------
1 file changed, 53 insertions(+), 53 deletions(-)
@@ -243,59 +243,6 @@ static void phy_sanitize_settings(struct phy_device *phydev)}}-intphy_ethtool_ksettings_set(structphy_device*phydev,-conststructethtool_link_ksettings*cmd)-{-__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);-u8autoneg=cmd->base.autoneg;-u8duplex=cmd->base.duplex;-u32speed=cmd->base.speed;--if(cmd->base.phy_address!=phydev->mdio.addr)-return-EINVAL;--linkmode_copy(advertising,cmd->link_modes.advertising);--/* We make sure that we don't pass unsupported values in to the PHY */-linkmode_and(advertising,advertising,phydev->supported);--/* Verify the settings we care about. */-if(autoneg!=AUTONEG_ENABLE&&autoneg!=AUTONEG_DISABLE)-return-EINVAL;--if(autoneg==AUTONEG_ENABLE&&linkmode_empty(advertising))-return-EINVAL;--if(autoneg==AUTONEG_DISABLE&&-((speed!=SPEED_1000&&-speed!=SPEED_100&&-speed!=SPEED_10)||-(duplex!=DUPLEX_HALF&&-duplex!=DUPLEX_FULL)))-return-EINVAL;--phydev->autoneg=autoneg;--if(autoneg==AUTONEG_DISABLE){-phydev->speed=speed;-phydev->duplex=duplex;-}--linkmode_copy(phydev->advertising,advertising);--linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,-phydev->advertising,autoneg==AUTONEG_ENABLE);--phydev->master_slave_set=cmd->base.master_slave_cfg;-phydev->mdix_ctrl=cmd->base.eth_tp_mdix_ctrl;--/* Restart the PHY */-phy_start_aneg(phydev);--return0;-}-EXPORT_SYMBOL(phy_ethtool_ksettings_set);-voidphy_ethtool_ksettings_get(structphy_device*phydev,structethtool_link_ksettings*cmd){
@@ -802,6 +749,59 @@ static int phy_poll_aneg_done(struct phy_device *phydev)returnret<0?ret:0;}+intphy_ethtool_ksettings_set(structphy_device*phydev,+conststructethtool_link_ksettings*cmd)+{+__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);+u8autoneg=cmd->base.autoneg;+u8duplex=cmd->base.duplex;+u32speed=cmd->base.speed;++if(cmd->base.phy_address!=phydev->mdio.addr)+return-EINVAL;++linkmode_copy(advertising,cmd->link_modes.advertising);++/* We make sure that we don't pass unsupported values in to the PHY */+linkmode_and(advertising,advertising,phydev->supported);++/* Verify the settings we care about. */+if(autoneg!=AUTONEG_ENABLE&&autoneg!=AUTONEG_DISABLE)+return-EINVAL;++if(autoneg==AUTONEG_ENABLE&&linkmode_empty(advertising))+return-EINVAL;++if(autoneg==AUTONEG_DISABLE&&+((speed!=SPEED_1000&&+speed!=SPEED_100&&+speed!=SPEED_10)||+(duplex!=DUPLEX_HALF&&+duplex!=DUPLEX_FULL)))+return-EINVAL;++phydev->autoneg=autoneg;++if(autoneg==AUTONEG_DISABLE){+phydev->speed=speed;+phydev->duplex=duplex;+}++linkmode_copy(phydev->advertising,advertising);++linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,+phydev->advertising,autoneg==AUTONEG_ENABLE);++phydev->master_slave_set=cmd->base.master_slave_cfg;+phydev->mdix_ctrl=cmd->base.eth_tp_mdix_ctrl;++/* Restart the PHY */+phy_start_aneg(phydev);++return0;+}+EXPORT_SYMBOL(phy_ethtool_ksettings_set);+/***phy_speed_down-setspeedtolowestspeedsupportedbybothlinkpartners*@phydev:thephy_devicestruct