diff mbox

[2/2] net: phy: Ensure the MDIO bus module is held

Message ID 1406074570-13246-3-git-send-email-ezequiel.garcia@free-electrons.com
State Superseded, archived
Delegated to: David Miller
Headers show

Commit Message

Ezequiel Garcia July 23, 2014, 12:16 a.m. UTC
This commit adds proper module_{get,put} to prevent the MDIO bus module
from being unloaded while the phydev is connected. By doing so, we fix
a kernel panic produced when a MDIO driver is removed, but the phydev
that relies on it is attached and running.

Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
---
 drivers/net/phy/phy_device.c | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

Comments

Florian Fainelli July 23, 2014, 6:22 p.m. UTC | #1
2014-07-22 17:16 GMT-07:00 Ezequiel Garcia <ezequiel.garcia@free-electrons.com>:
> This commit adds proper module_{get,put} to prevent the MDIO bus module
> from being unloaded while the phydev is connected. By doing so, we fix
> a kernel panic produced when a MDIO driver is removed, but the phydev
> that relies on it is attached and running.
>
> Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
> ---
>  drivers/net/phy/phy_device.c | 23 ++++++++++++++++++++---
>  1 file changed, 20 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
> index 35d753d..a10dc47 100644
> --- a/drivers/net/phy/phy_device.c
> +++ b/drivers/net/phy/phy_device.c
> @@ -575,6 +575,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
>                       u32 flags, phy_interface_t interface)
>  {
>         struct device *d = &phydev->dev;
> +       struct module *bus_module;
>         int err;
>
>         /* Assume that if there is no driver, that it doesn't
> @@ -599,6 +600,14 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
>                 return -EBUSY;
>         }
>
> +       /* Increment the bus module reference count */
> +       bus_module = phydev->bus->dev.driver ?
> +                    phydev->bus->dev.driver->owner : NULL;
> +       if (!try_module_get(bus_module)) {
> +               dev_err(&dev->dev, "failed to get the bus module\n");
> +               return -EIO;
> +       }
> +
>         phydev->attached_dev = dev;
>         dev->phydev = phydev;
>
> @@ -614,10 +623,14 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
>          */
>         err = phy_init_hw(phydev);
>         if (err)
> -               phy_detach(phydev);
> -       else
> -               phy_resume(phydev);
> +               goto err_module_put;
>
> +       phy_resume(phydev);
> +       return err;
> +
> +err_module_put:
> +       module_put(bus_module);
> +       phy_detach(phydev);

Since we are calling phy_detach() which is also attempting to do a
module_put(), does not that result in one too many calls to
module_put() on error path?

>         return err;
>  }
>  EXPORT_SYMBOL(phy_attach_direct);
> @@ -664,6 +677,10 @@ EXPORT_SYMBOL(phy_attach);
>  void phy_detach(struct phy_device *phydev)
>  {
>         int i;
> +
> +       if (phydev->bus->dev.driver)
> +               module_put(phydev->bus->dev.driver->owner);
> +
>         phydev->attached_dev->phydev = NULL;
>         phydev->attached_dev = NULL;
>         phy_suspend(phydev);
> --
> 2.0.1
>
Ezequiel Garcia July 23, 2014, 6:40 p.m. UTC | #2
On 23 Jul 11:22 AM, Florian Fainelli wrote:
> 2014-07-22 17:16 GMT-07:00 Ezequiel Garcia <ezequiel.garcia@free-electrons.com>:
> > @@ -614,10 +623,14 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
> >          */
> >         err = phy_init_hw(phydev);
> >         if (err)
> > -               phy_detach(phydev);
> > -       else
> > -               phy_resume(phydev);
> > +               goto err_module_put;
> >
> > +       phy_resume(phydev);
> > +       return err;
> > +
> > +err_module_put:
> > +       module_put(bus_module);
> > +       phy_detach(phydev);
> 
> Since we are calling phy_detach() which is also attempting to do a
> module_put(), does not that result in one too many calls to
> module_put() on error path?
> 

As far as I can see all that module_get() and module_put() do
is increment and decrement refcounters. Therefore, I think you can
have as many module_{get,put}() calls as you want on a path.
Florian Fainelli July 23, 2014, 6:46 p.m. UTC | #3
2014-07-23 11:40 GMT-07:00 Ezequiel Garcia <ezequiel.garcia@free-electrons.com>:
> On 23 Jul 11:22 AM, Florian Fainelli wrote:
>> 2014-07-22 17:16 GMT-07:00 Ezequiel Garcia <ezequiel.garcia@free-electrons.com>:
>> > @@ -614,10 +623,14 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
>> >          */
>> >         err = phy_init_hw(phydev);
>> >         if (err)
>> > -               phy_detach(phydev);
>> > -       else
>> > -               phy_resume(phydev);
>> > +               goto err_module_put;
>> >
>> > +       phy_resume(phydev);
>> > +       return err;
>> > +
>> > +err_module_put:
>> > +       module_put(bus_module);
>> > +       phy_detach(phydev);
>>
>> Since we are calling phy_detach() which is also attempting to do a
>> module_put(), does not that result in one too many calls to
>> module_put() on error path?
>>
>
> As far as I can see all that module_get() and module_put() do
> is increment and decrement refcounters. Therefore, I think you can
> have as many module_{get,put}() calls as you want on a path.

Ok, I was concerned that in case that specific PHY device cannot be
registered, we might be decrementing the reference count twice, hence
making it 0. There might be another PHY device sitting on this same
MDIO bus, and since the reference count for the MDIO driver is now 0,
we could end up unloading that module.

Exercising that specific case is not probably easy to trigger.
Ezequiel Garcia July 23, 2014, 6:54 p.m. UTC | #4
On 23 Jul 03:40 PM, Ezequiel Garcia wrote:
> On 23 Jul 11:22 AM, Florian Fainelli wrote:
> > 2014-07-22 17:16 GMT-07:00 Ezequiel Garcia <ezequiel.garcia@free-electrons.com>:
> > > @@ -614,10 +623,14 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
> > >          */
> > >         err = phy_init_hw(phydev);
> > >         if (err)
> > > -               phy_detach(phydev);
> > > -       else
> > > -               phy_resume(phydev);
> > > +               goto err_module_put;
> > >
> > > +       phy_resume(phydev);
> > > +       return err;
> > > +
> > > +err_module_put:
> > > +       module_put(bus_module);
> > > +       phy_detach(phydev);
> > 
> > Since we are calling phy_detach() which is also attempting to do a
> > module_put(), does not that result in one too many calls to
> > module_put() on error path?
> > 
> 
> As far as I can see all that module_get() and module_put() do
> is increment and decrement refcounters. Therefore, I think you can
> have as many module_{get,put}() calls as you want on a path.

After reading this again, I know see what your pointing. The call to
phy_detach already puts the module, so we don't need to do it on the
error path above.

I'll cook up v2.
diff mbox

Patch

diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 35d753d..a10dc47 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -575,6 +575,7 @@  int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 		      u32 flags, phy_interface_t interface)
 {
 	struct device *d = &phydev->dev;
+	struct module *bus_module;
 	int err;
 
 	/* Assume that if there is no driver, that it doesn't
@@ -599,6 +600,14 @@  int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 		return -EBUSY;
 	}
 
+	/* Increment the bus module reference count */
+	bus_module = phydev->bus->dev.driver ?
+		     phydev->bus->dev.driver->owner : NULL;
+	if (!try_module_get(bus_module)) {
+		dev_err(&dev->dev, "failed to get the bus module\n");
+		return -EIO;
+	}
+
 	phydev->attached_dev = dev;
 	dev->phydev = phydev;
 
@@ -614,10 +623,14 @@  int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 	 */
 	err = phy_init_hw(phydev);
 	if (err)
-		phy_detach(phydev);
-	else
-		phy_resume(phydev);
+		goto err_module_put;
 
+	phy_resume(phydev);
+	return err;
+
+err_module_put:
+	module_put(bus_module);
+	phy_detach(phydev);
 	return err;
 }
 EXPORT_SYMBOL(phy_attach_direct);
@@ -664,6 +677,10 @@  EXPORT_SYMBOL(phy_attach);
 void phy_detach(struct phy_device *phydev)
 {
 	int i;
+
+	if (phydev->bus->dev.driver)
+		module_put(phydev->bus->dev.driver->owner);
+
 	phydev->attached_dev->phydev = NULL;
 	phydev->attached_dev = NULL;
 	phy_suspend(phydev);