net: phy: improve and simplify phylib state machine
Subsystems (3)
Heiner Kallweit
maintainercover net: phy: improve and simplify phylib state machine
This patch series is based on two axioms:
- During autoneg a PHY always reports the link being down
- Info in clause 22/45 registers doesn't allow to differentiate between these two states:
1. Link is physically down
2. A link partner is connected and PHY is autonegotiating In both cases "link up" and "aneg finished" bits aren't set. One consequence is that having separate states PHY_NOLINK and PHY_AN isn't needed.
By using these two axioms the state machine can be significantly simplified.
Heiner Kallweit (5):
net: phy: remove useless check in state machine case PHY_NOLINK
net: phy: remove useless check in state machine case PHY_RESUMING
net: phy: add phy_check_link_status
net: phy: remove state PHY_AN
net: phy: use phy_check_link_status in more places in the state machine
drivers/net/phy/phy.c | 172 +++++++++++------------------------------- include/linux/phy.h | 19 +---- 2 files changed, 46 insertions(+), 145 deletions(-)
Signature · 1 line
2.19.1
Heiner Kallweit
maintainerPATCH 1/5 net: phy: remove useless check in state machine case PHY_NOLINK
If aneg is enabled and the PHY reports the link as up then definitely aneg finished successfully. Therefore this check is useless and can be removed.
--- drivers/net/phy/phy.c | 11 ----------- 1 file changed, 11 deletions(-)
drivers/net/phy/phy.c +0 -11
@@ -970,17 +970,6 @@ void phy_state_machine(struct work_struct *work)break ;if (phydev->link) { -if (AUTONEG_ENABLE == phydev->autoneg) { - err =phy_aneg_done (phydev); -if (err <0 ) -break ; - -if (!err) { - phydev->state =PHY_AN ; - phydev->link_timeout =PHY_AN_TIMEOUT ; -break ; - } - } phydev->state =PHY_RUNNING ;phy_link_up (phydev); }
Signature · 1 line
2.19.1
Heiner Kallweit
maintainerPATCH 2/5 net: phy: remove useless check in state machine case PHY_RESUMING
In reply to Heiner KallweitIf aneg isn't finished yet then the PHY reports the link as down. There's no benefit in setting the state to PHY_AN because the next state machine run would set the status to PHY_NOLINK anyway (except in the meantime aneg has been finished and link is up). Therefore we can set the state to PHY_RUNNING or PHY_NOLINK directly.
In addition change the do_carrier parameter in phy_link_down() to true. If carrier was marked as up before (what should never be the case because PHY was in state PHY_HALTED before) then we should mark it as down now.
--- drivers/net/phy/phy.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-)
drivers/net/phy/phy.c +1 -12
@@ -1022,17 +1022,6 @@ void phy_state_machine(struct work_struct *work) }break ;case PHY_RESUMING : -if (AUTONEG_ENABLE == phydev->autoneg) { - err =phy_aneg_done (phydev); -if (err <0 ) { -break ; - }else if (!err) { - phydev->state =PHY_AN ; - phydev->link_timeout =PHY_AN_TIMEOUT ; -break ; - } - } - err =phy_read_status (phydev);if (err)break ;
@@ -1042,7 +1031,7 @@ void phy_state_machine(struct work_struct *work)phy_link_up (phydev); }else { phydev->state =PHY_NOLINK ; -phy_link_down (phydev, false); +phy_link_down (phydev, true); }break ; }
Signature · 1 line
2.19.1
Heiner Kallweit
maintainerPATCH 3/5 net: phy: add phy_check_link_status
In reply to Heiner KallweitIn few places in the state machine the state is set to PHY_RUNNING or PHY_NOLINK after doing a phy_read_status(). So factor this out to phy_check_link_status().
First use it in phy_start_aneg(): By setting the state to PHY_RUNNING or PHY_NOLINK directly we can remove the code to handle the case that we're using interrupts and aneg was finished already.
Definition of phy_link_up and phy_link_down needs to be moved because they are called in the new function.
--- drivers/net/phy/phy.c | 70 ++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 30 deletions(-)
drivers/net/phy/phy.c +40 -30
@@ -62,6 +62,17 @@ static const char *phy_state_to_str(enum phy_state st)return NULL ; } +static void phy_link_up (struct phy_device *phydev) +{ + phydev->phy_link_change (phydev, true, true); +phy_led_trigger_change_speed (phydev); +} + +static void phy_link_down (struct phy_device *phydev,bool do_carrier) +{ + phydev->phy_link_change (phydev, false, do_carrier); +phy_led_trigger_change_speed (phydev); +}/** * phy_print_status - Convenience function to print out the current phy status
@@ -493,6 +504,34 @@ static int phy_config_aneg(struct phy_device *phydev)return genphy_config_aneg (phydev); } +/** +* phy_check_link_status - check link status and set state accordingly +* @phydev: the phy_device struct +* +* Description: Check for link and whether autoneg was triggered / is running +* and set state accordingly +*/ +static int phy_check_link_status (struct phy_device *phydev) +{ +int err; + +WARN_ON (!mutex_is_locked (&phydev->lock)); + + err =phy_read_status (phydev); +if (err) +return err; + +if (phydev->link && phydev->state !=PHY_RUNNING ) { + phydev->state =PHY_RUNNING ; +phy_link_up (phydev); + }else if (!phydev->link && phydev->state !=PHY_NOLINK ) { + phydev->state =PHY_NOLINK ; +phy_link_down (phydev, true); + } + +return 0 ; +} +/** * phy_start_aneg - start auto-negotiation for this PHY device * @phydev: the phy_device struct
@@ -504,7 +543,6 @@ static int phy_config_aneg(struct phy_device *phydev)*/ int phy_start_aneg (struct phy_device *phydev) { -bool trigger =0 ;int err;if (!phydev->drv)
@@ -524,32 +562,16 @@ int phy_start_aneg(struct phy_device *phydev)if (phydev->state !=PHY_HALTED ) {if (AUTONEG_ENABLE == phydev->autoneg) { - phydev->state =PHY_AN ; - phydev->link_timeout =PHY_AN_TIMEOUT ; + err =phy_check_link_status (phydev); }else { phydev->state =PHY_FORCING ; phydev->link_timeout =PHY_FORCE_TIMEOUT ; } } -/* Re-schedule a PHY state machine to check PHY status because -* negotiation may already be done and aneg interrupt may not be -* generated. -*/ -if (!phy_polling_mode (phydev) && phydev->state ==PHY_AN ) { - err =phy_aneg_done (phydev); -if (err >0 ) { - trigger = true; - err =0 ; - } - } - out_unlock:mutex_unlock (&phydev->lock); -if (trigger) -phy_trigger_machine (phydev); -return err; }EXPORT_SYMBOL (phy_start_aneg);
@@ -893,18 +915,6 @@ void phy_start(struct phy_device *phydev) }EXPORT_SYMBOL (phy_start); -static void phy_link_up (struct phy_device *phydev) -{ - phydev->phy_link_change (phydev, true, true); -phy_led_trigger_change_speed (phydev); -} - -static void phy_link_down (struct phy_device *phydev,bool do_carrier) -{ - phydev->phy_link_change (phydev, false, do_carrier); -phy_led_trigger_change_speed (phydev); -} -/** * phy_state_machine - Handle the state machine * @work: work_struct that describes the work to be done
Signature · 1 line
2.19.1
Heiner Kallweit
maintainerPATCH 4/5 net: phy: remove state PHY_AN
In reply to Heiner KallweitAfter the recent changes in the state machine state PHY_AN isn't used any longer and can be removed.
--- drivers/net/phy/phy.c | 27 --------------------------- include/linux/phy.h | 19 +------------------ 2 files changed, 1 insertion(+), 45 deletions(-)
drivers/net/phy/phy.c +0 -27
@@ -50,7 +50,6 @@ static const char *phy_state_to_str(enum phy_state st) PHY_STATE_STR(READY) PHY_STATE_STR(PENDING) PHY_STATE_STR(UP) - PHY_STATE_STR(AN) PHY_STATE_STR(RUNNING) PHY_STATE_STR(NOLINK) PHY_STATE_STR(FORCING)
@@ -944,32 +943,6 @@ void phy_state_machine(struct work_struct *work)case PHY_UP : needs_aneg = true; - phydev->link_timeout =PHY_AN_TIMEOUT ; - -break ; -case PHY_AN : - err =phy_read_status (phydev); -if (err <0 ) -break ; - -/* If the link is down, give up on negotiation for now */ -if (!phydev->link) { - phydev->state =PHY_NOLINK ; -phy_link_down (phydev, true); -break ; - } - -/* Check if negotiation is done. Break if there's an error */ - err =phy_aneg_done (phydev); -if (err <0 ) -break ; - -/* If AN is done, we're running */ -if (err >0 ) { - phydev->state =PHY_RUNNING ; -phy_link_up (phydev); - }else if (0 == phydev->link_timeout--) - needs_aneg = true;break ;case PHY_NOLINK :if (!phy_polling_mode (phydev))
include/linux/phy.h +1 -18
@@ -178,7 +178,6 @@ static inline const char *phy_modes(phy_interface_t interface)#define PHY_INIT_TIMEOUT 100000#define PHY_STATE_TIME 1#define PHY_FORCE_TIMEOUT 10 -#define PHY_AN_TIMEOUT 10#define PHY_MAX_ADDR 32
@@ -297,24 +296,10 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);* * UP: The PHY and attached device are ready to do work. * Interrupts should be started here. -* - timer moves to AN -* -* AN: The PHY is currently negotiating the link state. Link is -* therefore down for now. phy_timer will set this state when it -* detects the state is UP. config_aneg will set this state -* whenever called with phydev->autoneg set to AUTONEG_ENABLE. -* - If autonegotiation finishes, but there's no link, it sets -* the state to NOLINK. -* - If aneg finishes with link, it sets the state to RUNNING, -* and calls adjust_link -* - If autonegotiation did not finish after an arbitrary amount -* of time, autonegotiation should be tried again if the PHY -* supports "magic" autonegotiation (back to AN) -* - If it didn't finish, and no magic_aneg, move to FORCING. +* - timer moves to NOLINK or RUNNING * * NOLINK: PHY is up, but not currently plugged in. * - If the timer notes that the link comes back, we move to RUNNING -* - config_aneg moves to AN * - phy_stop moves to HALTED * * FORCING: PHY is being configured with forced settings
@@ -329,7 +314,6 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);* link state is polled every other cycle of this state machine, * which makes it every other second) * - irq will set CHANGELINK -* - config_aneg will set AN * - phy_stop moves to HALTED * * CHANGELINK: PHY experienced a change in link state
@@ -353,7 +337,6 @@ enum phy_state {PHY_READY ,PHY_PENDING ,PHY_UP , -PHY_AN ,PHY_RUNNING ,PHY_NOLINK , PHY_FORCING,
Signature · 1 line
2.19.1
Heiner Kallweit
maintainerPATCH 5/5 net: phy: use phy_check_link_status in more places in the state machine
In reply to Heiner KallweitUse phy_check_link_status in more places in the state machine.
--- drivers/net/phy/phy.c | 53 ++++--------------------------------------- 1 file changed, 5 insertions(+), 48 deletions(-)
drivers/net/phy/phy.c +5 -48
@@ -945,17 +945,13 @@ void phy_state_machine(struct work_struct *work)break ;case PHY_NOLINK : +case PHY_RUNNING :if (!phy_polling_mode (phydev))break ; - - err =phy_read_status (phydev); -if (err) -break ; - -if (phydev->link) { - phydev->state =PHY_RUNNING ; -phy_link_up (phydev); - } +/* fall through */ +case PHY_CHANGELINK : +case PHY_RESUMING : + err =phy_check_link_status (phydev);break ;case PHY_FORCING : err =genphy_update_link (phydev);
@@ -971,32 +967,6 @@ void phy_state_machine(struct work_struct *work)phy_link_down (phydev, false); }break ; -case PHY_RUNNING : -if (!phy_polling_mode (phydev)) -break ; - - err =phy_read_status (phydev); -if (err) -break ; - -if (!phydev->link) { - phydev->state =PHY_NOLINK ; -phy_link_down (phydev, true); - } -break ; -case PHY_CHANGELINK : - err =phy_read_status (phydev); -if (err) -break ; - -if (phydev->link) { - phydev->state =PHY_RUNNING ; -phy_link_up (phydev); - }else { - phydev->state =PHY_NOLINK ; -phy_link_down (phydev, true); - } -break ;case PHY_HALTED :if (phydev->link) { phydev->link =0 ;
@@ -1004,19 +974,6 @@ void phy_state_machine(struct work_struct *work) do_suspend = true; }break ; -case PHY_RESUMING : - err =phy_read_status (phydev); -if (err) -break ; - -if (phydev->link) { - phydev->state =PHY_RUNNING ; -phy_link_up (phydev); - }else { - phydev->state =PHY_NOLINK ; -phy_link_down (phydev, true); - } -break ; }mutex_unlock (&phydev->lock);
Signature · 1 line
2.19.1
Andrew Lunn
maintainerQuoted 3 lines · Heiner Kallweit
This patch series is based on two axioms:
- During autoneg a PHY always reports the link being down
Hi Heiner
I think that is a risky assumption to make.
What happens if this assumption is incorrect?
Andrew
Heiner Kallweit
maintainerQuoted 9 lines · Andrew Lunn
Quoted 3 lines · Heiner Kallweit
This patch series is based on two axioms:
- During autoneg a PHY always reports the link being down
Hi Heiner
I think that is a risky assumption to make.
I wasn't sure initially too (found no clear rule in 802.3 clause 22) and therefore asked around. Florian agrees to the assumption, see here: https://www.spinics.net/lists/netdev/msg519242.html
If a PHY reports the link as up then every user would assume that data can be transferred. But that's not the case during aneg. Therefore reporting the link as up during aneg wouldn't make sense.
Quoted 2 lines
What happens if this assumption is incorrect?
Then we have to flush this patch series down the drain ;) At least I would have to check in detail which parts need to be changed. I clearly mention the assumptions so that every reviewer can check whether he agrees.
Quoted 2 lines
Andrew
Heiner
Andrew Lunn
maintainerQuoted 10 lines · Heiner Kallweit
Quoted 9 lines · Andrew Lunn
Quoted 3 lines · Heiner Kallweit
This patch series is based on two axioms:
- During autoneg a PHY always reports the link being down
Hi Heiner
I think that is a risky assumption to make.
Quoted 7 lines
I wasn't sure initially too (found no clear rule in 802.3 clause 22) and therefore asked around. Florian agrees to the assumption, see here: https://www.spinics.net/lists/netdev/msg519242.html
If a PHY reports the link as up then every user would assume that data can be transferred. But that's not the case during aneg. Therefore reporting the link as up during aneg wouldn't make sense.
Hi Heiner
If auto-neg has already been completed once before, i can see a lazy hardware designed not reporting link down, or at least, not until auto-neg actually fails.
And what about if link is down for too short a time for us to notice? I've seen some code fail because the kernel went off and did something else for too long, and a state change was missed.
Quoted 6 lines
Quoted 2 lines
What happens if this assumption is incorrect?
Then we have to flush this patch series down the drain ;) At least I would have to check in detail which parts need to be changed. I clearly mention the assumptions so that every reviewer can check whether he agrees.
Thanks for doing that. I want to be happy this is safe, and not going to introduce regressions.
Andrew
Heiner Kallweit
maintainerQuoted 19 lines · Andrew Lunn
Quoted 10 lines · Heiner Kallweit
Quoted 9 lines · Andrew Lunn
Quoted 3 lines · Heiner Kallweit
This patch series is based on two axioms:
- During autoneg a PHY always reports the link being down
Hi Heiner
I think that is a risky assumption to make.
Quoted 7 lines
I wasn't sure initially too (found no clear rule in 802.3 clause 22) and therefore asked around. Florian agrees to the assumption, see here: https://www.spinics.net/lists/netdev/msg519242.html
If a PHY reports the link as up then every user would assume that data can be transferred. But that's not the case during aneg. Therefore reporting the link as up during aneg wouldn't make sense.
Quoted 6 lines
Hi Heiner
If auto-neg has already been completed once before, i can see a lazy hardware designed not reporting link down, or at least, not until auto-neg actually fails.
"aneg finished" flag means that the aneg parameters in the register set are valid. Once the link goes down that's not necessarily the case any longer. E.g. some PHYs have an "auto speed down" feature and reduce the speed to save power once they detect the link is down. Of course I can not rule out that there are broken designs (or as you stated more politely: lazy designs) out there. But in this case I assume we would see issues already. And we would have to think about whether we want to support such broken / lazy designs in phylib.
Quoted 4 lines
And what about if link is down for too short a time for us to notice? I've seen some code fail because the kernel went off and did something else for too long, and a state change was missed.
This is a case we have already, independent of my change. genphy_update_link() reads BMSR twice, thus ignoring potential latched info about a temporary link failure. When polling phylib ignores everything that happens between two poll intervals.
Quoted 12 lines
Quoted 6 lines
Quoted 2 lines
What happens if this assumption is incorrect?
Then we have to flush this patch series down the drain ;) At least I would have to check in detail which parts need to be changed. I clearly mention the assumptions so that every reviewer can check whether he agrees.
Thanks for doing that. I want to be happy this is safe, and not going to introduce regressions.
Andrew
Heiner Kallweit
maintainerQuoted 26 lines · Heiner Kallweit
Quoted 19 lines · Andrew Lunn
Quoted 10 lines · Heiner Kallweit
Quoted 9 lines · Andrew Lunn
Quoted 3 lines · Heiner Kallweit
This patch series is based on two axioms:
- During autoneg a PHY always reports the link being down
Hi Heiner
I think that is a risky assumption to make.
Quoted 7 lines
I wasn't sure initially too (found no clear rule in 802.3 clause 22) and therefore asked around. Florian agrees to the assumption, see here: https://www.spinics.net/lists/netdev/msg519242.html
If a PHY reports the link as up then every user would assume that data can be transferred. But that's not the case during aneg. Therefore reporting the link as up during aneg wouldn't make sense.
Quoted 6 lines
Hi Heiner
If auto-neg has already been completed once before, i can see a lazy hardware designed not reporting link down, or at least, not until auto-neg actually fails.
Quoted 9 lines
"aneg finished" flag means that the aneg parameters in the register set are valid. Once the link goes down that's not necessarily the case any longer. E.g. some PHYs have an "auto speed down" feature and reduce the speed to save power once they detect the link is down. Of course I can not rule out that there are broken designs (or as you stated more politely: lazy designs) out there. But in this case I assume we would see issues already. And we would have to think about whether we want to support such broken / lazy designs in phylib.
Had one more look at the changes to check what happens if "link up" and "aneg done" are not in sync.
When link goes down the changes don't change current behavior. We just check the "link up" bit.
When link goes up and aneg isn't finished yet, then we would report "link is up" earlier to userspace than we do now. If userspace starts some network activity based on the "link up" event then they may fail. But it really would be a major flaw if a PHY signals "link up" whilst it's not actually ready yet to transfer data.
In case of such a broken design we would have issues with the current code already, at least if interrupts are used. The "link up" interrupt would cause the state machine to go to PHY_CHANGELINK which doesn't check for aneg status.
Quoted 22 lines
Quoted 4 lines
And what about if link is down for too short a time for us to notice? I've seen some code fail because the kernel went off and did something else for too long, and a state change was missed.
This is a case we have already, independent of my change.
genphy_update_link()reads BMSR twice, thus ignoring potential latched info about a temporary link failure. When polling phylib ignores everything that happens between two poll intervals.Quoted 12 lines
Quoted 6 lines
Quoted 2 lines
What happens if this assumption is incorrect?
Then we have to flush this patch series down the drain ;) At least I would have to check in detail which parts need to be changed. I clearly mention the assumptions so that every reviewer can check whether he agrees.
Thanks for doing that. I want to be happy this is safe, and not going to introduce regressions.
Andrew
David Miller
maintainerFrom: Heiner Kallweit <[email protected]>
Date: Wed, 7 Nov 2018 20:41:52 +0100
Quoted 12 lines
This patch series is based on two axioms:
- During autoneg a PHY always reports the link being down
- Info in clause 22/45 registers doesn't allow to differentiate between these two states:
1. Link is physically down
2. A link partner is connected and PHY is autonegotiating In both cases "link up" and "aneg finished" bits aren't set. One consequence is that having separate statesPHY_NOLINKandPHY_ANisn't needed.
Quoted 2 lines
By using these two axioms the state machine can be significantly simplified.
So how are we going to move forward on this?
Maybe we can apply this series and just watch carefully for any problems that get reported or are found?
Quoted 15 lines · David Miller
From: Heiner Kallweit <[email protected]>
Date: Wed, 7 Nov 2018 20:41:52 +0100Quoted 12 lines
This patch series is based on two axioms:
- During autoneg a PHY always reports the link being down
- Info in clause 22/45 registers doesn't allow to differentiate between these two states:
1. Link is physically down
2. A link partner is connected and PHY is autonegotiating In both cases "link up" and "aneg finished" bits aren't set. One consequence is that having separate statesPHY_NOLINKandPHY_ANisn't needed.
Quoted 7 lines
Quoted 2 lines
By using these two axioms the state machine can be significantly simplified.
So how are we going to move forward on this?
Maybe we can apply this series and just watch carefully for any problems that get reported or are found?
Given Heiner is always responsive and taking care of fixing what might be/have broken, no objections with me on that.
Signature · 1 line
Florian
Andrew Lunn
maintainerQuoted 19 lines · Florian Fainelli
Quoted 18 lines · David Miller
From: Heiner Kallweit <[email protected]>
Date: Wed, 7 Nov 2018 20:41:52 +0100Quoted 14 lines
This patch series is based on two axioms:
- During autoneg a PHY always reports the link being down
- Info in clause 22/45 registers doesn't allow to differentiate between these two states:
1. Link is physically down
2. A link partner is connected and PHY is autonegotiating In both cases "link up" and "aneg finished" bits aren't set. One consequence is that having separate statesPHY_NOLINKandPHY_ANisn't needed.By using these two axioms the state machine can be significantly simplified.
Quoted 7 lines
Quoted 4 lines
So how are we going to move forward on this?
Maybe we can apply this series and just watch carefully for any problems that get reported or are found?
Given Heiner is always responsive and taking care of fixing what might be/have broken, no objections with me on that.
Yes, lets try it.
Andrew
David Miller
maintainerFrom: Florian Fainelli <[email protected]>
Date: Thu, 8 Nov 2018 15:00:01 -0800
Quoted 19 lines · David Miller
Quoted 18 lines
From: Heiner Kallweit <[email protected]>
Date: Wed, 7 Nov 2018 20:41:52 +0100Quoted 14 lines
This patch series is based on two axioms:
- During autoneg a PHY always reports the link being down
- Info in clause 22/45 registers doesn't allow to differentiate between these two states:
1. Link is physically down
2. A link partner is connected and PHY is autonegotiating In both cases "link up" and "aneg finished" bits aren't set. One consequence is that having separate statesPHY_NOLINKandPHY_ANisn't needed.By using these two axioms the state machine can be significantly simplified.
Quoted 7 lines
Quoted 4 lines
So how are we going to move forward on this?
Maybe we can apply this series and just watch carefully for any problems that get reported or are found?
Given Heiner is always responsive and taking care of fixing what might be/have broken, no objections with me on that.
Great, I've applied this series to net-next then.
Thanks.