On 03.12.2018 11:43, Anssi Hannula wrote: > On 1.12.2018 0:16, Heiner Kallweit wrote: >> On 30.11.2018 19:45, Anssi Hannula wrote: >>> When a PHY_HALTED phydev is resumed by phy_start(), it is set to >>> PHY_RESUMING to wait for the link to come up. >>> >>> However, if the phydev was put to PHY_HALTED (by e.g. phy_stop()) before >>> autonegotiation was ever started by phy_state_machine(), autonegotiation >>> remains unconfigured, i.e. phy_config_aneg() is never called. >>> >> phy_stop() should only be called once the PHY was started. But it's >> right that we don't have full control over it because misbehaving >> drivers may call phy_stop() even if the PHY hasn't been started yet. > > Having run phy_start() does not guarantee that the phy_state_machine() > has been run at least once, though. > > I was able to reproduce the issue by calling phy_start(); phy_stop(). > Then on the next phy_start() autoneg is not configured. > Right, phy_start() schedules the state machine run, so there is a small window where this can happen. Did you experience this in any real-life scenario?
>> I think phy_error() and phy_stop() should be extended to set the state >> to PHY_HALTED only if the PHY is in a started state, means: >> don't touch the state if it's DOWN, READY, or HALTED. >> In case of phy_error() it's more a precaution, because it's used within >> phylib only and AFAICS it can be triggered from a started state only. > > This wouldn't fix the issue that occurs when phy_stop() is called right > after phy_start(), though, as PHY is in UP state then. > After having removed state AN I was thinking already whether we really need to have separate states READY and HALTED. I think we wouldn't have this scenario if phy_stop() and phy_error() would set the state to READY. Merging the two states requires some upfront work, but I have some ideas in the back of my mind. Overall this should be cleaner than the proposed workaround. >> >> So yes, there is a theoretical issue. But as you wrote already, I think >> there's a better way to deal with it. >> >> For checking whether PHY is in a started state you could use the helper >> which is being discussed here: >> https://marc.info/?l=linux-netdev&m=154360368104946&w=2 >> >> >>> Fix that by going to PHY_UP instead of PHY_RESUMING if autonegotiation >>> has never been configured. >>> >>> Signed-off-by: Anssi Hannula <anssi.hann...@bitwise.fi> >>> --- >>> >>> This doesn't feel as clean is I'd like to, though. Maybe there is a >>> better way? >>> >>> >>> drivers/net/phy/phy.c | 11 ++++++++++- >>> include/linux/phy.h | 2 ++ >>> 2 files changed, 12 insertions(+), 1 deletion(-) >>> >>> diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c >>> index d46858694db9..7a650cce7599 100644 >>> --- a/drivers/net/phy/phy.c >>> +++ b/drivers/net/phy/phy.c >>> @@ -553,6 +553,8 @@ int phy_start_aneg(struct phy_device *phydev) >>> if (err < 0) >>> goto out_unlock; >>> >>> + phydev->autoneg_configured = 1; >>> + >>> if (phydev->state != PHY_HALTED) { >>> if (AUTONEG_ENABLE == phydev->autoneg) { >>> err = phy_check_link_status(phydev); >>> @@ -893,7 +895,14 @@ void phy_start(struct phy_device *phydev) >>> break; >>> } >>> >>> - phydev->state = PHY_RESUMING; >>> + /* if autoneg/forcing was never configured, go back to PHY_UP >>> + * to make that happen >>> + */ >>> + if (!phydev->autoneg_configured) >>> + phydev->state = PHY_UP; >>> + else >>> + phydev->state = PHY_RESUMING; >>> + >>> break; >>> default: >>> break; >>> diff --git a/include/linux/phy.h b/include/linux/phy.h >>> index 8f927246acdb..b306d5ed9d09 100644 >>> --- a/include/linux/phy.h >>> +++ b/include/linux/phy.h >>> @@ -389,6 +389,8 @@ struct phy_device { >>> unsigned loopback_enabled:1; >>> >>> unsigned autoneg:1; >>> + /* autoneg has been configured at least once */ >>> + unsigned autoneg_configured:1; >>> /* The most recently read link state */ >>> unsigned link:1; >>> >>> >