diff options
| -rw-r--r-- | drivers/net/phy/phy_link_topology.c | 8 | ||||
| -rw-r--r-- | include/linux/phy_link_topology.h | 5 | ||||
| -rw-r--r-- | net/ethtool/netlink.c | 6 | ||||
| -rw-r--r-- | net/ethtool/netlink.h | 7 | ||||
| -rw-r--r-- | net/ethtool/phy.c | 1 |
5 files changed, 21 insertions, 6 deletions
diff --git a/drivers/net/phy/phy_link_topology.c b/drivers/net/phy/phy_link_topology.c index 1f1eb5d59b38..aed3b26c1674 100644 --- a/drivers/net/phy/phy_link_topology.c +++ b/drivers/net/phy/phy_link_topology.c @@ -10,6 +10,7 @@ #include <linux/phy.h> #include <linux/rtnetlink.h> #include <linux/xarray.h> +#include <net/netdev_lock.h> static int netdev_alloc_phy_link_topology(struct net_device *dev) { @@ -35,6 +36,13 @@ int phy_link_topo_add_phy(struct net_device *dev, struct phy_device_node *pdn; int ret; + /* ethtool ops may run without rtnl_lock, and rtnl_lock is what + * currently protects the PHY topology. No driver currently mixes + * the two, flag if someone tries. See also ethnl_req_get_phydev(). + */ + if (WARN_ON_ONCE(netdev_need_ops_lock(dev))) + return -EOPNOTSUPP; + if (!topo) { ret = netdev_alloc_phy_link_topology(dev); if (ret) diff --git a/include/linux/phy_link_topology.h b/include/linux/phy_link_topology.h index 68a59e25821c..95575f68d5bc 100644 --- a/include/linux/phy_link_topology.h +++ b/include/linux/phy_link_topology.h @@ -36,6 +36,11 @@ struct phy_device_node { struct phy_device *phy; }; +static inline bool phy_link_topo_empty(struct net_device *dev) +{ + return !dev->link_topo; +} + #if IS_ENABLED(CONFIG_PHYLIB) int phy_link_topo_add_phy(struct net_device *dev, struct phy_device *phy, diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index c4054a9795ff..afafed738584 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -226,11 +226,13 @@ struct phy_device *ethnl_req_get_phydev(const struct ethnl_req_info *req_info, { struct phy_device *phydev; - ASSERT_RTNL(); - if (!req_info->dev) return NULL; + /* If there is no PHY in sight there's no need for assert locking */ + if (!phy_link_topo_empty(req_info->dev)) + ASSERT_RTNL(); + if (!req_info->phy_index) return req_info->dev->phydev; diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index f94aaa66379c..4ca2eca2e94b 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -275,14 +275,15 @@ static inline void ethnl_parse_header_dev_put(struct ethnl_req_info *req_info) /** * ethnl_req_get_phydev() - Gets the phy_device targeted by this request, - * if any. Must be called under rntl_lock(). + * if any. * @req_info: The ethnl request to get the phy from. * @tb: The netlink attributes array, for error reporting. * @header: The netlink header index, used for error reporting. * @extack: The netlink extended ACK, for error reporting. * - * The caller must hold RTNL, until it's done interacting with the returned - * phy_device. + * If a phy_device is returned the caller must hold rtnl_lock when calling + * this function, and until it's done interacting with the returned phy_device. + * IOW caller must hold rtnl_lock unless they know netdev has no phy_device. * * Return: A phy_device pointer corresponding either to the passed phy_index * if one is provided. If not, the phy_device attached to the diff --git a/net/ethtool/phy.c b/net/ethtool/phy.c index ddc6eab701ed..018b0412be86 100644 --- a/net/ethtool/phy.c +++ b/net/ethtool/phy.c @@ -78,7 +78,6 @@ static int phy_prepare_data(const struct ethnl_req_info *req_info, struct phy_device *phydev; int ret; - /* RTNL is held by the caller */ phydev = ethnl_req_get_phydev(req_info, tb, ETHTOOL_A_PHY_HEADER, info->extack); if (IS_ERR_OR_NULL(phydev)) |
