aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2018-09-26 23:04:11 -0400
committerDavid S. Miller <davem@davemloft.net>2018-09-26 23:04:11 -0400
commit30ed48719282aecf6728f6777fefe8440bf6ed5a (patch)
treec36973bf5980fc937c213c93f6b773068ffbc21e
parentd31d1d03aa909aa6257d9d581eb0eb5d0ed366e2 (diff)
parent93f41e67dc8ff0fd987120a6ef2717f21462c534 (diff)
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)
93 if (!netdev) 93 if (!netdev)
94 return !phydev->suspended; 94 return !phydev->suspended;
95 95
96 /* Don't suspend PHY if the attached netdev parent may wakeup. 96 if (netdev->wol_enabled)
97 return false;
98
99 /* As long as not all affected network drivers support the
100 * wol_enabled flag, let's check for hints that WoL is enabled.
101 * Don't suspend PHY if the attached netdev parent may wake up.
97 * The parent may point to a PCI device, as in tg3 driver. 102 * The parent may point to a PCI device, as in tg3 driver.
98 */ 103 */
99 if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent)) 104 if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent))
@@ -1132,9 +1137,9 @@ void phy_detach(struct phy_device *phydev)
1132 sysfs_remove_link(&dev->dev.kobj, "phydev"); 1137 sysfs_remove_link(&dev->dev.kobj, "phydev");
1133 sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev"); 1138 sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev");
1134 } 1139 }
1140 phy_suspend(phydev);
1135 phydev->attached_dev->phydev = NULL; 1141 phydev->attached_dev->phydev = NULL;
1136 phydev->attached_dev = NULL; 1142 phydev->attached_dev = NULL;
1137 phy_suspend(phydev);
1138 phydev->phylink = NULL; 1143 phydev->phylink = NULL;
1139 1144
1140 phy_led_triggers_unregister(phydev); 1145 phy_led_triggers_unregister(phydev);
@@ -1168,12 +1173,13 @@ EXPORT_SYMBOL(phy_detach);
1168int phy_suspend(struct phy_device *phydev) 1173int phy_suspend(struct phy_device *phydev)
1169{ 1174{
1170 struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver); 1175 struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
1176 struct net_device *netdev = phydev->attached_dev;
1171 struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; 1177 struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
1172 int ret = 0; 1178 int ret = 0;
1173 1179
1174 /* If the device has WOL enabled, we cannot suspend the PHY */ 1180 /* If the device has WOL enabled, we cannot suspend the PHY */
1175 phy_ethtool_get_wol(phydev, &wol); 1181 phy_ethtool_get_wol(phydev, &wol);
1176 if (wol.wolopts) 1182 if (wol.wolopts || (netdev && netdev->wol_enabled))
1177 return -EBUSY; 1183 return -EBUSY;
1178 1184
1179 if (phydev->drv && phydrv->suspend) 1185 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 {
1730 * switch driver and used to set the phys state of the 1730 * switch driver and used to set the phys state of the
1731 * switch port. 1731 * switch port.
1732 * 1732 *
1733 * @wol_enabled: Wake-on-LAN is enabled
1734 *
1733 * FIXME: cleanup struct net_device such that network protocol info 1735 * FIXME: cleanup struct net_device such that network protocol info
1734 * moves out. 1736 * moves out.
1735 */ 1737 */
@@ -2014,6 +2016,7 @@ struct net_device {
2014 struct lock_class_key *qdisc_tx_busylock; 2016 struct lock_class_key *qdisc_tx_busylock;
2015 struct lock_class_key *qdisc_running_key; 2017 struct lock_class_key *qdisc_running_key;
2016 bool proto_down; 2018 bool proto_down;
2019 unsigned wol_enabled:1;
2017}; 2020};
2018#define to_net_dev(d) container_of(d, struct net_device, dev) 2021#define to_net_dev(d) container_of(d, struct net_device, dev)
2019 2022
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)
1483static int ethtool_set_wol(struct net_device *dev, char __user *useraddr) 1483static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
1484{ 1484{
1485 struct ethtool_wolinfo wol; 1485 struct ethtool_wolinfo wol;
1486 int ret;
1486 1487
1487 if (!dev->ethtool_ops->set_wol) 1488 if (!dev->ethtool_ops->set_wol)
1488 return -EOPNOTSUPP; 1489 return -EOPNOTSUPP;
@@ -1490,7 +1491,13 @@ static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
1490 if (copy_from_user(&wol, useraddr, sizeof(wol))) 1491 if (copy_from_user(&wol, useraddr, sizeof(wol)))
1491 return -EFAULT; 1492 return -EFAULT;
1492 1493
1493 return dev->ethtool_ops->set_wol(dev, &wol); 1494 ret = dev->ethtool_ops->set_wol(dev, &wol);
1495 if (ret)
1496 return ret;
1497
1498 dev->wol_enabled = !!wol.wolopts;
1499
1500 return 0;
1494} 1501}
1495 1502
1496static int ethtool_get_eee(struct net_device *dev, char __user *useraddr) 1503static int ethtool_get_eee(struct net_device *dev, char __user *useraddr)