Skip to content

Commit 02018c5

Browse files
minimaxwelldavem330
authored andcommitted
net: phy: Introduce ethernet link topology representation
Link topologies containing multiple network PHYs attached to the same net_device can be found when using a PHY as a media converter for use with an SFP connector, on which an SFP transceiver containing a PHY can be used. With the current model, the transceiver's PHY can't be used for operations such as cable testing, timestamping, macsec offload, etc. The reason being that most of the logic for these configuration, coming from either ethtool netlink or ioctls tend to use netdev->phydev, which in multi-phy systems will reference the PHY closest to the MAC. Introduce a numbering scheme allowing to enumerate PHY devices that belong to any netdev, which can in turn allow userspace to take more precise decisions with regard to each PHY's configuration. The numbering is maintained per-netdev, in a phy_device_list. The numbering works similarly to a netdevice's ifindex, with identifiers that are only recycled once INT_MAX has been reached. This prevents races that could occur between PHY listing and SFP transceiver removal/insertion. The identifiers are assigned at phy_attach time, as the numbering depends on the netdevice the phy is attached to. Signed-off-by: Maxime Chevallier <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 109bf4c commit 02018c5

File tree

10 files changed

+188
-2
lines changed

10 files changed

+188
-2
lines changed

MAINTAINERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7871,6 +7871,8 @@ F: include/linux/mii.h
78717871
F: include/linux/of_net.h
78727872
F: include/linux/phy.h
78737873
F: include/linux/phy_fixed.h
7874+
F: include/linux/phy_link_topology.h
7875+
F: include/linux/phy_link_topology_core.h
78747876
F: include/linux/phylib_stubs.h
78757877
F: include/linux/platform_data/mdio-bcm-unimac.h
78767878
F: include/linux/platform_data/mdio-gpio.h

drivers/net/phy/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Makefile for Linux PHY drivers
33

44
libphy-y := phy.o phy-c45.o phy-core.o phy_device.o \
5-
linkmode.o
5+
linkmode.o phy_link_topology.o
66
mdio-bus-y += mdio_bus.o mdio_device.o
77

88
ifdef CONFIG_MDIO_DEVICE

drivers/net/phy/phy_device.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <linux/phy.h>
3030
#include <linux/phylib_stubs.h>
3131
#include <linux/phy_led_triggers.h>
32+
#include <linux/phy_link_topology.h>
3233
#include <linux/pse-pd/pse.h>
3334
#include <linux/property.h>
3435
#include <linux/rtnetlink.h>
@@ -1491,6 +1492,11 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
14911492

14921493
if (phydev->sfp_bus_attached)
14931494
dev->sfp_bus = phydev->sfp_bus;
1495+
1496+
err = phy_link_topo_add_phy(&dev->link_topo, phydev,
1497+
PHY_UPSTREAM_MAC, dev);
1498+
if (err)
1499+
goto error;
14941500
}
14951501

14961502
/* Some Ethernet drivers try to connect to a PHY device before
@@ -1820,6 +1826,7 @@ void phy_detach(struct phy_device *phydev)
18201826
if (dev) {
18211827
phydev->attached_dev->phydev = NULL;
18221828
phydev->attached_dev = NULL;
1829+
phy_link_topo_del_phy(&dev->link_topo, phydev);
18231830
}
18241831
phydev->phylink = NULL;
18251832

drivers/net/phy/phy_link_topology.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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<[email protected]>
7+
*/
8+
9+
#include <linux/phy_link_topology.h>
10+
#include <linux/netdevice.h>
11+
#include <linux/phy.h>
12+
#include <linux/rtnetlink.h>
13+
#include <linux/xarray.h>
14+
15+
int phy_link_topo_add_phy(struct phy_link_topology *topo,
16+
struct phy_device *phy,
17+
enum phy_upstream upt, void *upstream)
18+
{
19+
struct phy_device_node *pdn;
20+
int ret;
21+
22+
pdn = kzalloc(sizeof(*pdn), GFP_KERNEL);
23+
if (!pdn)
24+
return -ENOMEM;
25+
26+
pdn->phy = phy;
27+
switch (upt) {
28+
case PHY_UPSTREAM_MAC:
29+
pdn->upstream.netdev = (struct net_device *)upstream;
30+
if (phy_on_sfp(phy))
31+
pdn->parent_sfp_bus = pdn->upstream.netdev->sfp_bus;
32+
break;
33+
case PHY_UPSTREAM_PHY:
34+
pdn->upstream.phydev = (struct phy_device *)upstream;
35+
if (phy_on_sfp(phy))
36+
pdn->parent_sfp_bus = pdn->upstream.phydev->sfp_bus;
37+
break;
38+
default:
39+
ret = -EINVAL;
40+
goto err;
41+
}
42+
pdn->upstream_type = upt;
43+
44+
ret = xa_alloc_cyclic(&topo->phys, &phy->phyindex, pdn, xa_limit_32b,
45+
&topo->next_phy_index, GFP_KERNEL);
46+
if (ret)
47+
goto err;
48+
49+
return 0;
50+
51+
err:
52+
kfree(pdn);
53+
return ret;
54+
}
55+
EXPORT_SYMBOL_GPL(phy_link_topo_add_phy);
56+
57+
void phy_link_topo_del_phy(struct phy_link_topology *topo,
58+
struct phy_device *phy)
59+
{
60+
struct phy_device_node *pdn = xa_erase(&topo->phys, phy->phyindex);
61+
62+
phy->phyindex = 0;
63+
64+
kfree(pdn);
65+
}
66+
EXPORT_SYMBOL_GPL(phy_link_topo_del_phy);

include/linux/netdevice.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
#include <net/dcbnl.h>
4141
#endif
4242
#include <net/netprio_cgroup.h>
43-
4443
#include <linux/netdev_features.h>
4544
#include <linux/neighbour.h>
4645
#include <uapi/linux/netdevice.h>
@@ -52,6 +51,7 @@
5251
#include <net/net_trackers.h>
5352
#include <net/net_debug.h>
5453
#include <net/dropreason-core.h>
54+
#include <linux/phy_link_topology_core.h>
5555

5656
struct netpoll_info;
5757
struct device;
@@ -2047,6 +2047,7 @@ enum netdev_stat_type {
20472047
* @fcoe_ddp_xid: Max exchange id for FCoE LRO by ddp
20482048
*
20492049
* @priomap: XXX: need comments on this one
2050+
* @link_topo: Physical link topology tracking attached PHYs
20502051
* @phydev: Physical device may attach itself
20512052
* for hardware timestamping
20522053
* @sfp_bus: attached &struct sfp_bus structure.
@@ -2441,6 +2442,7 @@ struct net_device {
24412442
#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
24422443
struct netprio_map __rcu *priomap;
24432444
#endif
2445+
struct phy_link_topology link_topo;
24442446
struct phy_device *phydev;
24452447
struct sfp_bus *sfp_bus;
24462448
struct lock_class_key *qdisc_tx_busylock;

include/linux/phy.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,9 @@ struct macsec_ops;
547547
* @drv: Pointer to the driver for this PHY instance
548548
* @devlink: Create a link between phy dev and mac dev, if the external phy
549549
* used by current mac interface is managed by another mac interface.
550+
* @phyindex: Unique id across the phy's parent tree of phys to address the PHY
551+
* from userspace, similar to ifindex. A zero index means the PHY
552+
* wasn't assigned an id yet.
550553
* @phy_id: UID for this device found during discovery
551554
* @c45_ids: 802.3-c45 Device Identifiers if is_c45.
552555
* @is_c45: Set to true if this PHY uses clause 45 addressing.
@@ -646,6 +649,7 @@ struct phy_device {
646649

647650
struct device_link *devlink;
648651

652+
u32 phyindex;
649653
u32 phy_id;
650654

651655
struct phy_c45_device_ids c45_ids;

include/linux/phy_link_topology.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* PHY device list allow maintaining a list of PHY devices that are
4+
* part of a netdevice's link topology. PHYs can for example be chained,
5+
* as is the case when using a PHY that exposes an SFP module, on which an
6+
* SFP transceiver that embeds a PHY is connected.
7+
*
8+
* This list can then be used by userspace to leverage individual PHY
9+
* capabilities.
10+
*/
11+
#ifndef __PHY_LINK_TOPOLOGY_H
12+
#define __PHY_LINK_TOPOLOGY_H
13+
14+
#include <linux/ethtool.h>
15+
#include <linux/phy_link_topology_core.h>
16+
17+
struct xarray;
18+
struct phy_device;
19+
struct net_device;
20+
struct sfp_bus;
21+
22+
struct phy_device_node {
23+
enum phy_upstream upstream_type;
24+
25+
union {
26+
struct net_device *netdev;
27+
struct phy_device *phydev;
28+
} upstream;
29+
30+
struct sfp_bus *parent_sfp_bus;
31+
32+
struct phy_device *phy;
33+
};
34+
35+
static inline struct phy_device *
36+
phy_link_topo_get_phy(struct phy_link_topology *topo, u32 phyindex)
37+
{
38+
struct phy_device_node *pdn = xa_load(&topo->phys, phyindex);
39+
40+
if (pdn)
41+
return pdn->phy;
42+
43+
return NULL;
44+
}
45+
46+
#if IS_ENABLED(CONFIG_PHYLIB)
47+
int phy_link_topo_add_phy(struct phy_link_topology *topo,
48+
struct phy_device *phy,
49+
enum phy_upstream upt, void *upstream);
50+
51+
void phy_link_topo_del_phy(struct phy_link_topology *lt, struct phy_device *phy);
52+
53+
#else
54+
static inline int phy_link_topo_add_phy(struct phy_link_topology *topo,
55+
struct phy_device *phy,
56+
enum phy_upstream upt, void *upstream)
57+
{
58+
return 0;
59+
}
60+
61+
static inline void phy_link_topo_del_phy(struct phy_link_topology *topo,
62+
struct phy_device *phy)
63+
{
64+
}
65+
#endif
66+
67+
#endif /* __PHY_LINK_TOPOLOGY_H */
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef __PHY_LINK_TOPOLOGY_CORE_H
3+
#define __PHY_LINK_TOPOLOGY_CORE_H
4+
5+
struct xarray;
6+
7+
struct phy_link_topology {
8+
struct xarray phys;
9+
10+
u32 next_phy_index;
11+
};
12+
13+
static inline void phy_link_topo_init(struct phy_link_topology *topo)
14+
{
15+
xa_init_flags(&topo->phys, XA_FLAGS_ALLOC1);
16+
topo->next_phy_index = 1;
17+
}
18+
19+
#endif /* __PHY_LINK_TOPOLOGY_CORE_H */

include/uapi/linux/ethtool.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2219,4 +2219,20 @@ struct ethtool_link_settings {
22192219
* __u32 map_lp_advertising[link_mode_masks_nwords];
22202220
*/
22212221
};
2222+
2223+
/**
2224+
* enum phy_upstream - Represents the upstream component a given PHY device
2225+
* is connected to, as in what is on the other end of the MII bus. Most PHYs
2226+
* will be attached to an Ethernet MAC controller, but in some cases, there's
2227+
* an intermediate PHY used as a media-converter, which will driver another
2228+
* MII interface as its output.
2229+
* @PHY_UPSTREAM_MAC: Upstream component is a MAC (a switch port,
2230+
* or ethernet controller)
2231+
* @PHY_UPSTREAM_PHY: Upstream component is a PHY (likely a media converter)
2232+
*/
2233+
enum phy_upstream {
2234+
PHY_UPSTREAM_MAC,
2235+
PHY_UPSTREAM_PHY,
2236+
};
2237+
22222238
#endif /* _UAPI_LINUX_ETHTOOL_H */

net/core/dev.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@
153153
#include <linux/prandom.h>
154154
#include <linux/once_lite.h>
155155
#include <net/netdev_rx_queue.h>
156+
#include <linux/phy_link_topology_core.h>
156157

157158
#include "dev.h"
158159
#include "net-sysfs.h"
@@ -10875,6 +10876,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
1087510876
#ifdef CONFIG_NET_SCHED
1087610877
hash_init(dev->qdisc_hash);
1087710878
#endif
10879+
phy_link_topo_init(&dev->link_topo);
10880+
1087810881
dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM;
1087910882
setup(dev);
1088010883

0 commit comments

Comments
 (0)