On 05/23/2018 12:31 PM, Heiner Kallweit wrote: > If the parent of the MDIO bus is runtime-suspended, we may not be able > to access the MDIO bus. Therefore add a check for this situation. > > So far phy_suspend() only checks for WoL being enabled, other checks > are in mdio_bus_phy_may_suspend(). Improve this and move all checks > to a new function phy_may_suspend() and call it from phy_suspend(). > > Signed-off-by: Heiner Kallweit <hkallwe...@gmail.com> > --- > drivers/net/phy/phy_device.c | 33 +++++++++++++++++++++------------ > 1 file changed, 21 insertions(+), 12 deletions(-) > > diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c > index 1662781fb..e0a71e3e5 100644 > --- a/drivers/net/phy/phy_device.c > +++ b/drivers/net/phy/phy_device.c > @@ -35,6 +35,7 @@ > #include <linux/io.h> > #include <linux/uaccess.h> > #include <linux/of.h> > +#include <linux/pm_runtime.h> > > #include <asm/irq.h> > > @@ -75,14 +76,27 @@ extern struct phy_driver genphy_10g_driver; > static LIST_HEAD(phy_fixup_list); > static DEFINE_MUTEX(phy_fixup_lock); > > -#ifdef CONFIG_PM > -static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) > +static bool phy_may_suspend(struct phy_device *phydev) > { > struct device_driver *drv = phydev->mdio.dev.driver; > struct phy_driver *phydrv = to_phy_driver(drv); > struct net_device *netdev = phydev->attached_dev; > + struct device *mdio_bus_parent = phydev->mdio.bus->parent; > + struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; > + > + if (phydev->suspended || !drv || !phydrv->suspend) > + return false; > + > + /* If the device has WOL enabled, we cannot suspend the PHY */ > + phy_ethtool_get_wol(phydev, &wol); > + if (wol.wolopts) > + return false;
phy_ethtool_get_wol() can created MDIO bus accesses so should not this be moved after the check for the MDIO bus being runtime suspended? > > - if (!drv || !phydrv->suspend) > + /* If the parent of the MDIO bus is runtime-suspended, the MDIO bus may > + * not be accessible and we expect the parent to suspend all devices > + * on the MDIO bus when it suspends. > + */ > + if (mdio_bus_parent && pm_runtime_suspended(mdio_bus_parent)) > return false; > > /* PHY not attached? May suspend if the PHY has not already been > @@ -91,7 +105,7 @@ static bool mdio_bus_phy_may_suspend(struct phy_device > *phydev) > * MDIO bus driver and clock gated at this point. > */ > if (!netdev) > - return !phydev->suspended; > + return true; > > /* Don't suspend PHY if the attached netdev parent may wakeup. > * The parent may point to a PCI device, as in tg3 driver. > @@ -109,6 +123,7 @@ static bool mdio_bus_phy_may_suspend(struct phy_device > *phydev) > return true; > } > > +#ifdef CONFIG_PM > static int mdio_bus_phy_suspend(struct device *dev) > { > struct phy_device *phydev = to_phy_device(dev); > @@ -121,9 +136,6 @@ static int mdio_bus_phy_suspend(struct device *dev) > if (phydev->attached_dev && phydev->adjust_link) > phy_stop_machine(phydev); > > - if (!mdio_bus_phy_may_suspend(phydev)) > - return 0; Hummm why is it okay to drop that one? > - > return phy_suspend(phydev); > } > > @@ -1162,13 +1174,10 @@ EXPORT_SYMBOL(phy_detach); > int phy_suspend(struct phy_device *phydev) > { > struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver); > - struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; > int ret = 0; > > - /* If the device has WOL enabled, we cannot suspend the PHY */ > - phy_ethtool_get_wol(phydev, &wol); > - if (wol.wolopts) > - return -EBUSY; > + if (!phy_may_suspend(phydev)) > + return 0; > > if (phydev->drv && phydrv->suspend) > ret = phydrv->suspend(phydev); > -- Florian