diff mbox series

[net,v2] net: phy: fix phy_ethtool_set_eee() incorrectly enabling LPI

Message ID E1tESfx-004evI-NH@rmk-PC.armlinux.org.uk (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series [net,v2] net: phy: fix phy_ethtool_set_eee() incorrectly enabling LPI | expand

Checks

Context Check Description
netdev/series_format success Single patches do not need cover letters
netdev/tree_selection success Clearly marked for net
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present fail Series targets non-next tree, but doesn't contain any Fixes tags
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 3 this patch: 3
netdev/build_tools success Errors and warnings before: 0 (+0) this patch: 0 (+0)
netdev/cc_maintainers success CCed 7 of 7 maintainers
netdev/build_clang success Errors and warnings before: 11 this patch: 11
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 369 this patch: 369
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 63 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc fail Errors and warnings before: 99 this patch: 100
netdev/source_inline success Was 0 now: 0

Commit Message

Russell King (Oracle) Nov. 22, 2024, 12:22 p.m. UTC
When phy_ethtool_set_eee_noneg() detects a change in the LPI
parameters, it attempts to update phylib state and trigger the link
to cycle so the MAC sees the updated parameters.

However, in doing so, it sets phydev->enable_tx_lpi depending on
whether the EEE configuration allows the MAC to generate LPI without
taking into account the result of negotiation.

This can be demonstrated with a 1000base-T FD interface by:

 # ethtool --set-eee eno0 advertise 8	# cause EEE to be not negotiated
 # ethtool --set-eee eno0 tx-lpi off
 # ethtool --set-eee eno0 tx-lpi on

This results in being true, despite EEE not having been negotiated and:
 # ethtool --show-eee eno0
	EEE status: enabled - inactive
	Tx LPI: 250 (us)
	Supported EEE link modes:  100baseT/Full
	                           1000baseT/Full
	Advertised EEE link modes:  100baseT/Full
	Link partner advertised EEE link modes:  100baseT/Full
	                                         1000baseT/Full

Fix this by keeping track of whether EEE was negotiated via a new
eee_active member in struct phy_device, and include this state in
the decision whether phydev->enable_tx_lpi should be set.

Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
---
 drivers/net/phy/phy-c45.c |  2 +-
 drivers/net/phy/phy.c     | 32 ++++++++++++++++++--------------
 include/linux/phy.h       |  1 +
 3 files changed, 20 insertions(+), 15 deletions(-)

Comments

Andrew Lunn Nov. 22, 2024, 1:10 p.m. UTC | #1
> diff --git a/include/linux/phy.h b/include/linux/phy.h
> index 77c6d6451638..6a17cc05f876 100644
> --- a/include/linux/phy.h
> +++ b/include/linux/phy.h
> @@ -723,6 +723,7 @@ struct phy_device {
>  	/* Energy efficient ethernet modes which should be prohibited */
>  	__ETHTOOL_DECLARE_LINK_MODE_MASK(eee_broken_modes);
>  	bool enable_tx_lpi;
> +	bool eee_active;

This is missing a kdoc entry.

With that fixed, please add my Reviewed-by:

    Andrew

---
pw-bot: cr
Heiner Kallweit Nov. 22, 2024, 8:04 p.m. UTC | #2
On 22.11.2024 13:22, Russell King (Oracle) wrote:
> ---
>  drivers/net/phy/phy-c45.c |  2 +-
>  drivers/net/phy/phy.c     | 32 ++++++++++++++++++--------------
>  include/linux/phy.h       |  1 +
>  3 files changed, 20 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
> index 96d0b3a5a9d3..944ae98ad110 100644
> --- a/drivers/net/phy/phy-c45.c
> +++ b/drivers/net/phy/phy-c45.c
> @@ -1530,7 +1530,7 @@ int genphy_c45_ethtool_get_eee(struct phy_device *phydev,
>  		return ret;
>  
>  	data->eee_enabled = is_enabled;
> -	data->eee_active = ret;
> +	data->eee_active = phydev->eee_active;
>  	linkmode_copy(data->supported, phydev->supported_eee);
>  
>  	return 0;
> diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
> index 4f3e742907cb..e174107b96e2 100644
> --- a/drivers/net/phy/phy.c
> +++ b/drivers/net/phy/phy.c
> @@ -990,14 +990,14 @@ static int phy_check_link_status(struct phy_device *phydev)
>  		phydev->state = PHY_RUNNING;
>  		err = genphy_c45_eee_is_active(phydev,
>  					       NULL, NULL, NULL);
> -		if (err <= 0)
> -			phydev->enable_tx_lpi = false;
> -		else
> -			phydev->enable_tx_lpi = phydev->eee_cfg.tx_lpi_enabled;
> +		phydev->eee_active = err > 0;
> +		phydev->enable_tx_lpi = phydev->eee_cfg.tx_lpi_enabled &&
> +					phydev->eee_active;
>  
>  		phy_link_up(phydev);
>  	} else if (!phydev->link && phydev->state != PHY_NOLINK) {
>  		phydev->state = PHY_NOLINK;
> +		phydev->eee_active = false;
>  		phydev->enable_tx_lpi = false;
>  		phy_link_down(phydev);
>  	}
> @@ -1685,16 +1685,20 @@ EXPORT_SYMBOL(phy_ethtool_get_eee);
>  static void phy_ethtool_set_eee_noneg(struct phy_device *phydev,
>  				      struct ethtool_keee *data)
>  {
> -	if (phydev->eee_cfg.tx_lpi_enabled != data->tx_lpi_enabled ||
> -	    phydev->eee_cfg.tx_lpi_timer != data->tx_lpi_timer) {
> -		eee_to_eeecfg(&phydev->eee_cfg, data);
> -		phydev->enable_tx_lpi = eeecfg_mac_can_tx_lpi(&phydev->eee_cfg);
> -		if (phydev->link) {
> -			phydev->link = false;
> -			phy_link_down(phydev);
> -			phydev->link = true;
> -			phy_link_up(phydev);
> -		}
> +	bool enable_tx_lpi = data->tx_lpi_enabled &&
> +			     phydev->eee_active;
> +
> +	eee_to_eeecfg(&phydev->eee_cfg, data);
> +
> +	if ((phydev->enable_tx_lpi != enable_tx_lpi ||
> +	     phydev->eee_cfg.tx_lpi_timer != data->tx_lpi_timer) &&
> +	    phydev->link) {
> +		phydev->enable_tx_lpi = false;
> +		phydev->link = false;
> +		phy_link_down(phydev);
> +		phydev->enable_tx_lpi = enable_tx_lpi;
> +		phydev->link = true;
> +		phy_link_up(phydev);

This part collides with a pending patch:
https://patchwork.kernel.org/project/netdevbpf/patch/a5efc274-ce58-49f3-ac8a-5384d9b41695@gmail.com/
I think you have to rebase and resubmit once the pending patch has been applied.

>  	}
>  }
>  
> diff --git a/include/linux/phy.h b/include/linux/phy.h
> index 77c6d6451638..6a17cc05f876 100644
> --- a/include/linux/phy.h
> +++ b/include/linux/phy.h
> @@ -723,6 +723,7 @@ struct phy_device {
>  	/* Energy efficient ethernet modes which should be prohibited */
>  	__ETHTOOL_DECLARE_LINK_MODE_MASK(eee_broken_modes);
>  	bool enable_tx_lpi;
> +	bool eee_active;
>  	struct eee_config eee_cfg;
>  
>  	/* Host supported PHY interface types. Should be ignored if empty. */
> -- 2.30.2
Heiner Kallweit Nov. 22, 2024, 8:28 p.m. UTC | #3
On 22.11.2024 21:04, Heiner Kallweit wrote:
> On 22.11.2024 13:22, Russell King (Oracle) wrote:
>> ---
>>  drivers/net/phy/phy-c45.c |  2 +-
>>  drivers/net/phy/phy.c     | 32 ++++++++++++++++++--------------
>>  include/linux/phy.h       |  1 +
>>  3 files changed, 20 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
>> index 96d0b3a5a9d3..944ae98ad110 100644
>> --- a/drivers/net/phy/phy-c45.c
>> +++ b/drivers/net/phy/phy-c45.c
>> @@ -1530,7 +1530,7 @@ int genphy_c45_ethtool_get_eee(struct phy_device *phydev,
>>  		return ret;
>>  
>>  	data->eee_enabled = is_enabled;
>> -	data->eee_active = ret;
>> +	data->eee_active = phydev->eee_active;
>>  	linkmode_copy(data->supported, phydev->supported_eee);
>>  
>>  	return 0;
>> diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
>> index 4f3e742907cb..e174107b96e2 100644
>> --- a/drivers/net/phy/phy.c
>> +++ b/drivers/net/phy/phy.c
>> @@ -990,14 +990,14 @@ static int phy_check_link_status(struct phy_device *phydev)
>>  		phydev->state = PHY_RUNNING;
>>  		err = genphy_c45_eee_is_active(phydev,
>>  					       NULL, NULL, NULL);
>> -		if (err <= 0)
>> -			phydev->enable_tx_lpi = false;
>> -		else
>> -			phydev->enable_tx_lpi = phydev->eee_cfg.tx_lpi_enabled;
>> +		phydev->eee_active = err > 0;
>> +		phydev->enable_tx_lpi = phydev->eee_cfg.tx_lpi_enabled &&
>> +					phydev->eee_active;
>>  
>>  		phy_link_up(phydev);
>>  	} else if (!phydev->link && phydev->state != PHY_NOLINK) {
>>  		phydev->state = PHY_NOLINK;
>> +		phydev->eee_active = false;
>>  		phydev->enable_tx_lpi = false;
>>  		phy_link_down(phydev);
>>  	}
>> @@ -1685,16 +1685,20 @@ EXPORT_SYMBOL(phy_ethtool_get_eee);
>>  static void phy_ethtool_set_eee_noneg(struct phy_device *phydev,
>>  				      struct ethtool_keee *data)
>>  {
>> -	if (phydev->eee_cfg.tx_lpi_enabled != data->tx_lpi_enabled ||
>> -	    phydev->eee_cfg.tx_lpi_timer != data->tx_lpi_timer) {
>> -		eee_to_eeecfg(&phydev->eee_cfg, data);
>> -		phydev->enable_tx_lpi = eeecfg_mac_can_tx_lpi(&phydev->eee_cfg);
>> -		if (phydev->link) {
>> -			phydev->link = false;
>> -			phy_link_down(phydev);
>> -			phydev->link = true;
>> -			phy_link_up(phydev);
>> -		}
>> +	bool enable_tx_lpi = data->tx_lpi_enabled &&
>> +			     phydev->eee_active;
>> +
>> +	eee_to_eeecfg(&phydev->eee_cfg, data);
>> +
>> +	if ((phydev->enable_tx_lpi != enable_tx_lpi ||
>> +	     phydev->eee_cfg.tx_lpi_timer != data->tx_lpi_timer) &&
>> +	    phydev->link) {
>> +		phydev->enable_tx_lpi = false;
>> +		phydev->link = false;
>> +		phy_link_down(phydev);
>> +		phydev->enable_tx_lpi = enable_tx_lpi;
>> +		phydev->link = true;
>> +		phy_link_up(phydev);
> 
> This part collides with a pending patch:
> https://patchwork.kernel.org/project/netdevbpf/patch/a5efc274-ce58-49f3-ac8a-5384d9b41695@gmail.com/
> I think you have to rebase and resubmit once the pending patch has been applied.
> 

Merge of both changes should result in something like this:

static void phy_ethtool_set_eee_noneg(struct phy_device *phydev,
                                      const struct eee_config *old_cfg)
{
        bool enable_tx_lpi;

        if (!phydev->link)
                return;

        enable_tx_lpi = phydev->eee_cfg.tx_lpi_enabled && phydev->eee_active;

        if (enable_tx_lpi != old_cfg->tx_lpi_enabled ||
            phydev->eee_cfg.tx_lpi_timer != old_cfg->tx_lpi_timer) {
                phydev->enable_tx_lpi = false;
                phydev->link = false;
                phy_link_down(phydev);
                phydev->enable_tx_lpi = enable_tx_lpi;
                phydev->link = true;
                phy_link_up(phydev);
        }
}
Russell King (Oracle) Nov. 23, 2024, 2:03 p.m. UTC | #4
On Fri, Nov 22, 2024 at 09:28:04PM +0100, Heiner Kallweit wrote:
> On 22.11.2024 21:04, Heiner Kallweit wrote:
> > This part collides with a pending patch:
> > https://patchwork.kernel.org/project/netdevbpf/patch/a5efc274-ce58-49f3-ac8a-5384d9b41695@gmail.com/
> > I think you have to rebase and resubmit once the pending patch has been applied.
> 
> Merge of both changes should result in something like this:
> 
> static void phy_ethtool_set_eee_noneg(struct phy_device *phydev,
>                                       const struct eee_config *old_cfg)
> {
>         bool enable_tx_lpi;
> 
>         if (!phydev->link)
>                 return;
> 
>         enable_tx_lpi = phydev->eee_cfg.tx_lpi_enabled && phydev->eee_active;
> 
>         if (enable_tx_lpi != old_cfg->tx_lpi_enabled ||
>             phydev->eee_cfg.tx_lpi_timer != old_cfg->tx_lpi_timer) {
>                 phydev->enable_tx_lpi = false;
>                 phydev->link = false;
>                 phy_link_down(phydev);
>                 phydev->enable_tx_lpi = enable_tx_lpi;
>                 phydev->link = true;
>                 phy_link_up(phydev);
>         }
> }

Thanks for pointing this out - I've now merged your patch into my tree
and rebased on top. Your resolution isn't quite right - the if()
statement should be:

+       if (phydev->enable_tx_lpi != enable_tx_lpi ||
            phydev->eee_cfg.tx_lpi_timer != old_cfg->tx_lpi_timer) {

Since we only need to bounce the link if the LPI timer or the MAC LPI
enable state has changed. I'll send a replacement patch shortly.
diff mbox series

Patch

diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index 96d0b3a5a9d3..944ae98ad110 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -1530,7 +1530,7 @@  int genphy_c45_ethtool_get_eee(struct phy_device *phydev,
 		return ret;
 
 	data->eee_enabled = is_enabled;
-	data->eee_active = ret;
+	data->eee_active = phydev->eee_active;
 	linkmode_copy(data->supported, phydev->supported_eee);
 
 	return 0;
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 4f3e742907cb..e174107b96e2 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -990,14 +990,14 @@  static int phy_check_link_status(struct phy_device *phydev)
 		phydev->state = PHY_RUNNING;
 		err = genphy_c45_eee_is_active(phydev,
 					       NULL, NULL, NULL);
-		if (err <= 0)
-			phydev->enable_tx_lpi = false;
-		else
-			phydev->enable_tx_lpi = phydev->eee_cfg.tx_lpi_enabled;
+		phydev->eee_active = err > 0;
+		phydev->enable_tx_lpi = phydev->eee_cfg.tx_lpi_enabled &&
+					phydev->eee_active;
 
 		phy_link_up(phydev);
 	} else if (!phydev->link && phydev->state != PHY_NOLINK) {
 		phydev->state = PHY_NOLINK;
+		phydev->eee_active = false;
 		phydev->enable_tx_lpi = false;
 		phy_link_down(phydev);
 	}
@@ -1685,16 +1685,20 @@  EXPORT_SYMBOL(phy_ethtool_get_eee);
 static void phy_ethtool_set_eee_noneg(struct phy_device *phydev,
 				      struct ethtool_keee *data)
 {
-	if (phydev->eee_cfg.tx_lpi_enabled != data->tx_lpi_enabled ||
-	    phydev->eee_cfg.tx_lpi_timer != data->tx_lpi_timer) {
-		eee_to_eeecfg(&phydev->eee_cfg, data);
-		phydev->enable_tx_lpi = eeecfg_mac_can_tx_lpi(&phydev->eee_cfg);
-		if (phydev->link) {
-			phydev->link = false;
-			phy_link_down(phydev);
-			phydev->link = true;
-			phy_link_up(phydev);
-		}
+	bool enable_tx_lpi = data->tx_lpi_enabled &&
+			     phydev->eee_active;
+
+	eee_to_eeecfg(&phydev->eee_cfg, data);
+
+	if ((phydev->enable_tx_lpi != enable_tx_lpi ||
+	     phydev->eee_cfg.tx_lpi_timer != data->tx_lpi_timer) &&
+	    phydev->link) {
+		phydev->enable_tx_lpi = false;
+		phydev->link = false;
+		phy_link_down(phydev);
+		phydev->enable_tx_lpi = enable_tx_lpi;
+		phydev->link = true;
+		phy_link_up(phydev);
 	}
 }
 
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 77c6d6451638..6a17cc05f876 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -723,6 +723,7 @@  struct phy_device {
 	/* Energy efficient ethernet modes which should be prohibited */
 	__ETHTOOL_DECLARE_LINK_MODE_MASK(eee_broken_modes);
 	bool enable_tx_lpi;
+	bool eee_active;
 	struct eee_config eee_cfg;
 
 	/* Host supported PHY interface types. Should be ignored if empty. */