summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/phy/phy_link_topology.c8
-rw-r--r--include/linux/phy_link_topology.h5
-rw-r--r--net/ethtool/netlink.c6
-rw-r--r--net/ethtool/netlink.h7
-rw-r--r--net/ethtool/phy.c1
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))