Skip to content

Commit 6916e46

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. The PHY index can be re-used for PHYs that are persistent. Signed-off-by: Maxime Chevallier <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent d133ef1 commit 6916e46

File tree

10 files changed

+244
-2
lines changed

10 files changed

+244
-2
lines changed

MAINTAINERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8015,6 +8015,8 @@ F: include/linux/mii.h
80158015
F: include/linux/of_net.h
80168016
F: include/linux/phy.h
80178017
F: include/linux/phy_fixed.h
8018+
F: include/linux/phy_link_topology.h
8019+
F: include/linux/phy_link_topology_core.h
80188020
F: include/linux/phylib_stubs.h
80198021
F: include/linux/platform_data/mdio-bcm-unimac.h
80208022
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>
@@ -1511,6 +1512,11 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
15111512

15121513
if (phydev->sfp_bus_attached)
15131514
dev->sfp_bus = phydev->sfp_bus;
1515+
1516+
err = phy_link_topo_add_phy(dev->link_topo, phydev,
1517+
PHY_UPSTREAM_MAC, dev);
1518+
if (err)
1519+
goto error;
15141520
}
15151521

15161522
/* Some Ethernet drivers try to connect to a PHY device before
@@ -1938,6 +1944,7 @@ void phy_detach(struct phy_device *phydev)
19381944
if (dev) {
19391945
phydev->attached_dev->phydev = NULL;
19401946
phydev->attached_dev = NULL;
1947+
phy_link_topo_del_phy(dev->link_topo, phydev);
19411948
}
19421949
phydev->phylink = NULL;
19431950

drivers/net/phy/phy_link_topology.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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+
struct phy_link_topology *phy_link_topo_create(struct net_device *dev)
16+
{
17+
struct phy_link_topology *topo;
18+
19+
topo = kzalloc(sizeof(*topo), GFP_KERNEL);
20+
if (!topo)
21+
return ERR_PTR(-ENOMEM);
22+
23+
xa_init_flags(&topo->phys, XA_FLAGS_ALLOC1);
24+
topo->next_phy_index = 1;
25+
26+
return topo;
27+
}
28+
29+
void phy_link_topo_destroy(struct phy_link_topology *topo)
30+
{
31+
if (!topo)
32+
return;
33+
34+
xa_destroy(&topo->phys);
35+
kfree(topo);
36+
}
37+
38+
int phy_link_topo_add_phy(struct phy_link_topology *topo,
39+
struct phy_device *phy,
40+
enum phy_upstream upt, void *upstream)
41+
{
42+
struct phy_device_node *pdn;
43+
int ret;
44+
45+
pdn = kzalloc(sizeof(*pdn), GFP_KERNEL);
46+
if (!pdn)
47+
return -ENOMEM;
48+
49+
pdn->phy = phy;
50+
switch (upt) {
51+
case PHY_UPSTREAM_MAC:
52+
pdn->upstream.netdev = (struct net_device *)upstream;
53+
if (phy_on_sfp(phy))
54+
pdn->parent_sfp_bus = pdn->upstream.netdev->sfp_bus;
55+
break;
56+
case PHY_UPSTREAM_PHY:
57+
pdn->upstream.phydev = (struct phy_device *)upstream;
58+
if (phy_on_sfp(phy))
59+
pdn->parent_sfp_bus = pdn->upstream.phydev->sfp_bus;
60+
break;
61+
default:
62+
ret = -EINVAL;
63+
goto err;
64+
}
65+
pdn->upstream_type = upt;
66+
67+
/* Attempt to re-use a previously allocated phy_index */
68+
if (phy->phyindex) {
69+
ret = xa_insert(&topo->phys, phy->phyindex, pdn, GFP_KERNEL);
70+
71+
/* Errors could be either -ENOMEM or -EBUSY. If the phy has an
72+
* index, and there's another entry at the same index, this is
73+
* unexpected and we still error-out
74+
*/
75+
if (ret)
76+
goto err;
77+
return 0;
78+
}
79+
80+
ret = xa_alloc_cyclic(&topo->phys, &phy->phyindex, pdn, xa_limit_32b,
81+
&topo->next_phy_index, GFP_KERNEL);
82+
if (ret)
83+
goto err;
84+
85+
return 0;
86+
87+
err:
88+
kfree(pdn);
89+
return ret;
90+
}
91+
EXPORT_SYMBOL_GPL(phy_link_topo_add_phy);
92+
93+
void phy_link_topo_del_phy(struct phy_link_topology *topo,
94+
struct phy_device *phy)
95+
{
96+
struct phy_device_node *pdn = xa_erase(&topo->phys, phy->phyindex);
97+
98+
/* We delete the PHY from the topology, however we don't re-set the
99+
* phy->phyindex field. If the PHY isn't gone, we can re-assign it the
100+
* same index next time it's added back to the topology
101+
*/
102+
103+
kfree(pdn);
104+
}
105+
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;
@@ -1974,6 +1974,7 @@ enum netdev_reg_state {
19741974
* @fcoe_ddp_xid: Max exchange id for FCoE LRO by ddp
19751975
*
19761976
* @priomap: XXX: need comments on this one
1977+
* @link_topo: Physical link topology tracking attached PHYs
19771978
* @phydev: Physical device may attach itself
19781979
* for hardware timestamping
19791980
* @sfp_bus: attached &struct sfp_bus structure.
@@ -2364,6 +2365,7 @@ struct net_device {
23642365
#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
23652366
struct netprio_map __rcu *priomap;
23662367
#endif
2368+
struct phy_link_topology *link_topo;
23672369
struct phy_device *phydev;
23682370
struct sfp_bus *sfp_bus;
23692371
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
@@ -550,6 +550,9 @@ struct macsec_ops;
550550
* @drv: Pointer to the driver for this PHY instance
551551
* @devlink: Create a link between phy dev and mac dev, if the external phy
552552
* used by current mac interface is managed by another mac interface.
553+
* @phyindex: Unique id across the phy's parent tree of phys to address the PHY
554+
* from userspace, similar to ifindex. A zero index means the PHY
555+
* wasn't assigned an id yet.
553556
* @phy_id: UID for this device found during discovery
554557
* @c45_ids: 802.3-c45 Device Identifiers if is_c45.
555558
* @is_c45: Set to true if this PHY uses clause 45 addressing.
@@ -650,6 +653,7 @@ struct phy_device {
650653

651654
struct device_link *devlink;
652655

656+
u32 phyindex;
653657
u32 phy_id;
654658

655659
struct phy_c45_device_ids c45_ids;

include/linux/phy_link_topology.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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+
struct phy_link_topology {
36+
struct xarray phys;
37+
u32 next_phy_index;
38+
};
39+
40+
static inline struct phy_device *
41+
phy_link_topo_get_phy(struct phy_link_topology *topo, u32 phyindex)
42+
{
43+
struct phy_device_node *pdn = xa_load(&topo->phys, phyindex);
44+
45+
if (pdn)
46+
return pdn->phy;
47+
48+
return NULL;
49+
}
50+
51+
#if IS_REACHABLE(CONFIG_PHYLIB)
52+
int phy_link_topo_add_phy(struct phy_link_topology *topo,
53+
struct phy_device *phy,
54+
enum phy_upstream upt, void *upstream);
55+
56+
void phy_link_topo_del_phy(struct phy_link_topology *lt, struct phy_device *phy);
57+
58+
#else
59+
static inline int phy_link_topo_add_phy(struct phy_link_topology *topo,
60+
struct phy_device *phy,
61+
enum phy_upstream upt, void *upstream)
62+
{
63+
return 0;
64+
}
65+
66+
static inline void phy_link_topo_del_phy(struct phy_link_topology *topo,
67+
struct phy_device *phy)
68+
{
69+
}
70+
#endif
71+
72+
#endif /* __PHY_LINK_TOPOLOGY_H */
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef __PHY_LINK_TOPOLOGY_CORE_H
3+
#define __PHY_LINK_TOPOLOGY_CORE_H
4+
5+
struct phy_link_topology;
6+
7+
#if IS_REACHABLE(CONFIG_PHYLIB)
8+
9+
struct phy_link_topology *phy_link_topo_create(struct net_device *dev);
10+
void phy_link_topo_destroy(struct phy_link_topology *topo);
11+
12+
#else
13+
14+
static inline struct phy_link_topology *phy_link_topo_create(struct net_device *dev)
15+
{
16+
return NULL;
17+
}
18+
19+
static inline void phy_link_topo_destroy(struct phy_link_topology *topo)
20+
{
21+
}
22+
23+
#endif
24+
25+
#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
@@ -2268,4 +2268,20 @@ struct ethtool_link_settings {
22682268
* __u32 map_lp_advertising[link_mode_masks_nwords];
22692269
*/
22702270
};
2271+
2272+
/**
2273+
* enum phy_upstream - Represents the upstream component a given PHY device
2274+
* is connected to, as in what is on the other end of the MII bus. Most PHYs
2275+
* will be attached to an Ethernet MAC controller, but in some cases, there's
2276+
* an intermediate PHY used as a media-converter, which will driver another
2277+
* MII interface as its output.
2278+
* @PHY_UPSTREAM_MAC: Upstream component is a MAC (a switch port,
2279+
* or ethernet controller)
2280+
* @PHY_UPSTREAM_PHY: Upstream component is a PHY (likely a media converter)
2281+
*/
2282+
enum phy_upstream {
2283+
PHY_UPSTREAM_MAC,
2284+
PHY_UPSTREAM_PHY,
2285+
};
2286+
22712287
#endif /* _UAPI_LINUX_ETHTOOL_H */

net/core/dev.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@
158158
#include <net/page_pool/types.h>
159159
#include <net/page_pool/helpers.h>
160160
#include <net/rps.h>
161+
#include <linux/phy_link_topology_core.h>
161162

162163
#include "dev.h"
163164
#include "net-sysfs.h"
@@ -10962,6 +10963,12 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
1096210963
#ifdef CONFIG_NET_SCHED
1096310964
hash_init(dev->qdisc_hash);
1096410965
#endif
10966+
dev->link_topo = phy_link_topo_create(dev);
10967+
if (IS_ERR(dev->link_topo)) {
10968+
dev->link_topo = NULL;
10969+
goto free_all;
10970+
}
10971+
1096510972
dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM;
1096610973
setup(dev);
1096710974

@@ -11050,6 +11057,8 @@ void free_netdev(struct net_device *dev)
1105011057
free_percpu(dev->xdp_bulkq);
1105111058
dev->xdp_bulkq = NULL;
1105211059

11060+
phy_link_topo_destroy(dev->link_topo);
11061+
1105311062
/* Compatibility with error handling in drivers */
1105411063
if (dev->reg_state == NETREG_UNINITIALIZED) {
1105511064
netdev_freemem(dev);

0 commit comments

Comments
 (0)