diff mbox series

[net-next,2/2] net: phy: Fallback to C22 access if needed in phy_mii_ioctl()

Message ID 20240906093955.3083245-3-niklas.soderlund+renesas@ragnatech.se (mailing list archive)
State Awaiting Upstream
Delegated to: Geert Uytterhoeven
Headers show
Series net: phy: Fallback to C22 for PHY register read/write | expand

Commit Message

Niklas Söderlund Sept. 6, 2024, 9:39 a.m. UTC
If a C45 only PHY is attached to a driver that only knows how to talk
C22 phylib will fallback and use indirect access. This frees the driver
from having to implement this themself.

The IOCTL implementation for SIOCGMIIREG and SIOCSMIIREG do not use
these convenience functions and instead fail if a C45 PHY is used
together with a driver that only knows how to speak C22.

Fix this by using the two convince functions that knows when to fallback
to indirect access to read/write to the MDIO bus when needed.

Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
---
 drivers/net/phy/phy.c | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

Comments

Andrew Lunn Sept. 6, 2024, 9:04 p.m. UTC | #1
On Fri, Sep 06, 2024 at 11:39:55AM +0200, Niklas Söderlund wrote:
> If a C45 only PHY is attached to a driver that only knows how to talk
> C22 phylib will fallback and use indirect access. This frees the driver
> from having to implement this themself.
> 
> The IOCTL implementation for SIOCGMIIREG and SIOCSMIIREG do not use
> these convenience functions and instead fail if a C45 PHY is used
> together with a driver that only knows how to speak C22.
> 
> Fix this by using the two convince functions that knows when to fallback
> to indirect access to read/write to the MDIO bus when needed.
> 
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> ---
>  drivers/net/phy/phy.c | 18 ++++++++++++------
>  1 file changed, 12 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
> index 4f3e742907cb..89f52bb123aa 100644
> --- a/drivers/net/phy/phy.c
> +++ b/drivers/net/phy/phy.c
> @@ -342,9 +342,12 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
>  		if (mdio_phy_id_is_c45(mii_data->phy_id)) {
>  			prtad = mdio_phy_id_prtad(mii_data->phy_id);
>  			devad = mdio_phy_id_devad(mii_data->phy_id);
> -			ret = mdiobus_c45_read(phydev->mdio.bus, prtad, devad,
> -					       mii_data->reg_num);
>  
> +			mutex_lock(&phydev->mdio.bus->mdio_lock);
> +			ret = mmd_phy_read(phydev->mdio.bus, prtad,
> +					   phydev->is_c45, devad,

Using phydev->is_c45 is probably wrong.

mii_data->phy_id is the device on the bus you want to access. It does
not need to be the same device as the MAC is using. Just because the
device the MAC is using is a c45 device does not mean the device you
are trying to access is.

Maybe i gave you some bad advice. Sorry.

This API is reasonably well known to be a foot gun. You should only be
using it for debug, and actually using it, even only to read
registers, can mess up a PHY/phylib.

The API gives you the ability to perform a C22 bus transaction, or a
C45 bus transaction on any arbitrary device. That is all you need for
debug, you can do C45 over C22 in user space. Yes, there are race
conditions, but this API already has race conditions, which is part of
why it is a foot gun.

    Andrew
Niklas Söderlund Sept. 6, 2024, 9:36 p.m. UTC | #2
On 2024-09-06 23:04:50 +0200, Andrew Lunn wrote:
> On Fri, Sep 06, 2024 at 11:39:55AM +0200, Niklas Söderlund wrote:
> > If a C45 only PHY is attached to a driver that only knows how to talk
> > C22 phylib will fallback and use indirect access. This frees the driver
> > from having to implement this themself.
> > 
> > The IOCTL implementation for SIOCGMIIREG and SIOCSMIIREG do not use
> > these convenience functions and instead fail if a C45 PHY is used
> > together with a driver that only knows how to speak C22.
> > 
> > Fix this by using the two convince functions that knows when to fallback
> > to indirect access to read/write to the MDIO bus when needed.
> > 
> > Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> > ---
> >  drivers/net/phy/phy.c | 18 ++++++++++++------
> >  1 file changed, 12 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
> > index 4f3e742907cb..89f52bb123aa 100644
> > --- a/drivers/net/phy/phy.c
> > +++ b/drivers/net/phy/phy.c
> > @@ -342,9 +342,12 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
> >  		if (mdio_phy_id_is_c45(mii_data->phy_id)) {
> >  			prtad = mdio_phy_id_prtad(mii_data->phy_id);
> >  			devad = mdio_phy_id_devad(mii_data->phy_id);
> > -			ret = mdiobus_c45_read(phydev->mdio.bus, prtad, devad,
> > -					       mii_data->reg_num);
> >  
> > +			mutex_lock(&phydev->mdio.bus->mdio_lock);
> > +			ret = mmd_phy_read(phydev->mdio.bus, prtad,
> > +					   phydev->is_c45, devad,
> 
> Using phydev->is_c45 is probably wrong.
> 
> mii_data->phy_id is the device on the bus you want to access. It does
> not need to be the same device as the MAC is using. Just because the
> device the MAC is using is a c45 device does not mean the device you
> are trying to access is.

I think phydev->is_c45. The outer if clause is checking if the PHY I 
want to talk to is C45, or not. The phydev->is_c45 just express if the 
MAC can talk C45, or if it only supports C22. This is the variable 
mmd_phy_read() uses to determine if it shall fallback to indirect C45.

> 
> Maybe i gave you some bad advice. Sorry.
> 
> This API is reasonably well known to be a foot gun. You should only be
> using it for debug, and actually using it, even only to read
> registers, can mess up a PHY/phylib.
> 
> The API gives you the ability to perform a C22 bus transaction, or a
> C45 bus transaction on any arbitrary device. That is all you need for
> debug, you can do C45 over C22 in user space. Yes, there are race
> conditions, but this API already has race conditions, which is part of
> why it is a foot gun.

I agree it's not the best. But is it not better to have the IOCTLs 
working as they already exists, rather then have it function depending 
on the combination of if the MAC and PHY both speak C22 or C45?
Russell King (Oracle) Sept. 17, 2024, 4:10 p.m. UTC | #3
On Fri, Sep 06, 2024 at 11:36:08PM +0200, Niklas Söderlund wrote:
> On 2024-09-06 23:04:50 +0200, Andrew Lunn wrote:
> > On Fri, Sep 06, 2024 at 11:39:55AM +0200, Niklas Söderlund wrote:
> > > If a C45 only PHY is attached to a driver that only knows how to talk
> > > C22 phylib will fallback and use indirect access. This frees the driver
> > > from having to implement this themself.
> > > 
> > > The IOCTL implementation for SIOCGMIIREG and SIOCSMIIREG do not use
> > > these convenience functions and instead fail if a C45 PHY is used
> > > together with a driver that only knows how to speak C22.
> > > 
> > > Fix this by using the two convince functions that knows when to fallback
> > > to indirect access to read/write to the MDIO bus when needed.
> > > 
> > > Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> > > ---
> > >  drivers/net/phy/phy.c | 18 ++++++++++++------
> > >  1 file changed, 12 insertions(+), 6 deletions(-)
> > > 
> > > diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
> > > index 4f3e742907cb..89f52bb123aa 100644
> > > --- a/drivers/net/phy/phy.c
> > > +++ b/drivers/net/phy/phy.c
> > > @@ -342,9 +342,12 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
> > >  		if (mdio_phy_id_is_c45(mii_data->phy_id)) {
> > >  			prtad = mdio_phy_id_prtad(mii_data->phy_id);
> > >  			devad = mdio_phy_id_devad(mii_data->phy_id);
> > > -			ret = mdiobus_c45_read(phydev->mdio.bus, prtad, devad,
> > > -					       mii_data->reg_num);
> > >  
> > > +			mutex_lock(&phydev->mdio.bus->mdio_lock);
> > > +			ret = mmd_phy_read(phydev->mdio.bus, prtad,
> > > +					   phydev->is_c45, devad,
> > 
> > Using phydev->is_c45 is probably wrong.
> > 
> > mii_data->phy_id is the device on the bus you want to access. It does
> > not need to be the same device as the MAC is using. Just because the
> > device the MAC is using is a c45 device does not mean the device you
> > are trying to access is.
> 
> I think phydev->is_c45. The outer if clause is checking if the PHY I 
> want to talk to is C45, or not. The phydev->is_c45 just express if the 
> MAC can talk C45, or if it only supports C22. This is the variable 
> mmd_phy_read() uses to determine if it shall fallback to indirect C45.

Nevertheless, this is wrong for two reasons:

1) the MII access API is a raw bus access debugging API, so its up to
userspace to do the necessary accesses. Yes, it's racy... it isn't
supposed to be used for production purposes.

2) the MII access API gives access to any MDIO device on the bus that
the _associated_ PHY is attached to. This includes non-PHY devices.
Just because the PHY that is attached to the netdev *might* support
C45-over-C22 (not every PHY does), this is unique to PHYs and is not
true of every MDIO device. So "upgrading" a C45 access to a
C45-over-C22 could end up corrupting other devices on the bus.

So, I'm afraid that I disagree with your change.

> I agree it's not the best. But is it not better to have the IOCTLs 
> working as they already exists, rather then have it function depending 
> on the combination of if the MAC and PHY both speak C22 or C45?

If the bus supports C22, and userspace asks for a C22 transaction, a
C22 transaction will happen on the bus.

If the bus supports C45, and userspace asks for a C45 transaction, a
C45 transaction will happen on the bus.

Otherwise, an error is returned.

Just because PHYs have this special C45-over-C22 thing, does not mean
that the low level debugging API should use C45-over-C22 for C45
transactions when the bus does not support C45.

I re-iterate, the MII bus access ioctls provide access to any device
on the the bus not specifically the PHY that you have attached to
the netdev. Using the properties of the attached PHY is meaningless
here.
diff mbox series

Patch

diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 4f3e742907cb..89f52bb123aa 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -342,9 +342,12 @@  int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
 		if (mdio_phy_id_is_c45(mii_data->phy_id)) {
 			prtad = mdio_phy_id_prtad(mii_data->phy_id);
 			devad = mdio_phy_id_devad(mii_data->phy_id);
-			ret = mdiobus_c45_read(phydev->mdio.bus, prtad, devad,
-					       mii_data->reg_num);
 
+			mutex_lock(&phydev->mdio.bus->mdio_lock);
+			ret = mmd_phy_read(phydev->mdio.bus, prtad,
+					   phydev->is_c45, devad,
+					   mii_data->reg_num);
+			mutex_unlock(&phydev->mdio.bus->mdio_lock);
 		} else {
 			ret = mdiobus_read(phydev->mdio.bus, mii_data->phy_id,
 					   mii_data->reg_num);
@@ -403,11 +406,14 @@  int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
 			}
 		}
 
-		if (mdio_phy_id_is_c45(mii_data->phy_id))
-			mdiobus_c45_write(phydev->mdio.bus, prtad, devad,
-					  mii_data->reg_num, val);
-		else
+		if (mdio_phy_id_is_c45(mii_data->phy_id)) {
+			mutex_lock(&phydev->mdio.bus->mdio_lock);
+			mmd_phy_write(phydev->mdio.bus, prtad, phydev->is_c45,
+				      devad, mii_data->reg_num, val);
+			mutex_unlock(&phydev->mdio.bus->mdio_lock);
+		} else {
 			mdiobus_write(phydev->mdio.bus, prtad, devad, val);
+		}
 
 		if (prtad == phydev->mdio.addr &&
 		    devad == MII_BMCR &&