aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2018-09-26 20:04:11 -0700
committerDavid S. Miller <davem@davemloft.net>2018-09-26 20:04:11 -0700
commit30ed48719282aecf6728f6777fefe8440bf6ed5a (patch)
treec36973bf5980fc937c213c93f6b773068ffbc21e
parentRevert "net: phy: fix WoL handling when suspending the PHY" (diff)
parentnet: phy: fix WoL handling when suspending the PHY (diff)
downloadlinux-dev-30ed48719282aecf6728f6777fefe8440bf6ed5a.tar.xz
linux-dev-30ed48719282aecf6728f6777fefe8440bf6ed5a.zip
Merge branch 'net-phy-fix-WoL-handling-when-suspending-the-PHY'
Heiner Kallweit says: ==================== net: phy: fix WoL handling when suspending the PHY phy_suspend doesn't always recognize that WoL is enabled and therefore suspends the PHY when it should not. First idea to address the issue was to reuse checks used in mdio_bus_phy_may_suspend which check whether relevant devices are wakeup-enabled. Florian raised some concerns because drivers may enable wakeup even if WoL isn't enabled (e.g. certain USB network drivers). The new approach focuses on reducing the risk to break existing stuff. We add a flag wol_enabled to struct net_device which is set in ethtool_set_wol(). Then this flag is checked in phy_suspend(). This doesn't cover 100% of the cases yet (e.g. if WoL is enabled w/o explicit configuration), but it covers the most relevant cases with very little risk of regressions. v2: - Fix a typo ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/phy/phy_device.c12
-rw-r--r--include/linux/netdevice.h3
-rw-r--r--net/core/ethtool.c9
3 files changed, 20 insertions, 4 deletions
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index db1172db1e7c..19ab8a7d1e48 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -93,7 +93,12 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
if (!netdev)
return !phydev->suspended;
- /* Don't suspend PHY if the attached netdev parent may wakeup.
+ if (netdev->wol_enabled)
+ return false;
+
+ /* As long as not all affected network drivers support the
+ * wol_enabled flag, let's check for hints that WoL is enabled.
+ * Don't suspend PHY if the attached netdev parent may wake up.
* The parent may point to a PCI device, as in tg3 driver.
*/
if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent))
@@ -1132,9 +1137,9 @@ void phy_detach(struct phy_device *phydev)
sysfs_remove_link(&dev->dev.kobj, "phydev");
sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev");
}
+ phy_suspend(phydev);
phydev->attached_dev->phydev = NULL;
phydev->attached_dev = NULL;
- phy_suspend(phydev);
phydev->phylink = NULL;
phy_led_triggers_unregister(phydev);
@@ -1168,12 +1173,13 @@ EXPORT_SYMBOL(phy_detach);
int phy_suspend(struct phy_device *phydev)
{
struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
+ struct net_device *netdev = phydev->attached_dev;
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)
+ if (wol.wolopts || (netdev && netdev->wol_enabled))
return -EBUSY;
if (phydev->drv && phydrv->suspend)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index ca5ab98053c8..c7861e4b402c 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1730,6 +1730,8 @@ enum netdev_priv_flags {
* switch driver and used to set the phys state of the
* switch port.
*
+ * @wol_enabled: Wake-on-LAN is enabled
+ *
* FIXME: cleanup struct net_device such that network protocol info
* moves out.
*/
@@ -2014,6 +2016,7 @@ struct net_device {
struct lock_class_key *qdisc_tx_busylock;
struct lock_class_key *qdisc_running_key;
bool proto_down;
+ unsigned wol_enabled:1;
};
#define to_net_dev(d) container_of(d, struct net_device, dev)
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 234a0ec2e932..0762aaf8e964 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -1483,6 +1483,7 @@ static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
{
struct ethtool_wolinfo wol;
+ int ret;
if (!dev->ethtool_ops->set_wol)
return -EOPNOTSUPP;
@@ -1490,7 +1491,13 @@ static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
if (copy_from_user(&wol, useraddr, sizeof(wol)))
return -EFAULT;
- return dev->ethtool_ops->set_wol(dev, &wol);
+ ret = dev->ethtool_ops->set_wol(dev, &wol);
+ if (ret)
+ return ret;
+
+ dev->wol_enabled = !!wol.wolopts;
+
+ return 0;
}
static int ethtool_get_eee(struct net_device *dev, char __user *useraddr)