Skip to content

Commit 9fb3dc1

Browse files
committed
Merge branch 'phy-listing-link_topology-tracking'
Maxime Chevallier says: ==================== Introduce PHY listing and link_topology tracking Here's a V5 of the multi-PHY support series. At a glance, besides some minor fixes and R'd-by from Andrew, one of the thing this series does is remove the ASSERT_RTNL() from the topo_add_phy/del_phy operations. These operations will take a PHY device and put it into the list of devices associated to a netdevice. The main thing to protect here is the list itself, but since we use xarrays, my naive understanding of it is that it contains its own protection scheme. There shouldn't be a need for more locking, as the insertion/deletion paths are already hooked into the PHY connection to a netdev, or disconnection from it. Now for the rest of the cover : As a remainder, this ongoing work aims ultimately at supporting complex link topologies that involve multiplexing multiple PHYs/SFPs on a single netdevice. As a first step, it's required that we are able to enumerate the PHYs on a given ethernet interface. By just doing so, we also improve already-existing use-cases, namely the copper SFP modules support when a media-converter is used (as we have 2 PHYs on the link, but only one is referenced by net_device.phydev, which is used on a variety of netlink commands). The series is architectured as follows : - The first patch adds the notion of phy_link_topology, which tracks all PHYs attached to a netdevice. - Patches 2, 3 and 4 adds some plumbing into SFP and phylib to be able to connect the dots when building the topology tree, to know which PHY is connected to which SFP bus, trying not to be too invasive on phylib. - Patch 5 allows passing a PHY_INDEX to ethnl commands. I'm uncertain about this, as there are at least 4 netlink commands ( 5 with the one introduced in patch 7 ) that targets PHYs directly or indirectly, which to me makes it worth-it to have a generic way to pass a PHY index to commands, however the approach taken may be too generic. - Patch 6 is the netlink spec update + ethtool-user.c|h autogenerated code update (the autogenerated code triggers checkpatch warning though) - Patch 7 introduces a new netlink command set to list PHYs on a netdevice. It implements a custom DUMP and GET operation to allow filtered dumps, that lists all PHYs on a given netdevice. I couldn't use most of ethnl's plumbing though. - Patch 8 is the netlink spec update + ethtool-user.c|h update for that new command - Patch 8,9,10 and 11 updates the PLCA, strset, cable-test and pse netlink commands to use the user-provided PHY instead of net_device.phydev. - Finally patch 12 adds some documentation for this whole work. Examples ======== Here's a short overview of the kind of operations you can have regarding the PHY topology. These tests were performed on a MacchiatoBin, which has 3 interfaces : eth0 and eth1 have the following layout: MAC - PHY - SFP eth2 has this more classic topology : MAC - PHY - RJ45 finally eth3 has the following topology : MAC - SFP When performing a dump with all interfaces down, we don't get any result, as no PHY has been attached to their respective net_device : None The following output is with eth0, eth2 and eth3 up, but no SFP module inserted in none of the interfaces : [{'downstream-sfp-name': 'sfp-eth0', 'drvname': 'mv88x3310', 'header': {'dev-index': 2, 'dev-name': 'eth0'}, 'id': 0, 'index': 1, 'name': 'f212a600.mdio-mii:00', 'upstream-type': 'mac'}, {'drvname': 'Marvell 88E1510', 'header': {'dev-index': 4, 'dev-name': 'eth2'}, 'id': 21040593, 'index': 1, 'name': 'f212a200.mdio-mii:00', 'upstream-type': 'mac'}] And now is a dump operation with a copper SFP in the eth0 port : [{'downstream-sfp-name': 'sfp-eth0', 'drvname': 'mv88x3310', 'header': {'dev-index': 2, 'dev-name': 'eth0'}, 'id': 0, 'index': 1, 'name': 'f212a600.mdio-mii:00', 'upstream-type': 'mac'}, {'drvname': 'Marvell 88E1111', 'header': {'dev-index': 2, 'dev-name': 'eth0'}, 'id': 21040322, 'index': 2, 'name': 'i2c:sfp-eth0:16', 'upstream': {'index': 1, 'sfp-name': 'sfp-eth0'}, 'upstream-type': 'phy'}, {'drvname': 'Marvell 88E1510', 'header': {'dev-index': 4, 'dev-name': 'eth2'}, 'id': 21040593, 'index': 1, 'name': 'f212a200.mdio-mii:00', 'upstream-type': 'mac'}] -- Note that this shouldn't actually work as the 88x3310 PHY doesn't allow a 1G SFP to be connected to its SFP interface, and I don't have a 10G copper SFP, so for the sake of the demo I applied the following modification, which of courses gives a non-functionnal link, but the PHY attach still works, which is what I want to demonstrate : @@ -488,7 +488,7 @@ static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) if (iface != PHY_INTERFACE_MODE_10GBASER) { dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); - return -EINVAL; + //return -EINVAL; } return 0; } Finally an example of the filtered DUMP operation that Jakub suggested in V1 : [{'downstream-sfp-name': 'sfp-eth0', 'drvname': 'mv88x3310', 'header': {'dev-index': 2, 'dev-name': 'eth0'}, 'id': 0, 'index': 1, 'name': 'f212a600.mdio-mii:00', 'upstream-type': 'mac'}, {'drvname': 'Marvell 88E1111', 'header': {'dev-index': 2, 'dev-name': 'eth0'}, 'id': 21040322, 'index': 2, 'name': 'i2c:sfp-eth0:16', 'upstream': {'index': 1, 'sfp-name': 'sfp-eth0'}, 'upstream-type': 'phy'}] And a classic GET operation allows querying a single PHY's info : {'drvname': 'Marvell 88E1111', 'header': {'dev-index': 2, 'dev-name': 'eth0'}, 'id': 21040322, 'index': 2, 'name': 'i2c:sfp-eth0:16', 'upstream': {'index': 1, 'sfp-name': 'sfp-eth0'}, 'upstream-type': 'phy'} Changed in V5: - Removed the RTNL assertion in the topology ops - Made the phy_topo_get_phy inline - Fixed the PSE-PD multi-PHY support by re-adding a wrongly dropped check - Fixed some typos in the documentation - Fixed reverse xmas trees Changes in V4: - Dropped the RFC flag - Made the net_device integration independent to having phylib enabled - Removed the autogenerated ethtool-user code for the YNL specs Changes in V3: - Added RTNL assertions where needed - Fixed issues in the DUMP code for PHY_GET, which crashed when running it twice in a row - Added the documentation, and moved in-source docs around - renamed link_topology to phy_link_topology Changes in V2: - Added the DUMP operation - Added much more information in the reported data, to be able to reconstruct precisely the topology tree - renamed phy_list to link_topology ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 109bf4c + 32bb451 commit 9fb3dc1

30 files changed

+912
-35
lines changed

Documentation/netlink/specs/ethtool.yaml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ definitions:
1616
name: stringset
1717
type: enum
1818
entries: []
19+
-
20+
name: phy-upstream-type
21+
enum-name:
22+
type: enum
23+
entries: [ mac, phy ]
1924

2025
attribute-sets:
2126
-
@@ -30,6 +35,9 @@ attribute-sets:
3035
-
3136
name: flags
3237
type: u32
38+
-
39+
name: phy-index
40+
type: u32
3341

3442
-
3543
name: bitset-bit
@@ -942,6 +950,45 @@ attribute-sets:
942950
-
943951
name: burst-tmr
944952
type: u32
953+
-
954+
name: phy-upstream
955+
attributes:
956+
-
957+
name: index
958+
type: u32
959+
-
960+
name: sfp-name
961+
type: string
962+
-
963+
name: phy
964+
attributes:
965+
-
966+
name: header
967+
type: nest
968+
nested-attributes: header
969+
-
970+
name: index
971+
type: u32
972+
-
973+
name: drvname
974+
type: string
975+
-
976+
name: name
977+
type: string
978+
-
979+
name: upstream-type
980+
type: u8
981+
enum: phy-upstream-type
982+
-
983+
name: upstream
984+
type: nest
985+
nested-attributes: phy-upstream
986+
-
987+
name: downstream-sfp-name
988+
type: string
989+
-
990+
name: id
991+
type: u32
945992

946993
operations:
947994
enum-model: directional
@@ -1693,3 +1740,24 @@ operations:
16931740
name: mm-ntf
16941741
doc: Notification for change in MAC Merge configuration.
16951742
notify: mm-get
1743+
-
1744+
name: phy-get
1745+
doc: Get PHY devices attached to an interface
1746+
1747+
attribute-set: phy
1748+
1749+
do: &phy-get-op
1750+
request:
1751+
attributes:
1752+
- header
1753+
reply:
1754+
attributes:
1755+
- header
1756+
- index
1757+
- drvname
1758+
- name
1759+
- upstream-type
1760+
- upstream
1761+
- downstream-sfp-name
1762+
- id
1763+
dump: *phy-get-op

Documentation/networking/ethtool-netlink.rst

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ Structure of this header is
5757
``ETHTOOL_A_HEADER_DEV_INDEX`` u32 device ifindex
5858
``ETHTOOL_A_HEADER_DEV_NAME`` string device name
5959
``ETHTOOL_A_HEADER_FLAGS`` u32 flags common for all requests
60+
``ETHTOOL_A_HEADER_PHY_INDEX`` u32 phy device index
6061
============================== ====== =============================
6162

6263
``ETHTOOL_A_HEADER_DEV_INDEX`` and ``ETHTOOL_A_HEADER_DEV_NAME`` identify the
@@ -81,6 +82,12 @@ the behaviour is backward compatible, i.e. requests from old clients not aware
8182
of the flag should be interpreted the way the client expects. A client must
8283
not set flags it does not understand.
8384

85+
``ETHTOOL_A_HEADER_PHY_INDEX`` identify the ethernet PHY the message relates to.
86+
As there are numerous commands that are related to PHY configuration, and because
87+
we can have more than one PHY on the link, the PHY index can be passed in the
88+
request for the commands that needs it. It is however not mandatory, and if it
89+
is not passed for commands that target a PHY, the net_device.phydev pointer
90+
is used, as a fallback that keeps the legacy behaviour.
8491

8592
Bit sets
8693
========
@@ -2004,6 +2011,49 @@ The attributes are propagated to the driver through the following structure:
20042011
.. kernel-doc:: include/linux/ethtool.h
20052012
:identifiers: ethtool_mm_cfg
20062013

2014+
PHY_GET
2015+
=======
2016+
2017+
Retrieve information about a given Ethernet PHY sitting on the link. As there
2018+
can be more than one PHY, the DUMP operation can be used to list the PHYs
2019+
present on a given interface, by passing an interface index or name in
2020+
the dump request
2021+
2022+
Request contents:
2023+
2024+
==================================== ====== ==========================
2025+
``ETHTOOL_A_PHY_HEADER`` nested request header
2026+
==================================== ====== ==========================
2027+
2028+
Kernel response contents:
2029+
2030+
===================================== ====== ==========================
2031+
``ETHTOOL_A_PHY_HEADER`` nested request header
2032+
``ETHTOOL_A_PHY_INDEX`` u32 the phy's unique index, that can
2033+
be used for phy-specific requests
2034+
``ETHTOOL_A_PHY_DRVNAME`` string the phy driver name
2035+
``ETHTOOL_A_PHY_NAME`` string the phy device name
2036+
``ETHTOOL_A_PHY_UPSTREAM_TYPE`` u32 the type of device this phy is
2037+
connected to
2038+
``ETHTOOL_A_PHY_UPSTREAM_PHY`` nested if the phy is connected to another
2039+
phy, this nest contains info on
2040+
that connection
2041+
``ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME`` string if the phy controls an sfp bus,
2042+
the name of the sfp bus
2043+
``ETHTOOL_A_PHY_ID`` u32 the phy id if the phy is C22
2044+
===================================== ====== ==========================
2045+
2046+
When ``ETHTOOL_A_PHY_UPSTREAM_TYPE`` is PHY_UPSTREAM_PHY, the PHY's parent is
2047+
another PHY. Information on the parent PHY will be set in the
2048+
``ETHTOOL_A_PHY_UPSTREAM_PHY`` nest, which has the following structure :
2049+
2050+
=================================== ====== ==========================
2051+
``ETHTOOL_A_PHY_UPSTREAM_INDEX`` u32 the PHY index of the upstream PHY
2052+
``ETHTOOL_A_PHY_UPSTREAM_SFP_NAME`` string if this PHY is connected to it's
2053+
parent PHY through an SFP bus, the
2054+
name of this sfp bus
2055+
=================================== ====== ==========================
2056+
20072057
Request translation
20082058
===================
20092059

@@ -2110,4 +2160,5 @@ are netlink only.
21102160
n/a ``ETHTOOL_MSG_PLCA_GET_STATUS``
21112161
n/a ``ETHTOOL_MSG_MM_GET``
21122162
n/a ``ETHTOOL_MSG_MM_SET``
2163+
n/a ``ETHTOOL_MSG_PHY_GET``
21132164
=================================== =====================================

Documentation/networking/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ Contents:
8888
operstates
8989
packet_mmap
9090
phonet
91+
phy-link-topology
9192
pktgen
9293
plip
9394
ppp_generic
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
.. SPDX-License-Identifier: GPL-2.0
2+
3+
=================
4+
PHY link topology
5+
=================
6+
7+
Overview
8+
========
9+
10+
The PHY link topology representation in the networking stack aims at representing
11+
the hardware layout for any given Ethernet link.
12+
13+
An Ethernet Interface from userspace's point of view is nothing but a
14+
:c:type:`struct net_device <net_device>`, which exposes configuration options
15+
through the legacy ioctls and the ethool netlink commands. The base assumption
16+
when designing these configuration channels were that the link looked
17+
something like this ::
18+
19+
+-----------------------+ +----------+ +--------------+
20+
| Ethernet Controller / | | Ethernet | | Connector / |
21+
| MAC | ------ | PHY | ---- | Port | ---... to LP
22+
+-----------------------+ +----------+ +--------------+
23+
struct net_device struct phy_device
24+
25+
Commands that needs to configure the PHY will go through the net_device.phydev
26+
field to reach the PHY and perform the relevant configuration.
27+
28+
This assumption falls apart in more complex topologies that can arise when,
29+
for example, using SFP transceivers (although that's not the only specific case).
30+
31+
Here, we have 2 basic scenarios. Either the MAC is able to output a serialized
32+
interface, that can directly be fed to an SFP cage, such as SGMII, 1000BaseX,
33+
10GBaseR, etc.
34+
35+
The link topology then looks like this (when an SFP module is inserted) ::
36+
37+
+-----+ SGMII +------------+
38+
| MAC | ------- | SFP Module |
39+
+-----+ +------------+
40+
41+
Knowing that some modules embed a PHY, the actual link is more like ::
42+
43+
+-----+ SGMII +--------------+
44+
| MAC | -------- | PHY (on SFP) |
45+
+-----+ +--------------+
46+
47+
In this case, the SFP PHY is handled by phylib, and registered by phylink through
48+
its SFP upstream ops.
49+
50+
Now some Ethernet controllers aren't able to output a serialized interface, so
51+
we can't directly connect them to an SFP cage. However, some PHYs can be used
52+
as media-converters, to translate the non-serialized MAC MII interface to a
53+
serialized MII interface fed to the SFP ::
54+
55+
+-----+ RGMII +-----------------------+ SGMII +--------------+
56+
| MAC | ------- | PHY (media converter) | ------- | PHY (on SFP) |
57+
+-----+ +-----------------------+ +--------------+
58+
59+
This is where the model of having a single net_device.phydev pointer shows its
60+
limitations, as we now have 2 PHYs on the link.
61+
62+
The phy_link topology framework aims at providing a way to keep track of every
63+
PHY on the link, for use by both kernel drivers and subsystems, but also to
64+
report the topology to userspace, allowing to target individual PHYs in configuration
65+
commands.
66+
67+
API
68+
===
69+
70+
The :c:type:`struct phy_link_topology <phy_link_topology>` is a per-netdevice
71+
resource, that gets initialized at netdevice creation. Once it's initialized,
72+
it is then possible to register PHYs to the topology through :
73+
74+
:c:func:`phy_link_topo_add_phy`
75+
76+
Besides registering the PHY to the topology, this call will also assign a unique
77+
index to the PHY, which can then be reported to userspace to refer to this PHY
78+
(akin to the ifindex). This index is a u32, ranging from 1 to U32_MAX. The value
79+
0 is reserved to indicate the PHY doesn't belong to any topology yet.
80+
81+
The PHY can then be removed from the topology through
82+
83+
:c:func:`phy_link_topo_del_phy`
84+
85+
These function are already hooked into the phylib subsystem, so all PHYs that
86+
are linked to a net_device through :c:func:`phy_attach_direct` will automatically
87+
join the netdev's topology.
88+
89+
PHYs that are on a SFP module will also be automatically registered IF the SFP
90+
upstream is phylink (so, no media-converter).
91+
92+
PHY drivers that can be used as SFP upstream need to call :c:func:`phy_sfp_attach_phy`
93+
and :c:func:`phy_sfp_detach_phy`, which can be used as a
94+
.attach_phy / .detach_phy implementation for the
95+
:c:type:`struct sfp_upstream_ops <sfp_upstream_ops>`.
96+
97+
UAPI
98+
====
99+
100+
There exist a set of netlink commands to query the link topology from userspace,
101+
see ``Documentation/networking/ethtool-netlink.rst``.
102+
103+
The whole point of having a topology representation is to assign the phyindex
104+
field in :c:type:`struct phy_device <phy_device>`. This index is reported to
105+
userspace using the ``ETHTOOL_MSG_PHY_GET`` ethtnl command. Performing a DUMP operation
106+
will result in all PHYs from all net_device being listed. The DUMP command
107+
accepts either a ``ETHTOOL_A_HEADER_DEV_INDEX`` or ``ETHTOOL_A_HEADER_DEV_NAME``
108+
to be passed in the request to filter the DUMP to a single net_device.
109+
110+
The retrieved index can then be passed as a request parameter using the
111+
``ETHTOOL_A_HEADER_PHY_INDEX`` field in the following ethnl commands :
112+
113+
* ``ETHTOOL_MSG_STRSET_GET`` to get the stats string set from a given PHY
114+
* ``ETHTOOL_MSG_CABLE_TEST_ACT`` and ``ETHTOOL_MSG_CABLE_TEST_ACT``, to perform
115+
cable testing on a given PHY on the link (most likely the outermost PHY)
116+
* ``ETHTOOL_MSG_PSE_SET`` and ``ETHTOOL_MSG_PSE_GET`` for PHY-controlled PoE and PSE settings
117+
* ``ETHTOOL_MSG_PLCA_GET_CFG``, ``ETHTOOL_MSG_PLCA_SET_CFG`` and ``ETHTOOL_MSG_PLCA_GET_STATUS``
118+
to set the PLCA (Physical Layer Collision Avoidance) parameters
119+
120+
Note that the PHY index can be passed to other requests, which will silently
121+
ignore it if present and irrelevant.

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/at803x.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,6 +1452,8 @@ static const struct sfp_upstream_ops at8031_sfp_ops = {
14521452
.attach = phy_sfp_attach,
14531453
.detach = phy_sfp_detach,
14541454
.module_insert = at8031_sfp_insert,
1455+
.connect_phy = phy_sfp_connect_phy,
1456+
.disconnect_phy = phy_sfp_disconnect_phy,
14551457
};
14561458

14571459
static int at8031_parse_dt(struct phy_device *phydev)

drivers/net/phy/marvell-88x2222.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,8 @@ static const struct sfp_upstream_ops sfp_phy_ops = {
555555
.link_down = mv2222_sfp_link_down,
556556
.attach = phy_sfp_attach,
557557
.detach = phy_sfp_detach,
558+
.connect_phy = phy_sfp_connect_phy,
559+
.disconnect_phy = phy_sfp_disconnect_phy,
558560
};
559561

560562
static int mv2222_probe(struct phy_device *phydev)

drivers/net/phy/marvell.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3254,6 +3254,8 @@ static const struct sfp_upstream_ops m88e1510_sfp_ops = {
32543254
.module_remove = m88e1510_sfp_remove,
32553255
.attach = phy_sfp_attach,
32563256
.detach = phy_sfp_detach,
3257+
.connect_phy = phy_sfp_connect_phy,
3258+
.disconnect_phy = phy_sfp_disconnect_phy,
32573259
};
32583260

32593261
static int m88e1510_probe(struct phy_device *phydev)

drivers/net/phy/marvell10g.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,8 @@ static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
503503
static const struct sfp_upstream_ops mv3310_sfp_ops = {
504504
.attach = phy_sfp_attach,
505505
.detach = phy_sfp_detach,
506+
.connect_phy = phy_sfp_connect_phy,
507+
.disconnect_phy = phy_sfp_disconnect_phy,
506508
.module_insert = mv3310_sfp_insert,
507509
};
508510

0 commit comments

Comments
 (0)