xref: /linux/drivers/net/phy/phy_link_topology.c (revision 97f51bf91b3afa8819fa10e9282e3f2328bb78e4)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Infrastructure to handle all PHY devices connected to a given netdev,
4  * either directly or indirectly attached.
5  *
6  * Copyright (c) 2023 Maxime Chevallier<maxime.chevallier@bootlin.com>
7  */
8 
9 #include <linux/phy_link_topology.h>
10 #include <linux/phy.h>
11 #include <linux/rtnetlink.h>
12 #include <linux/xarray.h>
13 #include <net/netdev_lock.h>
14 
15 static int netdev_alloc_phy_link_topology(struct net_device *dev)
16 {
17 	struct phy_link_topology *topo;
18 
19 	topo = kzalloc_obj(*topo);
20 	if (!topo)
21 		return -ENOMEM;
22 
23 	xa_init_flags(&topo->phys, XA_FLAGS_ALLOC1);
24 	topo->next_phy_index = 1;
25 
26 	dev->link_topo = topo;
27 
28 	return 0;
29 }
30 
31 int phy_link_topo_add_phy(struct net_device *dev,
32 			  struct phy_device *phy,
33 			  enum phy_upstream upt, void *upstream)
34 {
35 	struct phy_link_topology *topo = dev->link_topo;
36 	struct phy_device_node *pdn;
37 	int ret;
38 
39 	/* ethtool ops may run without rtnl_lock, and rtnl_lock is what
40 	 * currently protects the PHY topology. No driver currently mixes
41 	 * the two, flag if someone tries. See also:
42 	 *  - ethnl_req_get_phydev()
43 	 *  - phy_detach()
44 	 */
45 	if (WARN_ON_ONCE(netdev_need_ops_lock(dev)))
46 		return -EOPNOTSUPP;
47 
48 	if (!topo) {
49 		ret = netdev_alloc_phy_link_topology(dev);
50 		if (ret)
51 			return ret;
52 
53 		topo = dev->link_topo;
54 	}
55 
56 	pdn = kzalloc_obj(*pdn);
57 	if (!pdn)
58 		return -ENOMEM;
59 
60 	pdn->phy = phy;
61 	switch (upt) {
62 	case PHY_UPSTREAM_MAC:
63 		pdn->upstream.netdev = (struct net_device *)upstream;
64 		if (phy_on_sfp(phy))
65 			pdn->parent_sfp_bus = pdn->upstream.netdev->sfp_bus;
66 		break;
67 	case PHY_UPSTREAM_PHY:
68 		pdn->upstream.phydev = (struct phy_device *)upstream;
69 		if (phy_on_sfp(phy))
70 			pdn->parent_sfp_bus = pdn->upstream.phydev->sfp_bus;
71 		break;
72 	default:
73 		ret = -EINVAL;
74 		goto err;
75 	}
76 	pdn->upstream_type = upt;
77 
78 	/* Attempt to re-use a previously allocated phy_index */
79 	if (phy->phyindex)
80 		ret = xa_insert(&topo->phys, phy->phyindex, pdn, GFP_KERNEL);
81 	else
82 		ret = xa_alloc_cyclic(&topo->phys, &phy->phyindex, pdn,
83 				      xa_limit_32b, &topo->next_phy_index,
84 				      GFP_KERNEL);
85 
86 	if (ret < 0)
87 		goto err;
88 
89 	return 0;
90 
91 err:
92 	kfree(pdn);
93 	return ret;
94 }
95 EXPORT_SYMBOL_GPL(phy_link_topo_add_phy);
96 
97 void phy_link_topo_del_phy(struct net_device *dev,
98 			   struct phy_device *phy)
99 {
100 	struct phy_link_topology *topo = dev->link_topo;
101 	struct phy_device_node *pdn;
102 
103 	if (!topo)
104 		return;
105 
106 	pdn = xa_erase(&topo->phys, phy->phyindex);
107 
108 	/* We delete the PHY from the topology, however we don't re-set the
109 	 * phy->phyindex field. If the PHY isn't gone, we can re-assign it the
110 	 * same index next time it's added back to the topology
111 	 */
112 
113 	kfree(pdn);
114 }
115 EXPORT_SYMBOL_GPL(phy_link_topo_del_phy);
116