diff mbox

net: phy: fix uninitalized WOL parameters in phy_ethtool_get_wol

Message ID 1394413270-7169-1-git-send-email-sebastian.hesselbarth@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Sebastian Hesselbarth March 10, 2014, 1:01 a.m. UTC
phy_ethtool_get_wol is a helper to get current WOL settings from
a phy device. When using this helper on a PHY without .get_wol
callback, struct ethtool_wolinfo is never set-up correctly and
may contain misleading information about WOL status.

To fix this, always zero relevant fields of struct ethtool_wolinfo
regardless of .get_wol callback availability.

Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
---
Cc: David Miller <davem@davemloft.net>
Cc: Florian Fainelli <f.fainelli@gmail.com>
Cc: netdev@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
---
 drivers/net/phy/phy.c | 1 +
 1 file changed, 1 insertion(+)

Comments

Ben Hutchings March 10, 2014, 2:51 a.m. UTC | #1
On Mon, 2014-03-10 at 02:01 +0100, Sebastian Hesselbarth wrote:
> phy_ethtool_get_wol is a helper to get current WOL settings from
> a phy device. When using this helper on a PHY without .get_wol
> callback, struct ethtool_wolinfo is never set-up correctly and
> may contain misleading information about WOL status.
> 
> To fix this, always zero relevant fields of struct ethtool_wolinfo
> regardless of .get_wol callback availability.

I think it's the caller's responsibility to zero out struct
ethtool_wolinfo.  That is what ethtool_get_wol() does.

Maybe you could split ethtool_get_wol() like we did
ethtool_get_settings(), to support in-kernel invocation of ETHTOOL_GWOL?

Ben.

> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> ---
> Cc: David Miller <davem@davemloft.net>
> Cc: Florian Fainelli <f.fainelli@gmail.com>
> Cc: netdev@vger.kernel.org
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: linux-kernel@vger.kernel.org
> ---
>  drivers/net/phy/phy.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
> index 19c9eca0ef26..62a7cd401e1c 100644
> --- a/drivers/net/phy/phy.c
> +++ b/drivers/net/phy/phy.c
> @@ -1092,6 +1092,7 @@ EXPORT_SYMBOL(phy_ethtool_set_wol);
>  
>  void phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
>  {
> +	wol->supported = wol->wolopts = 0;
>  	if (phydev->drv->get_wol)
>  		phydev->drv->get_wol(phydev, wol);
>  }
Sebastian Hesselbarth March 10, 2014, 10:49 a.m. UTC | #2
On 03/10/2014 02:51 AM, Ben Hutchings wrote:
> On Mon, 2014-03-10 at 02:01 +0100, Sebastian Hesselbarth wrote:
>> phy_ethtool_get_wol is a helper to get current WOL settings from
>> a phy device. When using this helper on a PHY without .get_wol
>> callback, struct ethtool_wolinfo is never set-up correctly and
>> may contain misleading information about WOL status.
>>
>> To fix this, always zero relevant fields of struct ethtool_wolinfo
>> regardless of .get_wol callback availability.
>
> I think it's the caller's responsibility to zero out struct
> ethtool_wolinfo.  That is what ethtool_get_wol() does.

Actually, phy_ethtool_get_wol is the caller here. This belongs to
a set of helpers that deal with phy_device, not netdev.

> Maybe you could split ethtool_get_wol() like we did
> ethtool_get_settings(), to support in-kernel invocation of ETHTOOL_GWOL?

Looking at the other users of phy_ethtool_get_wol (mv643xx_eth and
cpsw), both drivers use this helper to determine what to pass back
on the corresponding ethtool_get_wol call.

BTW, both drivers above do zero ethtool_wolinfo before calling
phy_ethtool_get_wol. I can either zero it in phy_suspend too or we
deal with it properly in phy_ethtool_get_wol instead:

void phy_ethtool_get_wol(struct phy_device *phydev, struct 
ethtool_wolinfo *wol)
{
	memset(wol, 0, sizeof(*wol));

	if (phydev && phydev->drv->get_wol)
		phydev->drv->get_wol(phydev, wol);
}

That would also simplify above drivers down to e.g:

static void cpsw_get_wol(struct net_device *ndev, struct ethtool_wolinfo 
*wol)
{
	struct cpsw_priv *priv = netdev_priv(ndev);
	int slave_no = cpsw_slave_index(priv);
	phy_ethtool_get_wol(priv->slaves[slave_no].phy, wol);
}

instead of:

static void cpsw_get_wol(struct net_device *ndev, struct ethtool_wolinfo 
*wol)
{
	struct cpsw_priv *priv = netdev_priv(ndev);
	int slave_no = cpsw_slave_index(priv);

	wol->supported = 0;
	wol->wolopts = 0;

	if (priv->slaves[slave_no].phy)
		phy_ethtool_get_wol(priv->slaves[slave_no].phy, wol);
}

>> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
>> ---
>> Cc: David Miller <davem@davemloft.net>
>> Cc: Florian Fainelli <f.fainelli@gmail.com>
>> Cc: netdev@vger.kernel.org
>> Cc: linux-arm-kernel@lists.infradead.org
>> Cc: linux-kernel@vger.kernel.org
>> ---
>>   drivers/net/phy/phy.c | 1 +
>>   1 file changed, 1 insertion(+)
>>
>> diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
>> index 19c9eca0ef26..62a7cd401e1c 100644
>> --- a/drivers/net/phy/phy.c
>> +++ b/drivers/net/phy/phy.c
>> @@ -1092,6 +1092,7 @@ EXPORT_SYMBOL(phy_ethtool_set_wol);
>>
>>   void phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
>>   {
>> +	wol->supported = wol->wolopts = 0;
>>   	if (phydev->drv->get_wol)
>>   		phydev->drv->get_wol(phydev, wol);
>>   }
>
David Miller March 10, 2014, 8:23 p.m. UTC | #3
From: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Date: Mon, 10 Mar 2014 10:49:53 +0000

> void phy_ethtool_get_wol(struct phy_device *phydev, struct
> ethtool_wolinfo *wol)
> {
> 	memset(wol, 0, sizeof(*wol));
> 
> 	if (phydev && phydev->drv->get_wol)
> 		phydev->drv->get_wol(phydev, wol);
> }
> 
> That would also simplify above drivers down to e.g:
> 
> static void cpsw_get_wol(struct net_device *ndev, struct
> ethtool_wolinfo *wol)
> {
> 	struct cpsw_priv *priv = netdev_priv(ndev);
> 	int slave_no = cpsw_slave_index(priv);
> 	phy_ethtool_get_wol(priv->slaves[slave_no].phy, wol);
> }
> 
> instead of:
> 
> static void cpsw_get_wol(struct net_device *ndev, struct
> ethtool_wolinfo *wol)
> {
> 	struct cpsw_priv *priv = netdev_priv(ndev);
> 	int slave_no = cpsw_slave_index(priv);
> 
> 	wol->supported = 0;
> 	wol->wolopts = 0;
> 
> 	if (priv->slaves[slave_no].phy)
> 		phy_ethtool_get_wol(priv->slaves[slave_no].phy, wol);
> }

Agreed, since phy_ethtool_get_wol() is the common routine used by the drivers,
we should make it clear the structure.
Sebastian Hesselbarth March 10, 2014, 8:50 p.m. UTC | #4
David,

this is a small patch set based on a single patch sent earlier [1] to
fix uninitalized struct ethtool_wolinfo in phy_ethtool_get_wol.
Compared to the single patch, this clears the whole struct
ethtool_wolinfo passed to phy_ethtool_get_wol and adds a check for
non-NULL struct phy_device.

I also added two cleanup patches for current users of
phy_ethtool_get_wol. Those two patches are also based on v3.14-rc1 but
aren't really part of the fix. They can wait for v3.15 and I'll rebase
on request.

[1] https://lkml.org/lkml/2014/3/9/169

Sebastian Hesselbarth (3):
  net: phy: fix uninitalized WOL parameters in phy_ethtool_get_wol
  net: mv643xx_eth: simplify phy_ethtool_get_wol call
  net: cpsw: simplify phy_ethtool_get_wol call

 drivers/net/ethernet/marvell/mv643xx_eth.c | 5 +----
 drivers/net/ethernet/ti/cpsw.c             | 7 +------
 drivers/net/phy/phy.c                      | 4 +++-
 3 files changed, 5 insertions(+), 11 deletions(-)

---
Cc: David Miller <davem@davemloft.net>
Cc: Florian Fainelli <f.fainelli@gmail.com>
Cc: netdev@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Florian Fainelli March 10, 2014, 10:20 p.m. UTC | #5
2014-03-10 13:50 GMT-07:00 Sebastian Hesselbarth
<sebastian.hesselbarth@gmail.com>:
> David,
>
> this is a small patch set based on a single patch sent earlier [1] to
> fix uninitalized struct ethtool_wolinfo in phy_ethtool_get_wol.
> Compared to the single patch, this clears the whole struct
> ethtool_wolinfo passed to phy_ethtool_get_wol and adds a check for
> non-NULL struct phy_device.
>
> I also added two cleanup patches for current users of
> phy_ethtool_get_wol. Those two patches are also based on v3.14-rc1 but
> aren't really part of the fix. They can wait for v3.15 and I'll rebase
> on request.

Looks good to me, thanks Sebastian:

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>

>
> [1] https://lkml.org/lkml/2014/3/9/169
>
> Sebastian Hesselbarth (3):
>   net: phy: fix uninitalized WOL parameters in phy_ethtool_get_wol
>   net: mv643xx_eth: simplify phy_ethtool_get_wol call
>   net: cpsw: simplify phy_ethtool_get_wol call
>
>  drivers/net/ethernet/marvell/mv643xx_eth.c | 5 +----
>  drivers/net/ethernet/ti/cpsw.c             | 7 +------
>  drivers/net/phy/phy.c                      | 4 +++-
>  3 files changed, 5 insertions(+), 11 deletions(-)
>
> ---
> Cc: David Miller <davem@davemloft.net>
> Cc: Florian Fainelli <f.fainelli@gmail.com>
> Cc: netdev@vger.kernel.org
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: linux-kernel@vger.kernel.org
> --
> 1.8.5.3
>
Ben Hutchings March 10, 2014, 11:17 p.m. UTC | #6
On Mon, 2014-03-10 at 10:49 +0000, Sebastian Hesselbarth wrote:
> On 03/10/2014 02:51 AM, Ben Hutchings wrote:
> > On Mon, 2014-03-10 at 02:01 +0100, Sebastian Hesselbarth wrote:
> >> phy_ethtool_get_wol is a helper to get current WOL settings from
> >> a phy device. When using this helper on a PHY without .get_wol
> >> callback, struct ethtool_wolinfo is never set-up correctly and
> >> may contain misleading information about WOL status.
> >>
> >> To fix this, always zero relevant fields of struct ethtool_wolinfo
> >> regardless of .get_wol callback availability.
> >
> > I think it's the caller's responsibility to zero out struct
> > ethtool_wolinfo.  That is what ethtool_get_wol() does.
> 
> Actually, phy_ethtool_get_wol is the caller here. This belongs to
> a set of helpers that deal with phy_device, not netdev.

Right, but ethtool_get_wol() is further up the stack and is responsible
for initialising the struct to defaults.

> > Maybe you could split ethtool_get_wol() like we did
> > ethtool_get_settings(), to support in-kernel invocation of ETHTOOL_GWOL?
> 
> Looking at the other users of phy_ethtool_get_wol (mv643xx_eth and
> cpsw), both drivers use this helper to determine what to pass back
> on the corresponding ethtool_get_wol call.
> 
> BTW, both drivers above do zero ethtool_wolinfo before calling
> phy_ethtool_get_wol. I can either zero it in phy_suspend too or we
> deal with it properly in phy_ethtool_get_wol instead:
> 
> void phy_ethtool_get_wol(struct phy_device *phydev, struct 
> ethtool_wolinfo *wol)
> {
> 	memset(wol, 0, sizeof(*wol));
> 
> 	if (phydev && phydev->drv->get_wol)
> 		phydev->drv->get_wol(phydev, wol);
> }
[...]

This trashes wol->cmd.  Don't do that.

Ben.
Ben Hutchings March 10, 2014, 11:18 p.m. UTC | #7
On Mon, 2014-03-10 at 16:23 -0400, David Miller wrote:
> From: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
> Date: Mon, 10 Mar 2014 10:49:53 +0000
> 
> > void phy_ethtool_get_wol(struct phy_device *phydev, struct
> > ethtool_wolinfo *wol)
> > {
> >       memset(wol, 0, sizeof(*wol));
> > 
> >       if (phydev && phydev->drv->get_wol)
> >               phydev->drv->get_wol(phydev, wol);
> > }
> > 
> > That would also simplify above drivers down to e.g:
> > 
> > static void cpsw_get_wol(struct net_device *ndev, struct
> > ethtool_wolinfo *wol)
> > {
> >       struct cpsw_priv *priv = netdev_priv(ndev);
> >       int slave_no = cpsw_slave_index(priv);
> >       phy_ethtool_get_wol(priv->slaves[slave_no].phy, wol);
> > }
> > 
> > instead of:
> > 
> > static void cpsw_get_wol(struct net_device *ndev, struct
> > ethtool_wolinfo *wol)
> > {
> >       struct cpsw_priv *priv = netdev_priv(ndev);
> >       int slave_no = cpsw_slave_index(priv);
> > 
> >       wol->supported = 0;
> >       wol->wolopts = 0;
> > 
> >       if (priv->slaves[slave_no].phy)
> >               phy_ethtool_get_wol(priv->slaves[slave_no].phy, wol);
> > }
> 
> Agreed, since phy_ethtool_get_wol() is the common routine used by the drivers,
> we should make it clear the structure.

No, ethtool_get_wol() is the common routine used by *all* the drivers
and it initialises the struct properly.

Ben.
Sebastian Hesselbarth March 11, 2014, 8:40 a.m. UTC | #8
On 03/11/2014 12:17 AM, Ben Hutchings wrote:
> On Mon, 2014-03-10 at 10:49 +0000, Sebastian Hesselbarth wrote:
>> On 03/10/2014 02:51 AM, Ben Hutchings wrote:
>>> On Mon, 2014-03-10 at 02:01 +0100, Sebastian Hesselbarth wrote:
>>>> phy_ethtool_get_wol is a helper to get current WOL settings from
>>>> a phy device. When using this helper on a PHY without .get_wol
>>>> callback, struct ethtool_wolinfo is never set-up correctly and
>>>> may contain misleading information about WOL status.
>>>>
>>>> To fix this, always zero relevant fields of struct ethtool_wolinfo
>>>> regardless of .get_wol callback availability.
>>>
>>> I think it's the caller's responsibility to zero out struct
>>> ethtool_wolinfo.  That is what ethtool_get_wol() does.
>>
>> Actually, phy_ethtool_get_wol is the caller here. This belongs to
>> a set of helpers that deal with phy_device, not netdev.
>
> Right, but ethtool_get_wol() is further up the stack and is responsible
> for initialising the struct to defaults.

Ok, currently we have 3 users of phy_ethtool_get_wol():
- mv643xx_eth and cpsw use that in their .get_wol callback that is
   called on ethtool_get_wol().
- phy_suspend to determine if it is safe to suspend a PHY

With phy_suspend, I could use a kernel-compatible __ethtool_get_wol()
but that requires to have an .attached_dev as __ethtool_get_wol()
takes netdev. This would limit phy_suspend to attached PHYs while
even non-attached PHYs should be suspended.

OTOH, if we don't want phy_ethtool_get_wol to clear out ethtool_wol and
no dependency on .attached_dev, phy_suspend is the only place to
properly initialize ethtool_wol on this level.

>>> Maybe you could split ethtool_get_wol() like we did
>>> ethtool_get_settings(), to support in-kernel invocation of ETHTOOL_GWOL?
>>
>> Looking at the other users of phy_ethtool_get_wol (mv643xx_eth and
>> cpsw), both drivers use this helper to determine what to pass back
>> on the corresponding ethtool_get_wol call.
>>
>> BTW, both drivers above do zero ethtool_wolinfo before calling
>> phy_ethtool_get_wol. I can either zero it in phy_suspend too or we
>> deal with it properly in phy_ethtool_get_wol instead:
>>
>> void phy_ethtool_get_wol(struct phy_device *phydev, struct
>> ethtool_wolinfo *wol)
>> {
>> 	memset(wol, 0, sizeof(*wol));
>>
>> 	if (phydev && phydev->drv->get_wol)
>> 		phydev->drv->get_wol(phydev, wol);
>> }
> [...]
>
> This trashes wol->cmd.  Don't do that.

You are right on this one, I'll missed it and will fix it up.

Sebastian
diff mbox

Patch

diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 19c9eca0ef26..62a7cd401e1c 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -1092,6 +1092,7 @@  EXPORT_SYMBOL(phy_ethtool_set_wol);
 
 void phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
 {
+	wol->supported = wol->wolopts = 0;
 	if (phydev->drv->get_wol)
 		phydev->drv->get_wol(phydev, wol);
 }