2023-12-21 18:01:18

by Maxime Chevallier

[permalink] [raw]
Subject: [PATCH net-next v5 00/13] Introduce PHY listing and link_topology tracking

Hello everyone,

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 :

# ./cli.py --spec specs/ethtool.yaml --schema genetlink-legacy.yaml --dump phy-get
None

The following output is with eth0, eth2 and eth3 up, but no SFP module
inserted in none of the interfaces :

# ./cli.py --spec specs/ethtool.yaml --schema genetlink-legacy.yaml --dump phy-get
[{'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 :

# ./cli.py --spec specs/ethtool.yaml --schema genetlink-legacy.yaml --dump phy-get
[{'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 :

# ./cli.py --spec specs/ethtool.yaml --schema genetlink-legacy.yaml \
# --dump phy-get --json '{"header" : {"dev-name" : "eth0"}}'

[{'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 :

# ./cli.py --spec specs/ethtool.yaml --schema genetlink-legacy.yaml \
# --do phy-get --json '{"header" : {"dev-name" : "eth0", "phy-index" : 2}}'

{'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

Maxime Chevallier (13):
net: phy: Introduce ethernet link topology representation
net: sfp: pass the phy_device when disconnecting an sfp module's PHY
net: phy: add helpers to handle sfp phy connect/disconnect
net: sfp: Add helper to return the SFP bus name
net: ethtool: Allow passing a phy index for some commands
netlink: specs: add phy-index as a header parameter
net: ethtool: Introduce a command to list PHYs on an interface
netlink: specs: add ethnl PHY_GET command set
net: ethtool: plca: Target the command to the requested PHY
net: ethtool: pse-pd: Target the command to the requested PHY
net: ethtool: cable-test: Target the command to the requested PHY
net: ethtool: strset: Allow querying phy stats by index
Documentation: networking: document phy_link_topology

Documentation/netlink/specs/ethtool.yaml | 68 ++++
Documentation/networking/ethtool-netlink.rst | 51 +++
Documentation/networking/index.rst | 1 +
.../networking/phy-link-topology.rst | 121 +++++++
MAINTAINERS | 2 +
drivers/net/phy/Makefile | 2 +-
drivers/net/phy/at803x.c | 2 +
drivers/net/phy/marvell-88x2222.c | 2 +
drivers/net/phy/marvell.c | 2 +
drivers/net/phy/marvell10g.c | 2 +
drivers/net/phy/phy_device.c | 55 ++++
drivers/net/phy/phy_link_topology.c | 66 ++++
drivers/net/phy/phylink.c | 3 +-
drivers/net/phy/sfp-bus.c | 15 +-
include/linux/netdevice.h | 4 +-
include/linux/phy.h | 6 +
include/linux/phy_link_topology.h | 67 ++++
include/linux/phy_link_topology_core.h | 19 ++
include/linux/sfp.h | 8 +-
include/uapi/linux/ethtool.h | 16 +
include/uapi/linux/ethtool_netlink.h | 30 ++
net/core/dev.c | 3 +
net/ethtool/Makefile | 2 +-
net/ethtool/cabletest.c | 12 +-
net/ethtool/netlink.c | 33 ++
net/ethtool/netlink.h | 12 +-
net/ethtool/phy.c | 306 ++++++++++++++++++
net/ethtool/plca.c | 13 +-
net/ethtool/pse-pd.c | 9 +-
net/ethtool/strset.c | 15 +-
30 files changed, 912 insertions(+), 35 deletions(-)
create mode 100644 Documentation/networking/phy-link-topology.rst
create mode 100644 drivers/net/phy/phy_link_topology.c
create mode 100644 include/linux/phy_link_topology.h
create mode 100644 include/linux/phy_link_topology_core.h
create mode 100644 net/ethtool/phy.c

--
2.43.0



2023-12-21 18:01:33

by Maxime Chevallier

[permalink] [raw]
Subject: [PATCH net-next v5 01/13] 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]>
---
V5: - Dropped the ASSERT_RTNL()
- Made the phy_link_topo_get_phy inline
V4: - Moved the phy_link_topo_init() code to an inline header function
- Made the code build without phylib

V3: - Renamed to phy_link_topology
- Added assertions for RTNL
- Various cleanups of leftover, unused test code
- Made the PHY index u32

MAINTAINERS | 2 +
drivers/net/phy/Makefile | 2 +-
drivers/net/phy/phy_device.c | 7 +++
drivers/net/phy/phy_link_topology.c | 66 +++++++++++++++++++++++++
include/linux/netdevice.h | 4 +-
include/linux/phy.h | 4 ++
include/linux/phy_link_topology.h | 67 ++++++++++++++++++++++++++
include/linux/phy_link_topology_core.h | 19 ++++++++
include/uapi/linux/ethtool.h | 16 ++++++
net/core/dev.c | 3 ++
10 files changed, 188 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/phy/phy_link_topology.c
create mode 100644 include/linux/phy_link_topology.h
create mode 100644 include/linux/phy_link_topology_core.h

diff --git a/MAINTAINERS b/MAINTAINERS
index dda78b4ce707..f09b1d4e5487 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7873,6 +7873,8 @@ F: include/linux/mii.h
F: include/linux/of_net.h
F: include/linux/phy.h
F: include/linux/phy_fixed.h
+F: include/linux/phy_link_topology.h
+F: include/linux/phy_link_topology_core.h
F: include/linux/phylib_stubs.h
F: include/linux/platform_data/mdio-bcm-unimac.h
F: include/linux/platform_data/mdio-gpio.h
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index e35ea69d9cb4..a7a9640bfa3a 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -2,7 +2,7 @@
# Makefile for Linux PHY drivers

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

ifdef CONFIG_MDIO_DEVICE
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 0c52a9eff188..d7c0812bd107 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -29,6 +29,7 @@
#include <linux/phy.h>
#include <linux/phylib_stubs.h>
#include <linux/phy_led_triggers.h>
+#include <linux/phy_link_topology.h>
#include <linux/pse-pd/pse.h>
#include <linux/property.h>
#include <linux/rtnetlink.h>
@@ -1491,6 +1492,11 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,

if (phydev->sfp_bus_attached)
dev->sfp_bus = phydev->sfp_bus;
+
+ err = phy_link_topo_add_phy(&dev->link_topo, phydev,
+ PHY_UPSTREAM_MAC, dev);
+ if (err)
+ goto error;
}

/* Some Ethernet drivers try to connect to a PHY device before
@@ -1819,6 +1825,7 @@ void phy_detach(struct phy_device *phydev)
if (dev) {
phydev->attached_dev->phydev = NULL;
phydev->attached_dev = NULL;
+ phy_link_topo_del_phy(&dev->link_topo, phydev);
}
phydev->phylink = NULL;

diff --git a/drivers/net/phy/phy_link_topology.c b/drivers/net/phy/phy_link_topology.c
new file mode 100644
index 000000000000..34e7e08fbfc3
--- /dev/null
+++ b/drivers/net/phy/phy_link_topology.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Infrastructure to handle all PHY devices connected to a given netdev,
+ * either directly or indirectly attached.
+ *
+ * Copyright (c) 2023 Maxime Chevallier<[email protected]>
+ */
+
+#include <linux/phy_link_topology.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/rtnetlink.h>
+#include <linux/xarray.h>
+
+int phy_link_topo_add_phy(struct phy_link_topology *topo,
+ struct phy_device *phy,
+ enum phy_upstream upt, void *upstream)
+{
+ struct phy_device_node *pdn;
+ int ret;
+
+ pdn = kzalloc(sizeof(*pdn), GFP_KERNEL);
+ if (!pdn)
+ return -ENOMEM;
+
+ pdn->phy = phy;
+ switch (upt) {
+ case PHY_UPSTREAM_MAC:
+ pdn->upstream.netdev = (struct net_device *)upstream;
+ if (phy_on_sfp(phy))
+ pdn->parent_sfp_bus = pdn->upstream.netdev->sfp_bus;
+ break;
+ case PHY_UPSTREAM_PHY:
+ pdn->upstream.phydev = (struct phy_device *)upstream;
+ if (phy_on_sfp(phy))
+ pdn->parent_sfp_bus = pdn->upstream.phydev->sfp_bus;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+ pdn->upstream_type = upt;
+
+ ret = xa_alloc_cyclic(&topo->phys, &phy->phyindex, pdn, xa_limit_32b,
+ &topo->next_phy_index, GFP_KERNEL);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ kfree(pdn);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_link_topo_add_phy);
+
+void phy_link_topo_del_phy(struct phy_link_topology *topo,
+ struct phy_device *phy)
+{
+ struct phy_device_node *pdn = xa_erase(&topo->phys, phy->phyindex);
+
+ phy->phyindex = 0;
+
+ kfree(pdn);
+}
+EXPORT_SYMBOL_GPL(phy_link_topo_del_phy);
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 75c7725e5e4f..5baa5517f533 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -40,7 +40,6 @@
#include <net/dcbnl.h>
#endif
#include <net/netprio_cgroup.h>
-
#include <linux/netdev_features.h>
#include <linux/neighbour.h>
#include <uapi/linux/netdevice.h>
@@ -52,6 +51,7 @@
#include <net/net_trackers.h>
#include <net/net_debug.h>
#include <net/dropreason-core.h>
+#include <linux/phy_link_topology_core.h>

struct netpoll_info;
struct device;
@@ -2047,6 +2047,7 @@ enum netdev_stat_type {
* @fcoe_ddp_xid: Max exchange id for FCoE LRO by ddp
*
* @priomap: XXX: need comments on this one
+ * @link_topo: Physical link topology tracking attached PHYs
* @phydev: Physical device may attach itself
* for hardware timestamping
* @sfp_bus: attached &struct sfp_bus structure.
@@ -2441,6 +2442,7 @@ struct net_device {
#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
struct netprio_map __rcu *priomap;
#endif
+ struct phy_link_topology link_topo;
struct phy_device *phydev;
struct sfp_bus *sfp_bus;
struct lock_class_key *qdisc_tx_busylock;
diff --git a/include/linux/phy.h b/include/linux/phy.h
index e9e85d347587..9f21eb380475 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -544,6 +544,9 @@ struct macsec_ops;
* @drv: Pointer to the driver for this PHY instance
* @devlink: Create a link between phy dev and mac dev, if the external phy
* used by current mac interface is managed by another mac interface.
+ * @phyindex: Unique id across the phy's parent tree of phys to address the PHY
+ * from userspace, similar to ifindex. A zero index means the PHY
+ * wasn't assigned an id yet.
* @phy_id: UID for this device found during discovery
* @c45_ids: 802.3-c45 Device Identifiers if is_c45.
* @is_c45: Set to true if this PHY uses clause 45 addressing.
@@ -643,6 +646,7 @@ struct phy_device {

struct device_link *devlink;

+ u32 phyindex;
u32 phy_id;

struct phy_c45_device_ids c45_ids;
diff --git a/include/linux/phy_link_topology.h b/include/linux/phy_link_topology.h
new file mode 100644
index 000000000000..91902263ec0e
--- /dev/null
+++ b/include/linux/phy_link_topology.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * PHY device list allow maintaining a list of PHY devices that are
+ * part of a netdevice's link topology. PHYs can for example be chained,
+ * as is the case when using a PHY that exposes an SFP module, on which an
+ * SFP transceiver that embeds a PHY is connected.
+ *
+ * This list can then be used by userspace to leverage individual PHY
+ * capabilities.
+ */
+#ifndef __PHY_LINK_TOPOLOGY_H
+#define __PHY_LINK_TOPOLOGY_H
+
+#include <linux/ethtool.h>
+#include <linux/phy_link_topology_core.h>
+
+struct xarray;
+struct phy_device;
+struct net_device;
+struct sfp_bus;
+
+struct phy_device_node {
+ enum phy_upstream upstream_type;
+
+ union {
+ struct net_device *netdev;
+ struct phy_device *phydev;
+ } upstream;
+
+ struct sfp_bus *parent_sfp_bus;
+
+ struct phy_device *phy;
+};
+
+static inline struct phy_device *
+phy_link_topo_get_phy(struct phy_link_topology *topo, u32 phyindex)
+{
+ struct phy_device_node *pdn = xa_load(&topo->phys, phyindex);
+
+ if (pdn)
+ return pdn->phy;
+
+ return NULL;
+}
+
+#if IS_ENABLED(CONFIG_PHYLIB)
+int phy_link_topo_add_phy(struct phy_link_topology *topo,
+ struct phy_device *phy,
+ enum phy_upstream upt, void *upstream);
+
+void phy_link_topo_del_phy(struct phy_link_topology *lt, struct phy_device *phy);
+
+#else
+static inline int phy_link_topo_add_phy(struct phy_link_topology *topo,
+ struct phy_device *phy,
+ enum phy_upstream upt, void *upstream)
+{
+ return 0;
+}
+
+static inline void phy_link_topo_del_phy(struct phy_link_topology *topo,
+ struct phy_device *phy)
+{
+}
+#endif
+
+#endif /* __PHY_LINK_TOPOLOGY_H */
diff --git a/include/linux/phy_link_topology_core.h b/include/linux/phy_link_topology_core.h
new file mode 100644
index 000000000000..78c75f909489
--- /dev/null
+++ b/include/linux/phy_link_topology_core.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PHY_LINK_TOPOLOGY_CORE_H
+#define __PHY_LINK_TOPOLOGY_CORE_H
+
+struct xarray;
+
+struct phy_link_topology {
+ struct xarray phys;
+
+ u32 next_phy_index;
+};
+
+static inline void phy_link_topo_init(struct phy_link_topology *topo)
+{
+ xa_init_flags(&topo->phys, XA_FLAGS_ALLOC1);
+ topo->next_phy_index = 1;
+}
+
+#endif /* __PHY_LINK_TOPOLOGY_CORE_H */
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index 0787d561ace0..9cff798c6df9 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -2216,4 +2216,20 @@ struct ethtool_link_settings {
* __u32 map_lp_advertising[link_mode_masks_nwords];
*/
};
+
+/**
+ * enum phy_upstream - Represents the upstream component a given PHY device
+ * is connected to, as in what is on the other end of the MII bus. Most PHYs
+ * will be attached to an Ethernet MAC controller, but in some cases, there's
+ * an intermediate PHY used as a media-converter, which will driver another
+ * MII interface as its output.
+ * @PHY_UPSTREAM_MAC: Upstream component is a MAC (a switch port,
+ * or ethernet controller)
+ * @PHY_UPSTREAM_PHY: Upstream component is a PHY (likely a media converter)
+ */
+enum phy_upstream {
+ PHY_UPSTREAM_MAC,
+ PHY_UPSTREAM_PHY,
+};
+
#endif /* _UAPI_LINUX_ETHTOOL_H */
diff --git a/net/core/dev.c b/net/core/dev.c
index b87504078320..476acd22956c 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -153,6 +153,7 @@
#include <linux/prandom.h>
#include <linux/once_lite.h>
#include <net/netdev_rx_queue.h>
+#include <linux/phy_link_topology_core.h>

#include "dev.h"
#include "net-sysfs.h"
@@ -10872,6 +10873,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
#ifdef CONFIG_NET_SCHED
hash_init(dev->qdisc_hash);
#endif
+ phy_link_topo_init(&dev->link_topo);
+
dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM;
setup(dev);

--
2.43.0


2023-12-21 18:01:46

by Maxime Chevallier

[permalink] [raw]
Subject: [PATCH net-next v5 02/13] net: sfp: pass the phy_device when disconnecting an sfp module's PHY

Pass the phy_device as a parameter to the sfp upstream .disconnect_phy
operation. This is preparatory work to help track phy devices across
a net_device's link.

Signed-off-by: Maxime Chevallier <[email protected]>
---
V5: No changes
V4: No changes
V3: No changes

drivers/net/phy/phy_device.c | 8 ++++++++
drivers/net/phy/phylink.c | 3 ++-
drivers/net/phy/sfp-bus.c | 4 ++--
include/linux/sfp.h | 2 +-
4 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index d7c0812bd107..aad78e3f7894 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -266,6 +266,14 @@ static void phy_mdio_device_remove(struct mdio_device *mdiodev)

static struct phy_driver genphy_driver;

+static struct phy_link_topology *phy_get_link_topology(struct phy_device *phydev)
+{
+ if (phydev->attached_dev)
+ return &phydev->attached_dev->link_topo;
+
+ return NULL;
+}
+
static LIST_HEAD(phy_fixup_list);
static DEFINE_MUTEX(phy_fixup_lock);

diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 298dfd6982a5..3d25a4a6212b 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -3319,7 +3319,8 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
return ret;
}

-static void phylink_sfp_disconnect_phy(void *upstream)
+static void phylink_sfp_disconnect_phy(void *upstream,
+ struct phy_device *phydev)
{
phylink_disconnect_phy(upstream);
}
diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c
index 6fa679b36290..3a86c41e1235 100644
--- a/drivers/net/phy/sfp-bus.c
+++ b/drivers/net/phy/sfp-bus.c
@@ -486,7 +486,7 @@ static void sfp_unregister_bus(struct sfp_bus *bus)
bus->socket_ops->stop(bus->sfp);
bus->socket_ops->detach(bus->sfp);
if (bus->phydev && ops && ops->disconnect_phy)
- ops->disconnect_phy(bus->upstream);
+ ops->disconnect_phy(bus->upstream, bus->phydev);
}
bus->registered = false;
}
@@ -742,7 +742,7 @@ void sfp_remove_phy(struct sfp_bus *bus)
const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);

if (ops && ops->disconnect_phy)
- ops->disconnect_phy(bus->upstream);
+ ops->disconnect_phy(bus->upstream, bus->phydev);
bus->phydev = NULL;
}
EXPORT_SYMBOL_GPL(sfp_remove_phy);
diff --git a/include/linux/sfp.h b/include/linux/sfp.h
index 9346cd44814d..0573e53b0c11 100644
--- a/include/linux/sfp.h
+++ b/include/linux/sfp.h
@@ -544,7 +544,7 @@ struct sfp_upstream_ops {
void (*link_down)(void *priv);
void (*link_up)(void *priv);
int (*connect_phy)(void *priv, struct phy_device *);
- void (*disconnect_phy)(void *priv);
+ void (*disconnect_phy)(void *priv, struct phy_device *);
};

#if IS_ENABLED(CONFIG_SFP)
--
2.43.0


2023-12-21 18:02:14

by Maxime Chevallier

[permalink] [raw]
Subject: [PATCH net-next v5 03/13] net: phy: add helpers to handle sfp phy connect/disconnect

There are a few PHY drivers that can handle SFP modules through their
sfp_upstream_ops. Introduce Phylib helpers to keep track of connected
SFP PHYs in a netdevice's namespace, by adding the SFP PHY to the
upstream PHY's netdev's namespace.

By doing so, these SFP PHYs can be enumerated and exposed to users,
which will be able to use their capabilities.

Signed-off-by: Maxime Chevallier <[email protected]>
---
V5: No Changes
V4: Rebased the at803x part with the newer version on net-next
V3: Renaming
V2: Renaming

drivers/net/phy/at803x.c | 2 ++
drivers/net/phy/marvell-88x2222.c | 2 ++
drivers/net/phy/marvell.c | 2 ++
drivers/net/phy/marvell10g.c | 2 ++
drivers/net/phy/phy_device.c | 40 +++++++++++++++++++++++++++++++
include/linux/phy.h | 2 ++
6 files changed, 50 insertions(+)

diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index d5dc927618ab..1a62f868ec40 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -1452,6 +1452,8 @@ static const struct sfp_upstream_ops at8031_sfp_ops = {
.attach = phy_sfp_attach,
.detach = phy_sfp_detach,
.module_insert = at8031_sfp_insert,
+ .connect_phy = phy_sfp_connect_phy,
+ .disconnect_phy = phy_sfp_disconnect_phy,
};

static int at8031_parse_dt(struct phy_device *phydev)
diff --git a/drivers/net/phy/marvell-88x2222.c b/drivers/net/phy/marvell-88x2222.c
index e3aa30dad2e6..3f77bbc7e04f 100644
--- a/drivers/net/phy/marvell-88x2222.c
+++ b/drivers/net/phy/marvell-88x2222.c
@@ -555,6 +555,8 @@ static const struct sfp_upstream_ops sfp_phy_ops = {
.link_down = mv2222_sfp_link_down,
.attach = phy_sfp_attach,
.detach = phy_sfp_detach,
+ .connect_phy = phy_sfp_connect_phy,
+ .disconnect_phy = phy_sfp_disconnect_phy,
};

static int mv2222_probe(struct phy_device *phydev)
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index eba652a4c1d8..674e29bce2cc 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -3254,6 +3254,8 @@ static const struct sfp_upstream_ops m88e1510_sfp_ops = {
.module_remove = m88e1510_sfp_remove,
.attach = phy_sfp_attach,
.detach = phy_sfp_detach,
+ .connect_phy = phy_sfp_connect_phy,
+ .disconnect_phy = phy_sfp_disconnect_phy,
};

static int m88e1510_probe(struct phy_device *phydev)
diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c
index ad43e280930c..6642eb642d4b 100644
--- a/drivers/net/phy/marvell10g.c
+++ b/drivers/net/phy/marvell10g.c
@@ -503,6 +503,8 @@ static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
static const struct sfp_upstream_ops mv3310_sfp_ops = {
.attach = phy_sfp_attach,
.detach = phy_sfp_detach,
+ .connect_phy = phy_sfp_connect_phy,
+ .disconnect_phy = phy_sfp_disconnect_phy,
.module_insert = mv3310_sfp_insert,
};

diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index aad78e3f7894..6a7a05802bc6 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1363,6 +1363,46 @@ phy_standalone_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(phy_standalone);

+/**
+ * phy_sfp_connect_phy - Connect the SFP module's PHY to the upstream PHY
+ * @upstream: pointer to the upstream phy device
+ * @phy: pointer to the SFP module's phy device
+ *
+ * This helper allows keeping track of PHY devices on the link. It adds the
+ * SFP module's phy to the phy namespace of the upstream phy
+ */
+int phy_sfp_connect_phy(void *upstream, struct phy_device *phy)
+{
+ struct phy_device *phydev = upstream;
+ struct phy_link_topology *topo = phy_get_link_topology(phydev);
+
+ if (topo)
+ return phy_link_topo_add_phy(topo, phy, PHY_UPSTREAM_PHY, phydev);
+
+ return 0;
+}
+EXPORT_SYMBOL(phy_sfp_connect_phy);
+
+/**
+ * phy_sfp_disconnect_phy - Disconnect the SFP module's PHY from the upstream PHY
+ * @upstream: pointer to the upstream phy device
+ * @phy: pointer to the SFP module's phy device
+ *
+ * This helper allows keeping track of PHY devices on the link. It removes the
+ * SFP module's phy to the phy namespace of the upstream phy. As the module phy
+ * will be destroyed, re-inserting the same module will add a new phy with a
+ * new index.
+ */
+void phy_sfp_disconnect_phy(void *upstream, struct phy_device *phy)
+{
+ struct phy_device *phydev = upstream;
+ struct phy_link_topology *topo = phy_get_link_topology(phydev);
+
+ if (topo)
+ phy_link_topo_del_phy(topo, phy);
+}
+EXPORT_SYMBOL(phy_sfp_disconnect_phy);
+
/**
* phy_sfp_attach - attach the SFP bus to the PHY upstream network device
* @upstream: pointer to the phy device
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 9f21eb380475..6cb9d843aee9 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1726,6 +1726,8 @@ int phy_suspend(struct phy_device *phydev);
int phy_resume(struct phy_device *phydev);
int __phy_resume(struct phy_device *phydev);
int phy_loopback(struct phy_device *phydev, bool enable);
+int phy_sfp_connect_phy(void *upstream, struct phy_device *phy);
+void phy_sfp_disconnect_phy(void *upstream, struct phy_device *phy);
void phy_sfp_attach(void *upstream, struct sfp_bus *bus);
void phy_sfp_detach(void *upstream, struct sfp_bus *bus);
int phy_sfp_probe(struct phy_device *phydev,
--
2.43.0


2023-12-21 18:02:30

by Maxime Chevallier

[permalink] [raw]
Subject: [PATCH net-next v5 04/13] net: sfp: Add helper to return the SFP bus name

Knowing the bus name is helpful when we want to expose the link topology
to userspace, add a helper to return the SFP bus name.

Signed-off-by: Maxime Chevallier <[email protected]>
Reviewed-by: Andrew Lunn <[email protected]>
---
V5: - Added Andrew's R-b
V4: - No changes
V3: - Added RTNL assert
- Made the stub inline
V2: New patch

drivers/net/phy/sfp-bus.c | 11 +++++++++++
include/linux/sfp.h | 6 ++++++
2 files changed, 17 insertions(+)

diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c
index 3a86c41e1235..fb1c102714b5 100644
--- a/drivers/net/phy/sfp-bus.c
+++ b/drivers/net/phy/sfp-bus.c
@@ -859,3 +859,14 @@ void sfp_unregister_socket(struct sfp_bus *bus)
sfp_bus_put(bus);
}
EXPORT_SYMBOL_GPL(sfp_unregister_socket);
+
+const char *sfp_get_name(struct sfp_bus *bus)
+{
+ ASSERT_RTNL();
+
+ if (bus->sfp_dev)
+ return dev_name(bus->sfp_dev);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(sfp_get_name);
diff --git a/include/linux/sfp.h b/include/linux/sfp.h
index 0573e53b0c11..55c0ab17c9e2 100644
--- a/include/linux/sfp.h
+++ b/include/linux/sfp.h
@@ -570,6 +570,7 @@ struct sfp_bus *sfp_bus_find_fwnode(const struct fwnode_handle *fwnode);
int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream,
const struct sfp_upstream_ops *ops);
void sfp_bus_del_upstream(struct sfp_bus *bus);
+const char *sfp_get_name(struct sfp_bus *bus);
#else
static inline int sfp_parse_port(struct sfp_bus *bus,
const struct sfp_eeprom_id *id,
@@ -648,6 +649,11 @@ static inline int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream,
static inline void sfp_bus_del_upstream(struct sfp_bus *bus)
{
}
+
+static inline const char *sfp_get_name(struct sfp_bus *bus)
+{
+ return NULL;
+}
#endif

#endif
--
2.43.0


2023-12-21 18:02:57

by Maxime Chevallier

[permalink] [raw]
Subject: [PATCH net-next v5 05/13] net: ethtool: Allow passing a phy index for some commands

Some netlink commands are target towards ethernet PHYs, to control some
of their features. As there's several such commands, add the ability to
pass a PHY index in the ethnl request, which will populate the generic
ethnl_req_info with the relevant phydev when the command targets a PHY.

Signed-off-by: Maxime Chevallier <[email protected]>
Reviewed-by: Andrew Lunn <[email protected]>
---
V5: - Added Andrew's R-b
- Fix a typo reported by Simon
V4: - No Changes
V3: - Fixed the documentation
V2: - New patch

Documentation/networking/ethtool-netlink.rst | 7 ++++++
include/uapi/linux/ethtool_netlink.h | 1 +
net/ethtool/netlink.c | 24 ++++++++++++++++++++
net/ethtool/netlink.h | 7 ++++--
4 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index d583d9abf2f8..3ca6c21e74af 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -57,6 +57,7 @@ Structure of this header is
``ETHTOOL_A_HEADER_DEV_INDEX`` u32 device ifindex
``ETHTOOL_A_HEADER_DEV_NAME`` string device name
``ETHTOOL_A_HEADER_FLAGS`` u32 flags common for all requests
+ ``ETHTOOL_A_HEADER_PHY_INDEX`` u32 phy device index
============================== ====== =============================

``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
of the flag should be interpreted the way the client expects. A client must
not set flags it does not understand.

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

Bit sets
========
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 3f89074aa06c..422e8cfdd98c 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -133,6 +133,7 @@ enum {
ETHTOOL_A_HEADER_DEV_INDEX, /* u32 */
ETHTOOL_A_HEADER_DEV_NAME, /* string */
ETHTOOL_A_HEADER_FLAGS, /* u32 - ETHTOOL_FLAG_* */
+ ETHTOOL_A_HEADER_PHY_INDEX, /* u32 */

/* add new constants above here */
__ETHTOOL_A_HEADER_CNT,
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index fe3553f60bf3..1c26766ce996 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -4,6 +4,7 @@
#include <linux/ethtool_netlink.h>
#include <linux/pm_runtime.h>
#include "netlink.h"
+#include <linux/phy_link_topology.h>

static struct genl_family ethtool_genl_family;

@@ -20,6 +21,7 @@ const struct nla_policy ethnl_header_policy[] = {
.len = ALTIFNAMSIZ - 1 },
[ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32,
ETHTOOL_FLAGS_BASIC),
+ [ETHTOOL_A_HEADER_PHY_INDEX] = NLA_POLICY_MIN(NLA_U32, 1),
};

const struct nla_policy ethnl_header_policy_stats[] = {
@@ -28,6 +30,7 @@ const struct nla_policy ethnl_header_policy_stats[] = {
.len = ALTIFNAMSIZ - 1 },
[ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32,
ETHTOOL_FLAGS_STATS),
+ [ETHTOOL_A_HEADER_PHY_INDEX] = NLA_POLICY_MIN(NLA_U32, 1),
};

int ethnl_ops_begin(struct net_device *dev)
@@ -91,6 +94,7 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
{
struct nlattr *tb[ARRAY_SIZE(ethnl_header_policy)];
const struct nlattr *devname_attr;
+ struct phy_device *phydev = NULL;
struct net_device *dev = NULL;
u32 flags = 0;
int ret;
@@ -145,6 +149,26 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
return -EINVAL;
}

+ if (dev) {
+ if (tb[ETHTOOL_A_HEADER_PHY_INDEX]) {
+ u32 phy_index = nla_get_u32(tb[ETHTOOL_A_HEADER_PHY_INDEX]);
+
+ phydev = phy_link_topo_get_phy(&dev->link_topo,
+ phy_index);
+ if (!phydev) {
+ NL_SET_ERR_MSG_ATTR(extack, header,
+ "no phy matches phy index");
+ return -EINVAL;
+ }
+ } else {
+ /* If we need a PHY but no phy index is specified, fallback
+ * to dev->phydev
+ */
+ phydev = dev->phydev;
+ }
+ }
+
+ req_info->phydev = phydev;
req_info->dev = dev;
req_info->flags = flags;
return 0;
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 9a333a8d04c1..def84e2def9e 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -250,6 +250,7 @@ static inline unsigned int ethnl_reply_header_size(void)
* @dev: network device the request is for (may be null)
* @dev_tracker: refcount tracker for @dev reference
* @flags: request flags common for all request types
+ * @phydev: phy_device connected to @dev this request is for (may be null)
*
* This is a common base for request specific structures holding data from
* parsed userspace request. These always embed struct ethnl_req_info at
@@ -259,6 +260,7 @@ struct ethnl_req_info {
struct net_device *dev;
netdevice_tracker dev_tracker;
u32 flags;
+ struct phy_device *phydev;
};

static inline void ethnl_parse_header_dev_put(struct ethnl_req_info *req_info)
@@ -395,9 +397,10 @@ extern const struct ethnl_request_ops ethnl_rss_request_ops;
extern const struct ethnl_request_ops ethnl_plca_cfg_request_ops;
extern const struct ethnl_request_ops ethnl_plca_status_request_ops;
extern const struct ethnl_request_ops ethnl_mm_request_ops;
+extern const struct ethnl_request_ops ethnl_phy_request_ops;

-extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1];
-extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1];
+extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_PHY_INDEX + 1];
+extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_PHY_INDEX + 1];
extern const struct nla_policy ethnl_strset_get_policy[ETHTOOL_A_STRSET_COUNTS_ONLY + 1];
extern const struct nla_policy ethnl_linkinfo_get_policy[ETHTOOL_A_LINKINFO_HEADER + 1];
extern const struct nla_policy ethnl_linkinfo_set_policy[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL + 1];
--
2.43.0


2023-12-21 18:03:05

by Maxime Chevallier

[permalink] [raw]
Subject: [PATCH net-next v5 06/13] netlink: specs: add phy-index as a header parameter

Update the spec to take the newly introduced phy-index as a generic
request parameter.

Signed-off-by: Maxime Chevallier <[email protected]>
Reviewed-by: Andrew Lunn <[email protected]>
---
V5: Added Andrew's R-b
V4: Ditch the ethtool-user generated code
V3: New patch

Documentation/netlink/specs/ethtool.yaml | 3 +++
1 file changed, 3 insertions(+)

diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index 197208f419dc..bb6e1dc6d1c5 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -30,6 +30,9 @@ attribute-sets:
-
name: flags
type: u32
+ -
+ name: phy-index
+ type: u32

-
name: bitset-bit
--
2.43.0


2023-12-21 18:03:32

by Maxime Chevallier

[permalink] [raw]
Subject: [PATCH net-next v5 07/13] net: ethtool: Introduce a command to list PHYs on an interface

As we have the ability to track the PHYs connected to a net_device
through the link_topology, we can expose this list to userspace. This
allows userspace to use these identifiers for phy-specific commands and
take the decision of which PHY to target by knowing the link topology.

Add PHY_GET and PHY_DUMP, which can be a filtered DUMP operation to list
devices on only one interface.

Signed-off-by: Maxime Chevallier <[email protected]>
---
V5: - Fixed xmas tree
- Fixed uninitialized return variable (Simon)
V4: - Fixed errors when not having SFP enabled, resulting in null names
being passed as parameters to strlen.
V3: - Fixed the documentation
- Fixed the DUMP implementation
V2: New patch

Documentation/networking/ethtool-netlink.rst | 44 +++
include/uapi/linux/ethtool_netlink.h | 29 ++
net/ethtool/Makefile | 2 +-
net/ethtool/netlink.c | 9 +
net/ethtool/netlink.h | 5 +
net/ethtool/phy.c | 306 +++++++++++++++++++
6 files changed, 394 insertions(+), 1 deletion(-)
create mode 100644 net/ethtool/phy.c

diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index 3ca6c21e74af..97ff787a7dd8 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -2011,6 +2011,49 @@ The attributes are propagated to the driver through the following structure:
.. kernel-doc:: include/linux/ethtool.h
:identifiers: ethtool_mm_cfg

+PHY_GET
+=======
+
+Retrieve information about a given Ethernet PHY sitting on the link. As there
+can be more than one PHY, the DUMP operation can be used to list the PHYs
+present on a given interface, by passing an interface index or name in
+the dump request
+
+Request contents:
+
+ ==================================== ====== ==========================
+ ``ETHTOOL_A_PHY_HEADER`` nested request header
+ ==================================== ====== ==========================
+
+Kernel response contents:
+
+ ===================================== ====== ==========================
+ ``ETHTOOL_A_PHY_HEADER`` nested request header
+ ``ETHTOOL_A_PHY_INDEX`` u32 the phy's unique index, that can
+ be used for phy-specific requests
+ ``ETHTOOL_A_PHY_DRVNAME`` string the phy driver name
+ ``ETHTOOL_A_PHY_NAME`` string the phy device name
+ ``ETHTOOL_A_PHY_UPSTREAM_TYPE`` u32 the type of device this phy is
+ connected to
+ ``ETHTOOL_A_PHY_UPSTREAM_PHY`` nested if the phy is connected to another
+ phy, this nest contains info on
+ that connection
+ ``ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME`` string if the phy controls an sfp bus,
+ the name of the sfp bus
+ ``ETHTOOL_A_PHY_ID`` u32 the phy id if the phy is C22
+ ===================================== ====== ==========================
+
+When ``ETHTOOL_A_PHY_UPSTREAM_TYPE`` is PHY_UPSTREAM_PHY, the PHY's parent is
+another PHY. Information on the parent PHY will be set in the
+``ETHTOOL_A_PHY_UPSTREAM_PHY`` nest, which has the following structure :
+
+ =================================== ====== ==========================
+ ``ETHTOOL_A_PHY_UPSTREAM_INDEX`` u32 the PHY index of the upstream PHY
+ ``ETHTOOL_A_PHY_UPSTREAM_SFP_NAME`` string if this PHY is connected to it's
+ parent PHY through an SFP bus, the
+ name of this sfp bus
+ =================================== ====== ==========================
+
Request translation
===================

@@ -2117,4 +2160,5 @@ are netlink only.
n/a ``ETHTOOL_MSG_PLCA_GET_STATUS``
n/a ``ETHTOOL_MSG_MM_GET``
n/a ``ETHTOOL_MSG_MM_SET``
+ n/a ``ETHTOOL_MSG_PHY_GET``
=================================== =====================================
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 422e8cfdd98c..00cd7ad16709 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -57,6 +57,7 @@ enum {
ETHTOOL_MSG_PLCA_GET_STATUS,
ETHTOOL_MSG_MM_GET,
ETHTOOL_MSG_MM_SET,
+ ETHTOOL_MSG_PHY_GET,

/* add new constants above here */
__ETHTOOL_MSG_USER_CNT,
@@ -109,6 +110,8 @@ enum {
ETHTOOL_MSG_PLCA_NTF,
ETHTOOL_MSG_MM_GET_REPLY,
ETHTOOL_MSG_MM_NTF,
+ ETHTOOL_MSG_PHY_GET_REPLY,
+ ETHTOOL_MSG_PHY_NTF,

/* add new constants above here */
__ETHTOOL_MSG_KERNEL_CNT,
@@ -977,6 +980,32 @@ enum {
ETHTOOL_A_MM_MAX = (__ETHTOOL_A_MM_CNT - 1)
};

+enum {
+ ETHTOOL_A_PHY_UPSTREAM_UNSPEC,
+ ETHTOOL_A_PHY_UPSTREAM_INDEX, /* u32 */
+ ETHTOOL_A_PHY_UPSTREAM_SFP_NAME, /* string */
+
+ /* add new constants above here */
+ __ETHTOOL_A_PHY_UPSTREAM_CNT,
+ ETHTOOL_A_PHY_UPSTREAM_MAX = (__ETHTOOL_A_PHY_UPSTREAM_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_PHY_UNSPEC,
+ ETHTOOL_A_PHY_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_PHY_INDEX, /* u32 */
+ ETHTOOL_A_PHY_DRVNAME, /* string */
+ ETHTOOL_A_PHY_NAME, /* string */
+ ETHTOOL_A_PHY_UPSTREAM_TYPE, /* u8 */
+ ETHTOOL_A_PHY_UPSTREAM, /* nest - _A_PHY_UPSTREAM_* */
+ ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME, /* string */
+ ETHTOOL_A_PHY_ID, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_PHY_CNT,
+ ETHTOOL_A_PHY_MAX = (__ETHTOOL_A_PHY_CNT - 1)
+};
+
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
index 504f954a1b28..0ccd0e9afd3f 100644
--- a/net/ethtool/Makefile
+++ b/net/ethtool/Makefile
@@ -8,4 +8,4 @@ ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o rss.o \
linkstate.o debug.o wol.o features.o privflags.o rings.o \
channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \
tunnels.o fec.o eeprom.o stats.o phc_vclocks.o mm.o \
- module.o pse-pd.o plca.o mm.o
+ module.o pse-pd.o plca.o mm.o phy.o
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 1c26766ce996..92b0dd8ca046 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -1153,6 +1153,15 @@ static const struct genl_ops ethtool_genl_ops[] = {
.policy = ethnl_mm_set_policy,
.maxattr = ARRAY_SIZE(ethnl_mm_set_policy) - 1,
},
+ {
+ .cmd = ETHTOOL_MSG_PHY_GET,
+ .doit = ethnl_phy_doit,
+ .start = ethnl_phy_start,
+ .dumpit = ethnl_phy_dumpit,
+ .done = ethnl_phy_done,
+ .policy = ethnl_phy_get_policy,
+ .maxattr = ARRAY_SIZE(ethnl_phy_get_policy) - 1,
+ },
};

static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index def84e2def9e..5e6a43e35a09 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -444,6 +444,7 @@ extern const struct nla_policy ethnl_plca_set_cfg_policy[ETHTOOL_A_PLCA_MAX + 1]
extern const struct nla_policy ethnl_plca_get_status_policy[ETHTOOL_A_PLCA_HEADER + 1];
extern const struct nla_policy ethnl_mm_get_policy[ETHTOOL_A_MM_HEADER + 1];
extern const struct nla_policy ethnl_mm_set_policy[ETHTOOL_A_MM_MAX + 1];
+extern const struct nla_policy ethnl_phy_get_policy[ETHTOOL_A_PHY_HEADER + 1];

int ethnl_set_features(struct sk_buff *skb, struct genl_info *info);
int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info);
@@ -451,6 +452,10 @@ int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info);
int ethnl_tunnel_info_doit(struct sk_buff *skb, struct genl_info *info);
int ethnl_tunnel_info_start(struct netlink_callback *cb);
int ethnl_tunnel_info_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
+int ethnl_phy_start(struct netlink_callback *cb);
+int ethnl_phy_doit(struct sk_buff *skb, struct genl_info *info);
+int ethnl_phy_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
+int ethnl_phy_done(struct netlink_callback *cb);

extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN];
extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN];
diff --git a/net/ethtool/phy.c b/net/ethtool/phy.c
new file mode 100644
index 000000000000..5add2840aaeb
--- /dev/null
+++ b/net/ethtool/phy.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2023 Bootlin
+ *
+ */
+#include "common.h"
+#include "netlink.h"
+
+#include <linux/phy.h>
+#include <linux/phy_link_topology.h>
+#include <linux/sfp.h>
+
+struct phy_req_info {
+ struct ethnl_req_info base;
+ struct phy_device_node pdn;
+};
+
+#define PHY_REQINFO(__req_base) \
+ container_of(__req_base, struct phy_req_info, base)
+
+const struct nla_policy ethnl_phy_get_policy[ETHTOOL_A_PHY_HEADER + 1] = {
+ [ETHTOOL_A_PHY_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
+};
+
+/* Caller holds rtnl */
+static ssize_t
+ethnl_phy_reply_size(const struct ethnl_req_info *req_base,
+ struct netlink_ext_ack *extack)
+{
+ struct phy_link_topology *topo;
+ struct phy_device_node *pdn;
+ struct phy_device *phydev;
+ unsigned long index;
+ size_t size;
+
+ ASSERT_RTNL();
+
+ topo = &req_base->dev->link_topo;
+
+ size = nla_total_size(0);
+
+ xa_for_each(&topo->phys, index, pdn) {
+ phydev = pdn->phy;
+
+ /* ETHTOOL_A_PHY_INDEX */
+ size += nla_total_size(sizeof(u32));
+
+ /* ETHTOOL_A_DRVNAME */
+ size += nla_total_size(strlen(phydev->drv->name) + 1);
+
+ /* ETHTOOL_A_NAME */
+ size += nla_total_size(strlen(dev_name(&phydev->mdio.dev)) + 1);
+
+ /* ETHTOOL_A_PHY_UPSTREAM_TYPE */
+ size += nla_total_size(sizeof(u8));
+
+ /* ETHTOOL_A_PHY_ID */
+ size += nla_total_size(sizeof(u32));
+
+ if (phy_on_sfp(phydev)) {
+ const char *upstream_sfp_name = sfp_get_name(pdn->parent_sfp_bus);
+
+ /* ETHTOOL_A_PHY_UPSTREAM_SFP_NAME */
+ if (upstream_sfp_name)
+ size += nla_total_size(strlen(upstream_sfp_name) + 1);
+
+ /* ETHTOOL_A_PHY_UPSTREAM_INDEX */
+ size += nla_total_size(sizeof(u32));
+ }
+
+ /* ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME */
+ if (phydev->sfp_bus) {
+ const char *sfp_name = sfp_get_name(phydev->sfp_bus);
+
+ if (sfp_name)
+ size += nla_total_size(strlen(sfp_name) + 1);
+ }
+ }
+
+ return size;
+}
+
+static int
+ethnl_phy_fill_reply(const struct ethnl_req_info *req_base, struct sk_buff *skb)
+{
+ struct phy_req_info *req_info = PHY_REQINFO(req_base);
+ struct phy_device_node *pdn = &req_info->pdn;
+ struct phy_device *phydev = pdn->phy;
+ enum phy_upstream ptype;
+ struct nlattr *nest;
+
+ ptype = pdn->upstream_type;
+
+ if (nla_put_u32(skb, ETHTOOL_A_PHY_INDEX, phydev->phyindex) ||
+ nla_put_string(skb, ETHTOOL_A_PHY_DRVNAME, phydev->drv->name) ||
+ nla_put_string(skb, ETHTOOL_A_PHY_NAME, dev_name(&phydev->mdio.dev)) ||
+ nla_put_u8(skb, ETHTOOL_A_PHY_UPSTREAM_TYPE, ptype) ||
+ nla_put_u32(skb, ETHTOOL_A_PHY_ID, phydev->phy_id))
+ return -EMSGSIZE;
+
+ if (ptype == PHY_UPSTREAM_PHY) {
+ struct phy_device *upstream = pdn->upstream.phydev;
+ const char *sfp_upstream_name;
+
+ nest = nla_nest_start(skb, ETHTOOL_A_PHY_UPSTREAM);
+ if (!nest)
+ return -EMSGSIZE;
+
+ /* Parent index */
+ if (nla_put_u32(skb, ETHTOOL_A_PHY_UPSTREAM_INDEX, upstream->phyindex))
+ return -EMSGSIZE;
+
+ if (pdn->parent_sfp_bus) {
+ sfp_upstream_name = sfp_get_name(pdn->parent_sfp_bus);
+ if (sfp_upstream_name && nla_put_string(skb,
+ ETHTOOL_A_PHY_UPSTREAM_SFP_NAME,
+ sfp_upstream_name))
+ return -EMSGSIZE;
+ }
+
+ nla_nest_end(skb, nest);
+ }
+
+ if (phydev->sfp_bus) {
+ const char *sfp_name = sfp_get_name(phydev->sfp_bus);
+
+ if (sfp_name &&
+ nla_put_string(skb, ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME,
+ sfp_name))
+ return -EMSGSIZE;
+ }
+
+ return 0;
+}
+
+static int ethnl_phy_parse_request(struct ethnl_req_info *req_base,
+ struct nlattr **tb)
+{
+ struct phy_link_topology *topo = &req_base->dev->link_topo;
+ struct phy_req_info *req_info = PHY_REQINFO(req_base);
+ struct phy_device_node *pdn;
+
+ if (!req_base->phydev)
+ return 0;
+
+ pdn = xa_load(&topo->phys, req_base->phydev->phyindex);
+ memcpy(&req_info->pdn, pdn, sizeof(*pdn));
+
+ return 0;
+}
+
+int ethnl_phy_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct phy_req_info req_info = {};
+ struct nlattr **tb = info->attrs;
+ struct sk_buff *rskb;
+ void *reply_payload;
+ int reply_len;
+ int ret;
+
+ ret = ethnl_parse_header_dev_get(&req_info.base,
+ tb[ETHTOOL_A_PHY_HEADER],
+ genl_info_net(info), info->extack,
+ true);
+ if (ret < 0)
+ return ret;
+
+ rtnl_lock();
+
+ ret = ethnl_phy_parse_request(&req_info.base, tb);
+ if (ret < 0)
+ goto err_unlock_rtnl;
+
+ /* No PHY, return early */
+ if (!req_info.pdn.phy)
+ goto err_unlock_rtnl;
+
+ ret = ethnl_phy_reply_size(&req_info.base, info->extack);
+ if (ret < 0)
+ goto err_unlock_rtnl;
+ reply_len = ret + ethnl_reply_header_size();
+
+ rskb = ethnl_reply_init(reply_len, req_info.base.dev,
+ ETHTOOL_MSG_PHY_GET_REPLY,
+ ETHTOOL_A_PHY_HEADER,
+ info, &reply_payload);
+ if (!rskb) {
+ ret = -ENOMEM;
+ goto err_unlock_rtnl;
+ }
+
+ ret = ethnl_phy_fill_reply(&req_info.base, rskb);
+ if (ret)
+ goto err_free_msg;
+
+ rtnl_unlock();
+ ethnl_parse_header_dev_put(&req_info.base);
+ genlmsg_end(rskb, reply_payload);
+
+ return genlmsg_reply(rskb, info);
+
+err_free_msg:
+ nlmsg_free(rskb);
+err_unlock_rtnl:
+ rtnl_unlock();
+ ethnl_parse_header_dev_put(&req_info.base);
+ return ret;
+}
+
+struct ethnl_phy_dump_ctx {
+ struct phy_req_info *phy_req_info;
+};
+
+int ethnl_phy_start(struct netlink_callback *cb)
+{
+ const struct genl_dumpit_info *info = genl_dumpit_info(cb);
+ struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx;
+ struct nlattr **tb = info->info.attrs;
+ int ret;
+
+ BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx));
+
+ ctx->phy_req_info = kzalloc(sizeof(*ctx->phy_req_info), GFP_KERNEL);
+ if (!ctx->phy_req_info)
+ return -ENOMEM;
+
+ ret = ethnl_parse_header_dev_get(&ctx->phy_req_info->base,
+ tb[ETHTOOL_A_PHY_HEADER],
+ sock_net(cb->skb->sk), cb->extack,
+ false);
+ return ret;
+}
+
+int ethnl_phy_done(struct netlink_callback *cb)
+{
+ struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx;
+
+ kfree(ctx->phy_req_info);
+
+ return 0;
+}
+
+static int ethnl_phy_dump_one_dev(struct sk_buff *skb, struct net_device *dev,
+ struct netlink_callback *cb)
+{
+ struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx;
+ struct phy_req_info *pri = ctx->phy_req_info;
+ struct phy_device_node *pdn;
+ unsigned long index = 1;
+ int ret = 0;
+ void *ehdr;
+
+ pri->base.dev = dev;
+
+ xa_for_each(&dev->link_topo.phys, index, pdn) {
+ ehdr = ethnl_dump_put(skb, cb,
+ ETHTOOL_MSG_PHY_GET_REPLY);
+ if (!ehdr) {
+ ret = -EMSGSIZE;
+ break;
+ }
+
+ ret = ethnl_fill_reply_header(skb, dev,
+ ETHTOOL_A_PHY_HEADER);
+ if (ret < 0) {
+ genlmsg_cancel(skb, ehdr);
+ break;
+ }
+
+ memcpy(&pri->pdn, pdn, sizeof(*pdn));
+ ret = ethnl_phy_fill_reply(&pri->base, skb);
+
+ genlmsg_end(skb, ehdr);
+ }
+
+ return ret;
+}
+
+int ethnl_phy_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx;
+ struct net *net = sock_net(skb->sk);
+ unsigned long ifindex = 1;
+ struct net_device *dev;
+ int ret = 0;
+
+ rtnl_lock();
+
+ if (ctx->phy_req_info->base.dev) {
+ ret = ethnl_phy_dump_one_dev(skb, ctx->phy_req_info->base.dev, cb);
+ ethnl_parse_header_dev_put(&ctx->phy_req_info->base);
+ ctx->phy_req_info->base.dev = NULL;
+ } else {
+ for_each_netdev_dump(net, dev, ifindex) {
+ ret = ethnl_phy_dump_one_dev(skb, dev, cb);
+ if (ret)
+ break;
+ }
+ }
+ rtnl_unlock();
+
+ if (ret == -EMSGSIZE && skb->len)
+ return skb->len;
+ return ret;
+}
+
--
2.43.0


2023-12-21 18:03:56

by Maxime Chevallier

[permalink] [raw]
Subject: [PATCH net-next v5 08/13] netlink: specs: add ethnl PHY_GET command set

The PHY_GET command, supporting both DUMP and GET operations, is used to
retrieve the list of PHYs connected to a netdevice, and get topology
information to know where exactly it sits on the physical link.

Add the netlink specs corresponding to that command.

Signed-off-by: Maxime Chevallier <[email protected]>
---
V5: No changes
V4: Remove the ethtool-user generated code
V3: New patch

Documentation/netlink/specs/ethtool.yaml | 65 ++++++++++++++++++++++++
1 file changed, 65 insertions(+)

diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index bb6e1dc6d1c5..7f6fb1f61dd4 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -16,6 +16,11 @@ definitions:
name: stringset
type: enum
entries: []
+ -
+ name: phy-upstream-type
+ enum-name:
+ type: enum
+ entries: [ mac, phy ]

attribute-sets:
-
@@ -945,6 +950,45 @@ attribute-sets:
-
name: burst-tmr
type: u32
+ -
+ name: phy-upstream
+ attributes:
+ -
+ name: index
+ type: u32
+ -
+ name: sfp-name
+ type: string
+ -
+ name: phy
+ attributes:
+ -
+ name: header
+ type: nest
+ nested-attributes: header
+ -
+ name: index
+ type: u32
+ -
+ name: drvname
+ type: string
+ -
+ name: name
+ type: string
+ -
+ name: upstream-type
+ type: u8
+ enum: phy-upstream-type
+ -
+ name: upstream
+ type: nest
+ nested-attributes: phy-upstream
+ -
+ name: downstream-sfp-name
+ type: string
+ -
+ name: id
+ type: u32

operations:
enum-model: directional
@@ -1696,3 +1740,24 @@ operations:
name: mm-ntf
doc: Notification for change in MAC Merge configuration.
notify: mm-get
+ -
+ name: phy-get
+ doc: Get PHY devices attached to an interface
+
+ attribute-set: phy
+
+ do: &phy-get-op
+ request:
+ attributes:
+ - header
+ reply:
+ attributes:
+ - header
+ - index
+ - drvname
+ - name
+ - upstream-type
+ - upstream
+ - downstream-sfp-name
+ - id
+ dump: *phy-get-op
--
2.43.0


2023-12-21 18:04:04

by Maxime Chevallier

[permalink] [raw]
Subject: [PATCH net-next v5 09/13] net: ethtool: plca: Target the command to the requested PHY

PLCA is a PHY-specific command. Instead of targeting the command
towards dev->phydev, use the request to pick the targeted PHY.

Signed-off-by: Maxime Chevallier <[email protected]>
Reviewed-by: Andrew Lunn <[email protected]>
---
V5: Added Andrew's R-b
V4: No changes
V3: No changes
V2: New patch

net/ethtool/plca.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/net/ethtool/plca.c b/net/ethtool/plca.c
index b1e2e3b5027f..2b3e419f4dc2 100644
--- a/net/ethtool/plca.c
+++ b/net/ethtool/plca.c
@@ -61,7 +61,7 @@ static int plca_get_cfg_prepare_data(const struct ethnl_req_info *req_base,
int ret;

// check that the PHY device is available and connected
- if (!dev->phydev) {
+ if (!req_base->phydev) {
ret = -EOPNOTSUPP;
goto out;
}
@@ -80,7 +80,7 @@ static int plca_get_cfg_prepare_data(const struct ethnl_req_info *req_base,
memset(&data->plca_cfg, 0xff,
sizeof_field(struct plca_reply_data, plca_cfg));

- ret = ops->get_plca_cfg(dev->phydev, &data->plca_cfg);
+ ret = ops->get_plca_cfg(req_base->phydev, &data->plca_cfg);
ethnl_ops_complete(dev);

out:
@@ -141,7 +141,6 @@ const struct nla_policy ethnl_plca_set_cfg_policy[] = {
static int
ethnl_set_plca(struct ethnl_req_info *req_info, struct genl_info *info)
{
- struct net_device *dev = req_info->dev;
const struct ethtool_phy_ops *ops;
struct nlattr **tb = info->attrs;
struct phy_plca_cfg plca_cfg;
@@ -149,7 +148,7 @@ ethnl_set_plca(struct ethnl_req_info *req_info, struct genl_info *info)
int ret;

// check that the PHY device is available and connected
- if (!dev->phydev)
+ if (!req_info->phydev)
return -EOPNOTSUPP;

ops = ethtool_phy_ops;
@@ -168,7 +167,7 @@ ethnl_set_plca(struct ethnl_req_info *req_info, struct genl_info *info)
if (!mod)
return 0;

- ret = ops->set_plca_cfg(dev->phydev, &plca_cfg, info->extack);
+ ret = ops->set_plca_cfg(req_info->phydev, &plca_cfg, info->extack);
return ret < 0 ? ret : 1;
}

@@ -204,7 +203,7 @@ static int plca_get_status_prepare_data(const struct ethnl_req_info *req_base,
int ret;

// check that the PHY device is available and connected
- if (!dev->phydev) {
+ if (!req_base->phydev) {
ret = -EOPNOTSUPP;
goto out;
}
@@ -223,7 +222,7 @@ static int plca_get_status_prepare_data(const struct ethnl_req_info *req_base,
memset(&data->plca_st, 0xff,
sizeof_field(struct plca_reply_data, plca_st));

- ret = ops->get_plca_status(dev->phydev, &data->plca_st);
+ ret = ops->get_plca_status(req_base->phydev, &data->plca_st);
ethnl_ops_complete(dev);
out:
return ret;
--
2.43.0


2023-12-21 18:05:26

by Maxime Chevallier

[permalink] [raw]
Subject: [PATCH net-next v5 12/13] net: ethtool: strset: Allow querying phy stats by index

The ETH_SS_PHY_STATS command gets PHY statistics. Use the phydev pointer
from the ethnl request to allow query phy stats from each PHY on the
link.

Signed-off-by: Maxime Chevallier <[email protected]>
Reviewed-by: Andrew Lunn <[email protected]>
---
V5: Added Andrew's R-b
V4: No changes
V3: No changes
V2: New patch

net/ethtool/strset.c | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/net/ethtool/strset.c b/net/ethtool/strset.c
index c678b484a079..70c00631c51f 100644
--- a/net/ethtool/strset.c
+++ b/net/ethtool/strset.c
@@ -233,17 +233,18 @@ static void strset_cleanup_data(struct ethnl_reply_data *reply_base)
}

static int strset_prepare_set(struct strset_info *info, struct net_device *dev,
- unsigned int id, bool counts_only)
+ struct phy_device *phydev, unsigned int id,
+ bool counts_only)
{
const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops;
const struct ethtool_ops *ops = dev->ethtool_ops;
void *strings;
int count, ret;

- if (id == ETH_SS_PHY_STATS && dev->phydev &&
+ if (id == ETH_SS_PHY_STATS && phydev &&
!ops->get_ethtool_phy_stats && phy_ops &&
phy_ops->get_sset_count)
- ret = phy_ops->get_sset_count(dev->phydev);
+ ret = phy_ops->get_sset_count(phydev);
else if (ops->get_sset_count && ops->get_strings)
ret = ops->get_sset_count(dev, id);
else
@@ -258,10 +259,10 @@ static int strset_prepare_set(struct strset_info *info, struct net_device *dev,
strings = kcalloc(count, ETH_GSTRING_LEN, GFP_KERNEL);
if (!strings)
return -ENOMEM;
- if (id == ETH_SS_PHY_STATS && dev->phydev &&
+ if (id == ETH_SS_PHY_STATS && phydev &&
!ops->get_ethtool_phy_stats && phy_ops &&
phy_ops->get_strings)
- phy_ops->get_strings(dev->phydev, strings);
+ phy_ops->get_strings(phydev, strings);
else
ops->get_strings(dev, id, strings);
info->strings = strings;
@@ -305,8 +306,8 @@ static int strset_prepare_data(const struct ethnl_req_info *req_base,
!data->sets[i].per_dev)
continue;

- ret = strset_prepare_set(&data->sets[i], dev, i,
- req_info->counts_only);
+ ret = strset_prepare_set(&data->sets[i], dev, req_base->phydev,
+ i, req_info->counts_only);
if (ret < 0)
goto err_ops;
}
--
2.43.0


2023-12-21 18:06:49

by Maxime Chevallier

[permalink] [raw]
Subject: [PATCH net-next v5 10/13] net: ethtool: pse-pd: Target the command to the requested PHY

PSE and PD configuration is a PHY-specific command. Instead of targeting
the command towards dev->phydev, use the request to pick the targeted
PHY device.

Signed-off-by: Maxime Chevallier <[email protected]>
---
V5: Added-back an incorrectly removed check
V4: No changes
V3: No changes
V2: New patch

net/ethtool/pse-pd.c | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/net/ethtool/pse-pd.c b/net/ethtool/pse-pd.c
index cc478af77111..4a1c8d37bd3d 100644
--- a/net/ethtool/pse-pd.c
+++ b/net/ethtool/pse-pd.c
@@ -31,12 +31,10 @@ const struct nla_policy ethnl_pse_get_policy[ETHTOOL_A_PSE_HEADER + 1] = {
[ETHTOOL_A_PSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
};

-static int pse_get_pse_attributes(struct net_device *dev,
+static int pse_get_pse_attributes(struct phy_device *phydev,
struct netlink_ext_ack *extack,
struct pse_reply_data *data)
{
- struct phy_device *phydev = dev->phydev;
-
if (!phydev) {
NL_SET_ERR_MSG(extack, "No PHY is attached");
return -EOPNOTSUPP;
@@ -64,7 +62,7 @@ static int pse_prepare_data(const struct ethnl_req_info *req_base,
if (ret < 0)
return ret;

- ret = pse_get_pse_attributes(dev, info->extack, data);
+ ret = pse_get_pse_attributes(req_base->phydev, info->extack, data);

ethnl_ops_complete(dev);

@@ -124,7 +122,6 @@ ethnl_set_pse_validate(struct ethnl_req_info *req_info, struct genl_info *info)
static int
ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info)
{
- struct net_device *dev = req_info->dev;
struct pse_control_config config = {};
struct nlattr **tb = info->attrs;
struct phy_device *phydev;
@@ -132,7 +129,7 @@ ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info)
/* this values are already validated by the ethnl_pse_set_policy */
config.admin_cotrol = nla_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL]);

- phydev = dev->phydev;
+ phydev = req_info->phydev;
if (!phydev) {
NL_SET_ERR_MSG(info->extack, "No PHY is attached");
return -EOPNOTSUPP;
--
2.43.0


2023-12-21 18:07:10

by Maxime Chevallier

[permalink] [raw]
Subject: [PATCH net-next v5 11/13] net: ethtool: cable-test: Target the command to the requested PHY

Cable testing is a PHY-specific command. Instead of targeting the command
towards dev->phydev, use the request to pick the targeted PHY.

Signed-off-by: Maxime Chevallier <[email protected]>
Reviewed-by: Andrew Lunn <[email protected]>
---
V5: Added Andrew's R-b
V4: No changes
V3: No changes
V2: New patch

net/ethtool/cabletest.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/net/ethtool/cabletest.c b/net/ethtool/cabletest.c
index 06a151165c31..6b00d0800f23 100644
--- a/net/ethtool/cabletest.c
+++ b/net/ethtool/cabletest.c
@@ -69,7 +69,7 @@ int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info)
return ret;

dev = req_info.dev;
- if (!dev->phydev) {
+ if (!req_info.phydev) {
ret = -EOPNOTSUPP;
goto out_dev_put;
}
@@ -85,12 +85,12 @@ int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info)
if (ret < 0)
goto out_rtnl;

- ret = ops->start_cable_test(dev->phydev, info->extack);
+ ret = ops->start_cable_test(req_info.phydev, info->extack);

ethnl_ops_complete(dev);

if (!ret)
- ethnl_cable_test_started(dev->phydev,
+ ethnl_cable_test_started(req_info.phydev,
ETHTOOL_MSG_CABLE_TEST_NTF);

out_rtnl:
@@ -321,7 +321,7 @@ int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info)
return ret;

dev = req_info.dev;
- if (!dev->phydev) {
+ if (!req_info.phydev) {
ret = -EOPNOTSUPP;
goto out_dev_put;
}
@@ -342,12 +342,12 @@ int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info)
if (ret < 0)
goto out_rtnl;

- ret = ops->start_cable_test_tdr(dev->phydev, info->extack, &cfg);
+ ret = ops->start_cable_test_tdr(req_info.phydev, info->extack, &cfg);

ethnl_ops_complete(dev);

if (!ret)
- ethnl_cable_test_started(dev->phydev,
+ ethnl_cable_test_started(req_info.phydev,
ETHTOOL_MSG_CABLE_TEST_TDR_NTF);

out_rtnl:
--
2.43.0


2023-12-21 18:07:55

by Maxime Chevallier

[permalink] [raw]
Subject: [PATCH net-next v5 13/13] Documentation: networking: document phy_link_topology

The newly introduced phy_link_topology tracks all ethernet PHYs that are
attached to a netdevice. Document the base principle, internal and
external APIs. As the phy_link_topology is expected to be extended, this
documentation will hold any further improvements and additions made
relative to topology handling.

Signed-off-by: Maxime Chevallier <[email protected]>
---
V5: Fixed a lot of typos
V4: No changes
V3: New patch

Documentation/networking/index.rst | 1 +
.../networking/phy-link-topology.rst | 121 ++++++++++++++++++
2 files changed, 122 insertions(+)
create mode 100644 Documentation/networking/phy-link-topology.rst

diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst
index 69f3d6dcd9fd..a2c45a75a4a6 100644
--- a/Documentation/networking/index.rst
+++ b/Documentation/networking/index.rst
@@ -88,6 +88,7 @@ Contents:
operstates
packet_mmap
phonet
+ phy-link-topology
pktgen
plip
ppp_generic
diff --git a/Documentation/networking/phy-link-topology.rst b/Documentation/networking/phy-link-topology.rst
new file mode 100644
index 000000000000..1fd8e904ef4b
--- /dev/null
+++ b/Documentation/networking/phy-link-topology.rst
@@ -0,0 +1,121 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=================
+PHY link topology
+=================
+
+Overview
+========
+
+The PHY link topology representation in the networking stack aims at representing
+the hardware layout for any given Ethernet link.
+
+An Ethernet Interface from userspace's point of view is nothing but a
+:c:type:`struct net_device <net_device>`, which exposes configuration options
+through the legacy ioctls and the ethool netlink commands. The base assumption
+when designing these configuration channels were that the link looked
+something like this ::
+
+ +-----------------------+ +----------+ +--------------+
+ | Ethernet Controller / | | Ethernet | | Connector / |
+ | MAC | ------ | PHY | ---- | Port | ---... to LP
+ +-----------------------+ +----------+ +--------------+
+ struct net_device struct phy_device
+
+Commands that needs to configure the PHY will go through the net_device.phydev
+field to reach the PHY and perform the relevant configuration.
+
+This assumption falls apart in more complex topologies that can arise when,
+for example, using SFP transceivers (although that's not the only specific case).
+
+Here, we have 2 basic scenarios. Either the MAC is able to output a serialized
+interface, that can directly be fed to an SFP cage, such as SGMII, 1000BaseX,
+10GBaseR, etc.
+
+The link topology then looks like this (when an SFP module is inserted) ::
+
+ +-----+ SGMII +------------+
+ | MAC | ------- | SFP Module |
+ +-----+ +------------+
+
+Knowing that some modules embed a PHY, the actual link is more like ::
+
+ +-----+ SGMII +--------------+
+ | MAC | -------- | PHY (on SFP) |
+ +-----+ +--------------+
+
+In this case, the SFP PHY is handled by phylib, and registered by phylink through
+its SFP upstream ops.
+
+Now some Ethernet controllers aren't able to output a serialized interface, so
+we can't directly connect them to an SFP cage. However, some PHYs can be used
+as media-converters, to translate the non-serialized MAC MII interface to a
+serialized MII interface fed to the SFP ::
+
+ +-----+ RGMII +-----------------------+ SGMII +--------------+
+ | MAC | ------- | PHY (media converter) | ------- | PHY (on SFP) |
+ +-----+ +-----------------------+ +--------------+
+
+This is where the model of having a single net_device.phydev pointer shows its
+limitations, as we now have 2 PHYs on the link.
+
+The phy_link topology framework aims at providing a way to keep track of every
+PHY on the link, for use by both kernel drivers and subsystems, but also to
+report the topology to userspace, allowing to target individual PHYs in configuration
+commands.
+
+API
+===
+
+The :c:type:`struct phy_link_topology <phy_link_topology>` is a per-netdevice
+resource, that gets initialized at netdevice creation. Once it's initialized,
+it is then possible to register PHYs to the topology through :
+
+:c:func:`phy_link_topo_add_phy`
+
+Besides registering the PHY to the topology, this call will also assign a unique
+index to the PHY, which can then be reported to userspace to refer to this PHY
+(akin to the ifindex). This index is a u32, ranging from 1 to U32_MAX. The value
+0 is reserved to indicate the PHY doesn't belong to any topology yet.
+
+The PHY can then be removed from the topology through
+
+:c:func:`phy_link_topo_del_phy`
+
+These function are already hooked into the phylib subsystem, so all PHYs that
+are linked to a net_device through :c:func:`phy_attach_direct` will automatically
+join the netdev's topology.
+
+PHYs that are on a SFP module will also be automatically registered IF the SFP
+upstream is phylink (so, no media-converter).
+
+PHY drivers that can be used as SFP upstream need to call :c:func:`phy_sfp_attach_phy`
+and :c:func:`phy_sfp_detach_phy`, which can be used as a
+.attach_phy / .detach_phy implementation for the
+:c:type:`struct sfp_upstream_ops <sfp_upstream_ops>`.
+
+UAPI
+====
+
+There exist a set of netlink commands to query the link topology from userspace,
+see ``Documentation/networking/ethtool-netlink.rst``.
+
+The whole point of having a topology representation is to assign the phyindex
+field in :c:type:`struct phy_device <phy_device>`. This index is reported to
+userspace using the ``ETHTOOL_MSG_PHY_GET`` ethtnl command. Performing a DUMP operation
+will result in all PHYs from all net_device being listed. The DUMP command
+accepts either a ``ETHTOOL_A_HEADER_DEV_INDEX`` or ``ETHTOOL_A_HEADER_DEV_NAME``
+to be passed in the request to filter the DUMP to a single net_device.
+
+The retrieved index can then be passed as a request parameter using the
+``ETHTOOL_A_HEADER_PHY_INDEX`` field in the following ethnl commands :
+
+* ``ETHTOOL_MSG_STRSET_GET`` to get the stats string set from a given PHY
+* ``ETHTOOL_MSG_CABLE_TEST_ACT`` and ``ETHTOOL_MSG_CABLE_TEST_ACT``, to perform
+ cable testing on a given PHY on the link (most likely the outermost PHY)
+* ``ETHTOOL_MSG_PSE_SET`` and ``ETHTOOL_MSG_PSE_GET`` for PHY-controlled PoE and PSE settings
+* ``ETHTOOL_MSG_PLCA_GET_CFG``, ``ETHTOOL_MSG_PLCA_SET_CFG`` and ``ETHTOOL_MSG_PLCA_GET_STATUS``
+ to set the PLCA (Physical Layer Collision Avoidance) parameters
+
+Note that the PHY index can be passed to other requests, which will silently
+ignore it if present and irrelevant.
--
2.43.0


2024-01-01 18:40:38

by patchwork-bot+netdevbpf

[permalink] [raw]
Subject: Re: [PATCH net-next v5 00/13] Introduce PHY listing and link_topology tracking

Hello:

This series was applied to netdev/net-next.git (main)
by David S. Miller <[email protected]>:

On Thu, 21 Dec 2023 19:00:33 +0100 you wrote:
> Hello everyone,
>
> 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.
>
> [...]

Here is the summary with links:
- [net-next,v5,01/13] net: phy: Introduce ethernet link topology representation
https://git.kernel.org/netdev/net-next/c/02018c544ef1
- [net-next,v5,02/13] net: sfp: pass the phy_device when disconnecting an sfp module's PHY
https://git.kernel.org/netdev/net-next/c/9c5625f559ad
- [net-next,v5,03/13] net: phy: add helpers to handle sfp phy connect/disconnect
https://git.kernel.org/netdev/net-next/c/034fcc210349
- [net-next,v5,04/13] net: sfp: Add helper to return the SFP bus name
https://git.kernel.org/netdev/net-next/c/dedd702a3579
- [net-next,v5,05/13] net: ethtool: Allow passing a phy index for some commands
https://git.kernel.org/netdev/net-next/c/2ab0edb505fa
- [net-next,v5,06/13] netlink: specs: add phy-index as a header parameter
https://git.kernel.org/netdev/net-next/c/c29451aefcb4
- [net-next,v5,07/13] net: ethtool: Introduce a command to list PHYs on an interface
https://git.kernel.org/netdev/net-next/c/63d5eaf35ac3
- [net-next,v5,08/13] netlink: specs: add ethnl PHY_GET command set
https://git.kernel.org/netdev/net-next/c/95132a018f00
- [net-next,v5,09/13] net: ethtool: plca: Target the command to the requested PHY
https://git.kernel.org/netdev/net-next/c/7db69ec9cfb8
- [net-next,v5,10/13] net: ethtool: pse-pd: Target the command to the requested PHY
https://git.kernel.org/netdev/net-next/c/345237dbc1bd
- [net-next,v5,11/13] net: ethtool: cable-test: Target the command to the requested PHY
https://git.kernel.org/netdev/net-next/c/fcc4b105caa4
- [net-next,v5,12/13] net: ethtool: strset: Allow querying phy stats by index
https://git.kernel.org/netdev/net-next/c/d078d480639a
- [net-next,v5,13/13] Documentation: networking: document phy_link_topology
https://git.kernel.org/netdev/net-next/c/32bb4515e344

You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



2024-01-02 11:57:27

by Russell King (Oracle)

[permalink] [raw]
Subject: Re: [PATCH net-next v5 00/13] Introduce PHY listing and link_topology tracking

... and I haven't reviewed this yet. I guess it's now pointless to
review.

On Mon, Jan 01, 2024 at 06:40:27PM +0000, [email protected] wrote:
> Hello:
>
> This series was applied to netdev/net-next.git (main)
> by David S. Miller <[email protected]>:
>
> On Thu, 21 Dec 2023 19:00:33 +0100 you wrote:
> > Hello everyone,
> >
> > 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.
> >
> > [...]
>
> Here is the summary with links:
> - [net-next,v5,01/13] net: phy: Introduce ethernet link topology representation
> https://git.kernel.org/netdev/net-next/c/02018c544ef1
> - [net-next,v5,02/13] net: sfp: pass the phy_device when disconnecting an sfp module's PHY
> https://git.kernel.org/netdev/net-next/c/9c5625f559ad
> - [net-next,v5,03/13] net: phy: add helpers to handle sfp phy connect/disconnect
> https://git.kernel.org/netdev/net-next/c/034fcc210349
> - [net-next,v5,04/13] net: sfp: Add helper to return the SFP bus name
> https://git.kernel.org/netdev/net-next/c/dedd702a3579
> - [net-next,v5,05/13] net: ethtool: Allow passing a phy index for some commands
> https://git.kernel.org/netdev/net-next/c/2ab0edb505fa
> - [net-next,v5,06/13] netlink: specs: add phy-index as a header parameter
> https://git.kernel.org/netdev/net-next/c/c29451aefcb4
> - [net-next,v5,07/13] net: ethtool: Introduce a command to list PHYs on an interface
> https://git.kernel.org/netdev/net-next/c/63d5eaf35ac3
> - [net-next,v5,08/13] netlink: specs: add ethnl PHY_GET command set
> https://git.kernel.org/netdev/net-next/c/95132a018f00
> - [net-next,v5,09/13] net: ethtool: plca: Target the command to the requested PHY
> https://git.kernel.org/netdev/net-next/c/7db69ec9cfb8
> - [net-next,v5,10/13] net: ethtool: pse-pd: Target the command to the requested PHY
> https://git.kernel.org/netdev/net-next/c/345237dbc1bd
> - [net-next,v5,11/13] net: ethtool: cable-test: Target the command to the requested PHY
> https://git.kernel.org/netdev/net-next/c/fcc4b105caa4
> - [net-next,v5,12/13] net: ethtool: strset: Allow querying phy stats by index
> https://git.kernel.org/netdev/net-next/c/d078d480639a
> - [net-next,v5,13/13] Documentation: networking: document phy_link_topology
> https://git.kernel.org/netdev/net-next/c/32bb4515e344
>
> You are awesome, thank you!
> --
> Deet-doot-dot, I am a bot.
> https://korg.docs.kernel.org/patchwork/pwbot.html
>
>
>

--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

2024-01-02 18:51:40

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next v5 00/13] Introduce PHY listing and link_topology tracking

On Tue, 2 Jan 2024 11:57:09 +0000 Russell King (Oracle) wrote:
> ... and I haven't reviewed this yet. I guess it's now pointless to
> review.

I guess the shutdown was only a partial success. Nobody cleaned out
pending stuff on the 23rd, and old things got applied now before we
even officially reopened :( It is what it is, please review anyway,
we'll be reverting things which shouldn't have been applied..

2024-01-03 14:33:54

by Maxime Chevallier

[permalink] [raw]
Subject: Re: [PATCH net-next v5 00/13] Introduce PHY listing and link_topology tracking

Hi Russell, Jakub,

On Tue, 2 Jan 2024 10:51:25 -0800
Jakub Kicinski <[email protected]> wrote:

> On Tue, 2 Jan 2024 11:57:09 +0000 Russell King (Oracle) wrote:
> > ... and I haven't reviewed this yet. I guess it's now pointless to
> > review.
>
> I guess the shutdown was only a partial success. Nobody cleaned out
> pending stuff on the 23rd, and old things got applied now before we
> even officially reopened :( It is what it is, please review anyway,
> we'll be reverting things which shouldn't have been applied..

I've submitted the ethtool counterpart of that work a few seconds ago :
https://lore.kernel.org/netdev/[email protected]/T/#m334b7cec4be1c78d399d0899a30d522ab57b4622

I think this could help in reviewing the overall design and identifying
any glaring issue with this.

Thanks,

Maxime

2024-01-03 15:20:45

by Russell King (Oracle)

[permalink] [raw]
Subject: Re: [PATCH net-next v5 02/13] net: sfp: pass the phy_device when disconnecting an sfp module's PHY

On Thu, Dec 21, 2023 at 07:00:35PM +0100, Maxime Chevallier wrote:
> Pass the phy_device as a parameter to the sfp upstream .disconnect_phy
> operation. This is preparatory work to help track phy devices across
> a net_device's link.
>
> Signed-off-by: Maxime Chevallier <[email protected]>
> ---
> V5: No changes
> V4: No changes
> V3: No changes
>
> drivers/net/phy/phy_device.c | 8 ++++++++
> drivers/net/phy/phylink.c | 3 ++-
> drivers/net/phy/sfp-bus.c | 4 ++--
> include/linux/sfp.h | 2 +-
> 4 files changed, 13 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
> index d7c0812bd107..aad78e3f7894 100644
> --- a/drivers/net/phy/phy_device.c
> +++ b/drivers/net/phy/phy_device.c
> @@ -266,6 +266,14 @@ static void phy_mdio_device_remove(struct mdio_device *mdiodev)
>
> static struct phy_driver genphy_driver;
>
> +static struct phy_link_topology *phy_get_link_topology(struct phy_device *phydev)
> +{
> + if (phydev->attached_dev)
> + return &phydev->attached_dev->link_topo;
> +
> + return NULL;
> +}
> +
> static LIST_HEAD(phy_fixup_list);
> static DEFINE_MUTEX(phy_fixup_lock);
>

This should be in a different patch, it seems unrelated to the remainder
of this patch, and as it's static and no other changes to this file,
this would cause a build warning.

--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

2024-01-03 17:45:44

by Maxime Chevallier

[permalink] [raw]
Subject: Re: [PATCH net-next v5 02/13] net: sfp: pass the phy_device when disconnecting an sfp module's PHY

Hi Russell,

On Wed, 3 Jan 2024 15:20:25 +0000
"Russell King (Oracle)" <[email protected]> wrote:

> On Thu, Dec 21, 2023 at 07:00:35PM +0100, Maxime Chevallier wrote:
> > Pass the phy_device as a parameter to the sfp upstream .disconnect_phy
> > operation. This is preparatory work to help track phy devices across
> > a net_device's link.
> >
> > Signed-off-by: Maxime Chevallier <[email protected]>
> > ---
> > V5: No changes
> > V4: No changes
> > V3: No changes
> >
> > drivers/net/phy/phy_device.c | 8 ++++++++
> > drivers/net/phy/phylink.c | 3 ++-
> > drivers/net/phy/sfp-bus.c | 4 ++--
> > include/linux/sfp.h | 2 +-
> > 4 files changed, 13 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
> > index d7c0812bd107..aad78e3f7894 100644
> > --- a/drivers/net/phy/phy_device.c
> > +++ b/drivers/net/phy/phy_device.c
> > @@ -266,6 +266,14 @@ static void phy_mdio_device_remove(struct mdio_device *mdiodev)
> >
> > static struct phy_driver genphy_driver;
> >
> > +static struct phy_link_topology *phy_get_link_topology(struct phy_device *phydev)
> > +{
> > + if (phydev->attached_dev)
> > + return &phydev->attached_dev->link_topo;
> > +
> > + return NULL;
> > +}
> > +
> > static LIST_HEAD(phy_fixup_list);
> > static DEFINE_MUTEX(phy_fixup_lock);
> >
>
> This should be in a different patch, it seems unrelated to the remainder
> of this patch, and as it's static and no other changes to this file,
> this would cause a build warning.

Arg looks like I squashed that bit here to the wrong commit while
rebasing, it should have been part of commit 03/13 indeed... Sorry for
the hiccup.

Maxime


2024-01-04 23:12:55

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next v5 01/13] net: phy: Introduce ethernet link topology representation

On Thu, 21 Dec 2023 19:00:34 +0100 Maxime Chevallier wrote:
> @@ -2441,6 +2442,7 @@ struct net_device {
> #if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
> struct netprio_map __rcu *priomap;
> #endif
> + struct phy_link_topology link_topo;

Perhaps others would disagree but can we make this a pointer instead?
Only allocate it on demand, when first PHY gets attached?
Both saves space and netdevice.h will no longer need to know the
definition of the struct.

Complete noob question but I thought PHYs get attached at ndo_open
time for drivers, don't they? We shouldn't want to re-ID in that case.

> struct phy_device *phydev;
> struct sfp_bus *sfp_bus;
> struct lock_class_key *qdisc_tx_busylock;

> @@ -10872,6 +10873,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
> #ifdef CONFIG_NET_SCHED
> hash_init(dev->qdisc_hash);
> #endif
> + phy_link_topo_init(&dev->link_topo);
> +
> dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM;
> setup(dev);
>

I think you're missing a call to xa_destroy() somewhere, no?

2024-01-04 23:15:28

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/13] net: ethtool: Allow passing a phy index for some commands

On Thu, 21 Dec 2023 19:00:38 +0100 Maxime Chevallier wrote:
> @@ -20,6 +21,7 @@ const struct nla_policy ethnl_header_policy[] = {
> .len = ALTIFNAMSIZ - 1 },
> [ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32,
> ETHTOOL_FLAGS_BASIC),
> + [ETHTOOL_A_HEADER_PHY_INDEX] = NLA_POLICY_MIN(NLA_U32, 1),
> };
>
> const struct nla_policy ethnl_header_policy_stats[] = {
> @@ -28,6 +30,7 @@ const struct nla_policy ethnl_header_policy_stats[] = {
> .len = ALTIFNAMSIZ - 1 },
> [ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32,
> ETHTOOL_FLAGS_STATS),
> + [ETHTOOL_A_HEADER_PHY_INDEX] = NLA_POLICY_MIN(NLA_U32, 1),
> };

You should define (copy/paste) a new header policy and use it
only for commands which actually make use of the PHY index.
That's why we have separate copies already for stats.

2024-01-04 23:34:19

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next v5 07/13] net: ethtool: Introduce a command to list PHYs on an interface

On Thu, 21 Dec 2023 19:00:40 +0100 Maxime Chevallier wrote:
> As we have the ability to track the PHYs connected to a net_device
> through the link_topology, we can expose this list to userspace. This
> allows userspace to use these identifiers for phy-specific commands and
> take the decision of which PHY to target by knowing the link topology.
>
> Add PHY_GET and PHY_DUMP, which can be a filtered DUMP operation to list
> devices on only one interface.
>
> Signed-off-by: Maxime Chevallier <[email protected]>

> diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
> index 3ca6c21e74af..97ff787a7dd8 100644
> --- a/Documentation/networking/ethtool-netlink.rst
> +++ b/Documentation/networking/ethtool-netlink.rst
> @@ -2011,6 +2011,49 @@ The attributes are propagated to the driver through the following structure:
> .. kernel-doc:: include/linux/ethtool.h
> :identifiers: ethtool_mm_cfg
>
> +PHY_GET
> +=======
> +
> +Retrieve information about a given Ethernet PHY sitting on the link. As there
> +can be more than one PHY, the DUMP operation can be used to list the PHYs
> +present on a given interface, by passing an interface index or name in
> +the dump request
> +
> +Request contents:
> +
> + ==================================== ====== ==========================
> + ``ETHTOOL_A_PHY_HEADER`` nested request header
> + ==================================== ====== ==========================
> +
> +Kernel response contents:
> +
> + ===================================== ====== ==========================
> + ``ETHTOOL_A_PHY_HEADER`` nested request header
> + ``ETHTOOL_A_PHY_INDEX`` u32 the phy's unique index, that can

The fact that lines are longer than the ===== markings doesn't generate
warnings in htmldoc?

> + be used for phy-specific requests
> + ``ETHTOOL_A_PHY_DRVNAME`` string the phy driver name
> + ``ETHTOOL_A_PHY_NAME`` string the phy device name
> + ``ETHTOOL_A_PHY_UPSTREAM_TYPE`` u32 the type of device this phy is
> + connected to
> + ``ETHTOOL_A_PHY_UPSTREAM_PHY`` nested if the phy is connected to another
> + phy, this nest contains info on
> + that connection
> + ``ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME`` string if the phy controls an sfp bus,
> + the name of the sfp bus

Is upstream / downstream clear to everyone / from the spec.
I guess it's scoped to the netdev so upstream means "towards
the netdev MAC"?

> + ``ETHTOOL_A_PHY_ID`` u32 the phy id if the phy is C22
> + ===================================== ====== ==========================
> +
> +When ``ETHTOOL_A_PHY_UPSTREAM_TYPE`` is PHY_UPSTREAM_PHY, the PHY's parent is
> +another PHY. Information on the parent PHY will be set in the
> +``ETHTOOL_A_PHY_UPSTREAM_PHY`` nest, which has the following structure :
> +
> + =================================== ====== ==========================
> + ``ETHTOOL_A_PHY_UPSTREAM_INDEX`` u32 the PHY index of the upstream PHY
> + ``ETHTOOL_A_PHY_UPSTREAM_SFP_NAME`` string if this PHY is connected to it's
> + parent PHY through an SFP bus, the
> + name of this sfp bus
> + =================================== ====== ==========================

Why is this a nest?

> Request translation
> ===================

> +enum {
> + ETHTOOL_A_PHY_UNSPEC,
> + ETHTOOL_A_PHY_HEADER, /* nest - _A_HEADER_* */
> + ETHTOOL_A_PHY_INDEX, /* u32 */
> + ETHTOOL_A_PHY_DRVNAME, /* string */
> + ETHTOOL_A_PHY_NAME, /* string */
> + ETHTOOL_A_PHY_UPSTREAM_TYPE, /* u8 */

The Documentation say it's a u32 as it should be, AFAICT.
But code and some comments use u8.

> + ETHTOOL_A_PHY_UPSTREAM, /* nest - _A_PHY_UPSTREAM_* */
> + ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME, /* string */
> + ETHTOOL_A_PHY_ID, /* u32 */
> +
> + /* add new constants above here */
> + __ETHTOOL_A_PHY_CNT,
> + ETHTOOL_A_PHY_MAX = (__ETHTOOL_A_PHY_CNT - 1)
> +};

> +++ b/net/ethtool/phy.c
> @@ -0,0 +1,306 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright 2023 Bootlin
> + *
> + */

Do you really need 4 lines for the copyright? :)


> +/* Caller holds rtnl */
> +static ssize_t
> +ethnl_phy_reply_size(const struct ethnl_req_info *req_base,
> + struct netlink_ext_ack *extack)
> +{
> + struct phy_link_topology *topo;
> + struct phy_device_node *pdn;
> + struct phy_device *phydev;
> + unsigned long index;
> + size_t size;
> +
> + ASSERT_RTNL();
> +
> + topo = &req_base->dev->link_topo;
> +
> + size = nla_total_size(0);

no comment on this one?

> +
> + xa_for_each(&topo->phys, index, pdn) {

Why count all the PHYs, you only output one on doit, right?

> + phydev = pdn->phy;
> +
> + /* ETHTOOL_A_PHY_INDEX */
> + size += nla_total_size(sizeof(u32));
> +
> + /* ETHTOOL_A_DRVNAME */
> + size += nla_total_size(strlen(phydev->drv->name) + 1);
> +
> + /* ETHTOOL_A_NAME */
> + size += nla_total_size(strlen(dev_name(&phydev->mdio.dev)) + 1);
> +
> + /* ETHTOOL_A_PHY_UPSTREAM_TYPE */
> + size += nla_total_size(sizeof(u8));
> +
> + /* ETHTOOL_A_PHY_ID */
> + size += nla_total_size(sizeof(u32));
> +
> + if (phy_on_sfp(phydev)) {
> + const char *upstream_sfp_name = sfp_get_name(pdn->parent_sfp_bus);
> +
> + /* ETHTOOL_A_PHY_UPSTREAM_SFP_NAME */
> + if (upstream_sfp_name)
> + size += nla_total_size(strlen(upstream_sfp_name) + 1);
> +
> + /* ETHTOOL_A_PHY_UPSTREAM_INDEX */
> + size += nla_total_size(sizeof(u32));
> + }
> +
> + /* ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME */
> + if (phydev->sfp_bus) {
> + const char *sfp_name = sfp_get_name(phydev->sfp_bus);
> +
> + if (sfp_name)
> + size += nla_total_size(strlen(sfp_name) + 1);
> + }
> + }
> +
> + return size;
> +}

> +static int ethnl_phy_parse_request(struct ethnl_req_info *req_base,
> + struct nlattr **tb)
> +{
> + struct phy_link_topology *topo = &req_base->dev->link_topo;
> + struct phy_req_info *req_info = PHY_REQINFO(req_base);
> + struct phy_device_node *pdn;
> +
> + if (!req_base->phydev)
> + return 0;

The PHY INDEX should probably be a required attr, with
GENL_REQ_ATTR_CHECK()? Without phydev being specified
what's the point?

> + pdn = xa_load(&topo->phys, req_base->phydev->phyindex);
> + memcpy(&req_info->pdn, pdn, sizeof(*pdn));
> +
> + return 0;
> +}

> +int ethnl_phy_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
> +{
> + struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx;
> + struct net *net = sock_net(skb->sk);
> + unsigned long ifindex = 1;

This doesn't look right, if dump gets full you gotta pick up
when previous call left off.

> + struct net_device *dev;
> + int ret = 0;
> +
> + rtnl_lock();
> +
> + if (ctx->phy_req_info->base.dev) {
> + ret = ethnl_phy_dump_one_dev(skb, ctx->phy_req_info->base.dev, cb);
> + ethnl_parse_header_dev_put(&ctx->phy_req_info->base);
> + ctx->phy_req_info->base.dev = NULL;
> + } else {
> + for_each_netdev_dump(net, dev, ifindex) {
> + ret = ethnl_phy_dump_one_dev(skb, dev, cb);
> + if (ret)
> + break;
> + }
> + }
> + rtnl_unlock();
> +
> + if (ret == -EMSGSIZE && skb->len)
> + return skb->len;
> + return ret;
> +}
> +


2024-01-04 23:47:37

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next v5 00/13] Introduce PHY listing and link_topology tracking

On Wed, 3 Jan 2024 15:33:36 +0100 Maxime Chevallier wrote:
> I think this could help in reviewing the overall design and identifying
> any glaring issue with this.

The netlink handling looks a bit wobbly to me.
I commented best I could in the 20min I had to look at this code :(
I think it'd be best to revert, if you don't mind, because reviewing
incremental fixes will get even harder.

2024-01-04 23:50:59

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH net-next v5 00/13] Introduce PHY listing and link_topology tracking

On 1/4/24 15:47, Jakub Kicinski wrote:
> On Wed, 3 Jan 2024 15:33:36 +0100 Maxime Chevallier wrote:
>> I think this could help in reviewing the overall design and identifying
>> any glaring issue with this.
>
> The netlink handling looks a bit wobbly to me.
> I commented best I could in the 20min I had to look at this code :(
> I think it'd be best to revert, if you don't mind, because reviewing
> incremental fixes will get even harder.

+1
--
Florian


2024-01-05 00:56:20

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next v5 00/13] Introduce PHY listing and link_topology tracking

On Thu, 4 Jan 2024 15:50:40 -0800 Florian Fainelli wrote:
> > The netlink handling looks a bit wobbly to me.
> > I commented best I could in the 20min I had to look at this code :(
> > I think it'd be best to revert, if you don't mind, because reviewing
> > incremental fixes will get even harder.
>
> +1

Okay, let's say that the three of us - Florian, Russell
and I are a quorum? :) Reverted.

2024-01-05 02:21:32

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v5 01/13] net: phy: Introduce ethernet link topology representation

> Complete noob question but I thought PHYs get attached at ndo_open
> time for drivers, don't they?

Some do. Others get attached during probe. And Copper PHYs inside SFPs
get attached when hot-plugged.

Andrew

2024-01-05 08:01:16

by Maxime Chevallier

[permalink] [raw]
Subject: Re: [PATCH net-next v5 00/13] Introduce PHY listing and link_topology tracking

Hi Jakub,

On Thu, 4 Jan 2024 16:56:09 -0800
Jakub Kicinski <[email protected]> wrote:

> On Thu, 4 Jan 2024 15:50:40 -0800 Florian Fainelli wrote:
> > > The netlink handling looks a bit wobbly to me.
> > > I commented best I could in the 20min I had to look at this code :(
> > > I think it'd be best to revert, if you don't mind, because reviewing
> > > incremental fixes will get even harder.
> >
> > +1
>
> Okay, let's say that the three of us - Florian, Russell
> and I are a quorum? :) Reverted.

No worries, I'd rather make sure we get this right especially regarding
the API. Thanks for taking a look,

Maxime

2024-01-05 08:32:36

by Maxime Chevallier

[permalink] [raw]
Subject: Re: [PATCH net-next v5 01/13] net: phy: Introduce ethernet link topology representation

Hi Jakub,

On Thu, 4 Jan 2024 15:12:42 -0800
Jakub Kicinski <[email protected]> wrote:

> On Thu, 21 Dec 2023 19:00:34 +0100 Maxime Chevallier wrote:
> > @@ -2441,6 +2442,7 @@ struct net_device {
> > #if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
> > struct netprio_map __rcu *priomap;
> > #endif
> > + struct phy_link_topology link_topo;
>
> Perhaps others would disagree but can we make this a pointer instead?
> Only allocate it on demand, when first PHY gets attached?
> Both saves space and netdevice.h will no longer need to know the
> definition of the struct.
>
> Complete noob question but I thought PHYs get attached at ndo_open
> time for drivers, don't they? We shouldn't want to re-ID in that case.

I'll give it a try, it could be doable to try to keep the index for
_some_ PHYs. I had a first try at this in the very first iteration, but
that was lost when converting to xarray for the index management. Since
the phy_device keeps track of its own index, we can try to re-use it,
but I can see it becoming difficult if we destroy the topology when all
PHYs are detached (after ndo_close).

When re-creating the xarray at the next ndo_open, we would need to know
what was the previous next_index, and we can't know that until PHYs are
all re-attached.

For now it would work because in most cases, we only have one PHY
behind the MAC we can guarantee won't go away, but there are devices
out-there with 2 PHYs connected to the same MAC with a MUX in the
middle (that's one thing this series prepares for), and here it would
be more complex to re-allocate the topology while knowing what's the
next unused index. Hope that makes sense :)

To summarize, I don't think we can easily both dynamically
allocate/destroy the topology based only on the presence of at least
one PHY, and try to keep the PHY index the same for non-hot-pluggable
PHYs.

>
> > struct phy_device *phydev;
> > struct sfp_bus *sfp_bus;
> > struct lock_class_key *qdisc_tx_busylock;
>
> > @@ -10872,6 +10873,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
> > #ifdef CONFIG_NET_SCHED
> > hash_init(dev->qdisc_hash);
> > #endif
> > + phy_link_topo_init(&dev->link_topo);
> > +
> > dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM;
> > setup(dev);
> >
>
> I think you're missing a call to xa_destroy() somewhere, no?

Arg you're right... I was under the false assumption that because the
xarray wasn't dynamically allocated, we didn't need to perform any
cleanup, thanks for spotting this.

Maxime

2024-01-05 08:34:14

by Maxime Chevallier

[permalink] [raw]
Subject: Re: [PATCH net-next v5 05/13] net: ethtool: Allow passing a phy index for some commands

Hello Jakub,

On Thu, 4 Jan 2024 15:15:16 -0800
Jakub Kicinski <[email protected]> wrote:

> On Thu, 21 Dec 2023 19:00:38 +0100 Maxime Chevallier wrote:
> > @@ -20,6 +21,7 @@ const struct nla_policy ethnl_header_policy[] = {
> > .len = ALTIFNAMSIZ - 1 },
> > [ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32,
> > ETHTOOL_FLAGS_BASIC),
> > + [ETHTOOL_A_HEADER_PHY_INDEX] = NLA_POLICY_MIN(NLA_U32, 1),
> > };
> >
> > const struct nla_policy ethnl_header_policy_stats[] = {
> > @@ -28,6 +30,7 @@ const struct nla_policy ethnl_header_policy_stats[] = {
> > .len = ALTIFNAMSIZ - 1 },
> > [ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32,
> > ETHTOOL_FLAGS_STATS),
> > + [ETHTOOL_A_HEADER_PHY_INDEX] = NLA_POLICY_MIN(NLA_U32, 1),
> > };
>
> You should define (copy/paste) a new header policy and use it
> only for commands which actually make use of the PHY index.
> That's why we have separate copies already for stats.

Ah OK I didn't know that, I'll do this. Thanks !

Maxime

2024-01-05 08:47:45

by Maxime Chevallier

[permalink] [raw]
Subject: Re: [PATCH net-next v5 07/13] net: ethtool: Introduce a command to list PHYs on an interface

Hello Jakub,

Thanks a lot for the review on that part,

On Thu, 4 Jan 2024 15:34:01 -0800
Jakub Kicinski <[email protected]> wrote:

> On Thu, 21 Dec 2023 19:00:40 +0100 Maxime Chevallier wrote:
> > As we have the ability to track the PHYs connected to a net_device
> > through the link_topology, we can expose this list to userspace. This
> > allows userspace to use these identifiers for phy-specific commands and
> > take the decision of which PHY to target by knowing the link topology.
> >
> > Add PHY_GET and PHY_DUMP, which can be a filtered DUMP operation to list
> > devices on only one interface.
> >
> > Signed-off-by: Maxime Chevallier <[email protected]>
>
> > diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
> > index 3ca6c21e74af..97ff787a7dd8 100644
> > --- a/Documentation/networking/ethtool-netlink.rst
> > +++ b/Documentation/networking/ethtool-netlink.rst
> > @@ -2011,6 +2011,49 @@ The attributes are propagated to the driver through the following structure:
> > .. kernel-doc:: include/linux/ethtool.h
> > :identifiers: ethtool_mm_cfg
> >
> > +PHY_GET
> > +=======
> > +
> > +Retrieve information about a given Ethernet PHY sitting on the link. As there
> > +can be more than one PHY, the DUMP operation can be used to list the PHYs
> > +present on a given interface, by passing an interface index or name in
> > +the dump request
> > +
> > +Request contents:
> > +
> > + ==================================== ====== ==========================
> > + ``ETHTOOL_A_PHY_HEADER`` nested request header
> > + ==================================== ====== ==========================
> > +
> > +Kernel response contents:
> > +
> > + ===================================== ====== ==========================
> > + ``ETHTOOL_A_PHY_HEADER`` nested request header
> > + ``ETHTOOL_A_PHY_INDEX`` u32 the phy's unique index, that can
>
> The fact that lines are longer than the ===== markings doesn't generate
> warnings in htmldoc?

I did test the doc, but maybe I missed the warning. Since I'll need to
respin anyway, I'll clean this up :)

>
> > + be used for phy-specific requests
> > + ``ETHTOOL_A_PHY_DRVNAME`` string the phy driver name
> > + ``ETHTOOL_A_PHY_NAME`` string the phy device name
> > + ``ETHTOOL_A_PHY_UPSTREAM_TYPE`` u32 the type of device this phy is
> > + connected to
> > + ``ETHTOOL_A_PHY_UPSTREAM_PHY`` nested if the phy is connected to another
> > + phy, this nest contains info on
> > + that connection
> > + ``ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME`` string if the phy controls an sfp bus,
> > + the name of the sfp bus
>
> Is upstream / downstream clear to everyone / from the spec.
> I guess it's scoped to the netdev so upstream means "towards
> the netdev MAC"?

Good point, it's indeed scoped to the netdev, upstream means "towards
the MAC" and downstream "towards the link-partner". The upstream
terminology is used a little bit in the SFP code, but anyway should we
keep that terminology, I'll document it better both here and in the
dedicated documentation.

> > + ``ETHTOOL_A_PHY_ID`` u32 the phy id if the phy is C22
> > + ===================================== ====== ==========================
> > +
> > +When ``ETHTOOL_A_PHY_UPSTREAM_TYPE`` is PHY_UPSTREAM_PHY, the PHY's parent is
> > +another PHY. Information on the parent PHY will be set in the
> > +``ETHTOOL_A_PHY_UPSTREAM_PHY`` nest, which has the following structure :
> > +
> > + =================================== ====== ==========================
> > + ``ETHTOOL_A_PHY_UPSTREAM_INDEX`` u32 the PHY index of the upstream PHY
> > + ``ETHTOOL_A_PHY_UPSTREAM_SFP_NAME`` string if this PHY is connected to it's
> > + parent PHY through an SFP bus, the
> > + name of this sfp bus
> > + =================================== ====== ==========================
>
> Why is this a nest?

It was an attempt to structure the info, but I think we can do without,
as it only contains 2 fields.

> > Request translation
> > ===================
>
> > +enum {
> > + ETHTOOL_A_PHY_UNSPEC,
> > + ETHTOOL_A_PHY_HEADER, /* nest - _A_HEADER_* */
> > + ETHTOOL_A_PHY_INDEX, /* u32 */
> > + ETHTOOL_A_PHY_DRVNAME, /* string */
> > + ETHTOOL_A_PHY_NAME, /* string */
> > + ETHTOOL_A_PHY_UPSTREAM_TYPE, /* u8 */
>
> The Documentation say it's a u32 as it should be, AFAICT.
> But code and some comments use u8.

Ah my bad, Andrew did comment on that and I though I addressed the
inconsistency but there are some left apparently.

> > + ETHTOOL_A_PHY_UPSTREAM, /* nest - _A_PHY_UPSTREAM_* */
> > + ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME, /* string */
> > + ETHTOOL_A_PHY_ID, /* u32 */
> > +
> > + /* add new constants above here */
> > + __ETHTOOL_A_PHY_CNT,
> > + ETHTOOL_A_PHY_MAX = (__ETHTOOL_A_PHY_CNT - 1)
> > +};
>
> > +++ b/net/ethtool/phy.c
> > @@ -0,0 +1,306 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Copyright 2023 Bootlin
> > + *
> > + */
>
> Do you really need 4 lines for the copyright? :)

No I don't, I'll tidy this up :)

>
> > +/* Caller holds rtnl */
> > +static ssize_t
> > +ethnl_phy_reply_size(const struct ethnl_req_info *req_base,
> > + struct netlink_ext_ack *extack)
> > +{
> > + struct phy_link_topology *topo;
> > + struct phy_device_node *pdn;
> > + struct phy_device *phydev;
> > + unsigned long index;
> > + size_t size;
> > +
> > + ASSERT_RTNL();
> > +
> > + topo = &req_base->dev->link_topo;
> > +
> > + size = nla_total_size(0);
>
> no comment on this one?

Ah sorry I'll add it

> > +
> > + xa_for_each(&topo->phys, index, pdn) {
>
> Why count all the PHYs, you only output one on doit, right?

Uh ok good point, I'll fix it.

> > + phydev = pdn->phy;
> > +
> > + /* ETHTOOL_A_PHY_INDEX */
> > + size += nla_total_size(sizeof(u32));
> > +
> > + /* ETHTOOL_A_DRVNAME */
> > + size += nla_total_size(strlen(phydev->drv->name) + 1);
> > +
> > + /* ETHTOOL_A_NAME */
> > + size += nla_total_size(strlen(dev_name(&phydev->mdio.dev)) + 1);
> > +
> > + /* ETHTOOL_A_PHY_UPSTREAM_TYPE */
> > + size += nla_total_size(sizeof(u8));
> > +
> > + /* ETHTOOL_A_PHY_ID */
> > + size += nla_total_size(sizeof(u32));
> > +
> > + if (phy_on_sfp(phydev)) {
> > + const char *upstream_sfp_name = sfp_get_name(pdn->parent_sfp_bus);
> > +
> > + /* ETHTOOL_A_PHY_UPSTREAM_SFP_NAME */
> > + if (upstream_sfp_name)
> > + size += nla_total_size(strlen(upstream_sfp_name) + 1);
> > +
> > + /* ETHTOOL_A_PHY_UPSTREAM_INDEX */
> > + size += nla_total_size(sizeof(u32));
> > + }
> > +
> > + /* ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME */
> > + if (phydev->sfp_bus) {
> > + const char *sfp_name = sfp_get_name(phydev->sfp_bus);
> > +
> > + if (sfp_name)
> > + size += nla_total_size(strlen(sfp_name) + 1);
> > + }
> > + }
> > +
> > + return size;
> > +}
>
> > +static int ethnl_phy_parse_request(struct ethnl_req_info *req_base,
> > + struct nlattr **tb)
> > +{
> > + struct phy_link_topology *topo = &req_base->dev->link_topo;
> > + struct phy_req_info *req_info = PHY_REQINFO(req_base);
> > + struct phy_device_node *pdn;
> > +
> > + if (!req_base->phydev)
> > + return 0;
>
> The PHY INDEX should probably be a required attr, with
> GENL_REQ_ATTR_CHECK()? Without phydev being specified
> what's the point?

We can still have a phydev without passing a PHY INDEX, this would
report information on the netdev->phydev device, that can be helpful
for users to know which PHY is targeted by commands such as "ethtool
--cable-test eth0" when no PHY index is passed

> > + pdn = xa_load(&topo->phys, req_base->phydev->phyindex);
> > + memcpy(&req_info->pdn, pdn, sizeof(*pdn));
> > +
> > + return 0;
> > +}
>
> > +int ethnl_phy_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
> > +{
> > + struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx;
> > + struct net *net = sock_net(skb->sk);
> > + unsigned long ifindex = 1;
>
> This doesn't look right, if dump gets full you gotta pick up
> when previous call left off.

I wasn't aware that this was the expected DUMP behaviour. So I should
keep track of the last dev and last phy_index dumped in the dump_ctx I
guess ? I'm not sure how I'm going to test this though, I only have
devices with at most 2 PHYs :(

> > + struct net_device *dev;
> > + int ret = 0;
> > +
> > + rtnl_lock();
> > +
> > + if (ctx->phy_req_info->base.dev) {
> > + ret = ethnl_phy_dump_one_dev(skb, ctx->phy_req_info->base.dev, cb);
> > + ethnl_parse_header_dev_put(&ctx->phy_req_info->base);
> > + ctx->phy_req_info->base.dev = NULL;
> > + } else {
> > + for_each_netdev_dump(net, dev, ifindex) {
> > + ret = ethnl_phy_dump_one_dev(skb, dev, cb);
> > + if (ret)
> > + break;
> > + }
> > + }
> > + rtnl_unlock();
> > +
> > + if (ret == -EMSGSIZE && skb->len)
> > + return skb->len;
> > + return ret;
> > +}
> > +
>

Thanks a lot for the review, the netlink part was definitely the hard
part for me.

Maxime

2024-01-05 13:17:35

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v5 07/13] net: ethtool: Introduce a command to list PHYs on an interface

> > > +int ethnl_phy_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
> > > +{
> > > + struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx;
> > > + struct net *net = sock_net(skb->sk);
> > > + unsigned long ifindex = 1;
> >
> > This doesn't look right, if dump gets full you gotta pick up
> > when previous call left off.
>
> I wasn't aware that this was the expected DUMP behaviour. So I should
> keep track of the last dev and last phy_index dumped in the dump_ctx I
> guess ? I'm not sure how I'm going to test this though, I only have
> devices with at most 2 PHYs :(

At a guess....

You are supposed to dump until you are out of space in the buffer. You
then return what you have, and expect another call so you can continue
with the rest.

Rather than fill the buffer, just hack the code to only put in a
single PHY, and then return with the same condition of a full
buffer. Hopefully you should get a second call, and you can then test
your logic for picking up from where you left off.

Another option might be to add PHY support to netdevsim. Add a debugfs
interface to allow you to create arbitrary PHY topologies? You can
then even add a test script.

Andrew

2024-01-05 15:34:48

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next v5 01/13] net: phy: Introduce ethernet link topology representation

On Fri, 5 Jan 2024 10:29:18 +0100 Maxime Chevallier wrote:
> I'll give it a try, it could be doable to try to keep the index for
> _some_ PHYs. I had a first try at this in the very first iteration, but
> that was lost when converting to xarray for the index management. Since
> the phy_device keeps track of its own index, we can try to re-use it,
> but I can see it becoming difficult if we destroy the topology when all
> PHYs are detached (after ndo_close).

What I was thinking is if phy already has phyindex, and that index is
unused in the xarray of netdev in question - try to use that phyindex
instead of allocating a new one?
IDK if phys can move between netdevs, or get freed/recreated
automatically - otherwise it should work? (If user unbinds and rebinds
the PHY driver for instance that's their fault, we can renumber).

2024-01-05 15:40:09

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next v5 07/13] net: ethtool: Introduce a command to list PHYs on an interface

On Fri, 5 Jan 2024 10:43:11 +0100 Maxime Chevallier wrote:
> > > +static int ethnl_phy_parse_request(struct ethnl_req_info *req_base,
> > > + struct nlattr **tb)
> > > +{
> > > + struct phy_link_topology *topo = &req_base->dev->link_topo;
> > > + struct phy_req_info *req_info = PHY_REQINFO(req_base);
> > > + struct phy_device_node *pdn;
> > > +
> > > + if (!req_base->phydev)
> > > + return 0;
> >
> > The PHY INDEX should probably be a required attr, with
> > GENL_REQ_ATTR_CHECK()? Without phydev being specified
> > what's the point?
>
> We can still have a phydev without passing a PHY INDEX, this would
> report information on the netdev->phydev device, that can be helpful
> for users to know which PHY is targeted by commands such as "ethtool
> --cable-test eth0" when no PHY index is passed

But req_base->phydev will be netdev->phydev if user didn't specify
the index. Are you saying this is for commands which can operate
on netdevs as well as on PHYs (e.g. "integrated NICs" which don't
user phylib?)

2024-01-24 13:50:58

by Maxime Chevallier

[permalink] [raw]
Subject: Re: [PATCH net-next v5 07/13] net: ethtool: Introduce a command to list PHYs on an interface

Hello Andrew,

On Fri, 5 Jan 2024 14:17:10 +0100
Andrew Lunn <[email protected]> wrote:

> > > > +int ethnl_phy_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
> > > > +{
> > > > + struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx;
> > > > + struct net *net = sock_net(skb->sk);
> > > > + unsigned long ifindex = 1;
> > >
> > > This doesn't look right, if dump gets full you gotta pick up
> > > when previous call left off.
> >
> > I wasn't aware that this was the expected DUMP behaviour. So I should
> > keep track of the last dev and last phy_index dumped in the dump_ctx I
> > guess ? I'm not sure how I'm going to test this though, I only have
> > devices with at most 2 PHYs :(
>
> At a guess....
>
> You are supposed to dump until you are out of space in the buffer. You
> then return what you have, and expect another call so you can continue
> with the rest.
>
> Rather than fill the buffer, just hack the code to only put in a
> single PHY, and then return with the same condition of a full
> buffer. Hopefully you should get a second call, and you can then test
> your logic for picking up from where you left off.
>
> Another option might be to add PHY support to netdevsim. Add a debugfs
> interface to allow you to create arbitrary PHY topologies? You can
> then even add a test script.

Sorry for the delayed answer, I just took a few hours to give it a try,
and I was able to spin some very basic PHY support for the netdevsim,
allowing to attach arbitrary instances of fixed_phy devices. I can
therefore use that as a mean of testing the dump operation, I'll try to
include that in the next iteration, that should pave the way for some
testability of more PHY stuff hopefully.

Thanks for the suggestion,

Maxime

2024-01-24 16:55:44

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v5 07/13] net: ethtool: Introduce a command to list PHYs on an interface

> > Another option might be to add PHY support to netdevsim. Add a debugfs
> > interface to allow you to create arbitrary PHY topologies? You can
> > then even add a test script.
>
> Sorry for the delayed answer, I just took a few hours to give it a try,
> and I was able to spin some very basic PHY support for the netdevsim,
> allowing to attach arbitrary instances of fixed_phy devices. I can
> therefore use that as a mean of testing the dump operation, I'll try to
> include that in the next iteration, that should pave the way for some
> testability of more PHY stuff hopefully.

Great that you looked at this.

FYI: Jakub would like to see changes to netdevsim accompanied with
self tests making you of the features you add. There is also now a
build bot running the self tests against net-next, so these tests
should get run quite frequently.

Andrew


2024-01-25 08:37:23

by Maxime Chevallier

[permalink] [raw]
Subject: Re: [PATCH net-next v5 07/13] net: ethtool: Introduce a command to list PHYs on an interface

On Wed, 24 Jan 2024 17:54:53 +0100
Andrew Lunn <[email protected]> wrote:

> > > Another option might be to add PHY support to netdevsim. Add a debugfs
> > > interface to allow you to create arbitrary PHY topologies? You can
> > > then even add a test script.
> >
> > Sorry for the delayed answer, I just took a few hours to give it a try,
> > and I was able to spin some very basic PHY support for the netdevsim,
> > allowing to attach arbitrary instances of fixed_phy devices. I can
> > therefore use that as a mean of testing the dump operation, I'll try to
> > include that in the next iteration, that should pave the way for some
> > testability of more PHY stuff hopefully.
>
> Great that you looked at this.
>
> FYI: Jakub would like to see changes to netdevsim accompanied with
> self tests making you of the features you add. There is also now a
> build bot running the self tests against net-next, so these tests
> should get run quite frequently.

No problem, I'll include these as well.

I do face a problem with fixed_phy though now that I've played around
with it. As fixed_phys share the same global MDIO bus, what can happen
is that netdevsim-registered PHYs can starve the dummy MDIO bus by
exhausting all 32 mdio addresses, preventing real interfaces from
getting their own fixed-phy instance.

I'll probably register a dedicated mdio bus per netdevsim (or even
per-phy, so that we can imagine controling the returned register
values), let's see how it goes.

Thanks,

Maxime

2024-01-25 17:32:55

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v5 07/13] net: ethtool: Introduce a command to list PHYs on an interface

> I do face a problem with fixed_phy though now that I've played around
> with it. As fixed_phys share the same global MDIO bus, what can happen
> is that netdevsim-registered PHYs can starve the dummy MDIO bus by
> exhausting all 32 mdio addresses, preventing real interfaces from
> getting their own fixed-phy instance.
>
> I'll probably register a dedicated mdio bus per netdevsim (or even
> per-phy, so that we can imagine controling the returned register
> values), let's see how it goes.

I can see it being a problem, but how theoretical is it?

Anything using phylink does not need a fixed-link device, its just MAC
drivers making use of phylib. Its also only typically used with MACs
connected to switches, and you tend not to have too many of them on a
machine. And lastly, netdevsim is only really used for testing, and i
guess most tests run either on a desktop or server like machine which
does not have switches, probably does not even make use phylib, or the
tests are run in a VM which does not even have any PHYs, fixed or not.

So i'm wondering how much effort should be put into this, or should
the time be spent on other things?

Andrew