Skip to content

Commit

Permalink
Merge branch 'phy-listing-link_topology-tracking'
Browse files Browse the repository at this point in the history
Maxime Chevallier says:

====================
Introduce PHY listing and link_topology tracking

This is V11 for the link topology addition, allowing to track all PHYs
that are linked to netdevices.

This V11 addresses the various netlink-related issues that were raised
by Jakub, and fixes some typos in the documentation.

As a remainder, here's what the PHY listings would look like :
 - eth0 has a 88x3310 acting as media converter, and an SFP module with
   an embedded 88e1111 PHY
 - eth2 has a 88e1510 PHY

PHY for eth0:
PHY index: 1
Driver name: mv88x3310
PHY device name: f212a600.mdio-mii:00
Downstream SFP bus name: sfp-eth0
PHY id: 0
Upstream type: MAC

PHY for eth0:
PHY index: 2
Driver name: Marvell 88E1111
PHY device name: i2c:sfp-eth0:16
PHY id: 21040322
Upstream type: PHY
Upstream PHY index: 1
Upstream SFP name: sfp-eth0

PHY for eth2:
PHY index: 1
Driver name: Marvell 88E1510
PHY device name: f212a20.mdio-mii:00
PHY id: 21040593
Upstream type: MAC

Ethtool patches : https://github.com/minimaxwell/ethtool/tree/link-topo-v6
====================

Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
davem330 committed Apr 6, 2024
2 parents d133ef1 + 841942b commit 267e317
Show file tree
Hide file tree
Showing 22 changed files with 385 additions and 8 deletions.
7 changes: 7 additions & 0 deletions Documentation/networking/ethtool-netlink.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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`` identifies the Ethernet PHY the message relates to.
As there are numerous commands that are related to PHY configuration, and because
there may be 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.

Bit sets
========
Expand Down
2 changes: 2 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -8015,6 +8015,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
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/phy/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/phy/marvell-88x2222.c
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,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)
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/phy/marvell.c
Original file line number Diff line number Diff line change
Expand Up @@ -3550,6 +3550,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)
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/phy/marvell10g.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down
55 changes: 55 additions & 0 deletions drivers/net/phy/phy_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -276,6 +277,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);

Expand Down Expand Up @@ -1369,6 +1378,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
Expand Down Expand Up @@ -1511,6 +1560,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
Expand Down Expand Up @@ -1938,6 +1992,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;

Expand Down
105 changes: 105 additions & 0 deletions drivers/net/phy/phy_link_topology.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// 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>

struct phy_link_topology *phy_link_topo_create(struct net_device *dev)
{
struct phy_link_topology *topo;

topo = kzalloc(sizeof(*topo), GFP_KERNEL);
if (!topo)
return ERR_PTR(-ENOMEM);

xa_init_flags(&topo->phys, XA_FLAGS_ALLOC1);
topo->next_phy_index = 1;

return topo;
}

void phy_link_topo_destroy(struct phy_link_topology *topo)
{
if (!topo)
return;

xa_destroy(&topo->phys);
kfree(topo);
}

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;

/* Attempt to re-use a previously allocated phy_index */
if (phy->phyindex) {
ret = xa_insert(&topo->phys, phy->phyindex, pdn, GFP_KERNEL);

/* Errors could be either -ENOMEM or -EBUSY. If the phy has an
* index, and there's another entry at the same index, this is
* unexpected and we still error-out
*/
if (ret)
goto err;
return 0;
}

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);

/* We delete the PHY from the topology, however we don't re-set the
* phy->phyindex field. If the PHY isn't gone, we can re-assign it the
* same index next time it's added back to the topology
*/

kfree(pdn);
}
EXPORT_SYMBOL_GPL(phy_link_topo_del_phy);
3 changes: 2 additions & 1 deletion drivers/net/phy/phylink.c
Original file line number Diff line number Diff line change
Expand Up @@ -3408,7 +3408,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);
}
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/phy/qcom/at803x.c
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,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)
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/phy/qcom/qca807x.c
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,8 @@ static const struct sfp_upstream_ops qca807x_sfp_ops = {
.detach = phy_sfp_detach,
.module_insert = qca807x_sfp_insert,
.module_remove = qca807x_sfp_remove,
.connect_phy = phy_sfp_connect_phy,
.disconnect_phy = phy_sfp_disconnect_phy,
};

static int qca807x_probe(struct phy_device *phydev)
Expand Down
15 changes: 13 additions & 2 deletions drivers/net/phy/sfp-bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
4 changes: 3 additions & 1 deletion include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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;
Expand Down Expand Up @@ -1974,6 +1974,7 @@ enum netdev_reg_state {
* @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.
Expand Down Expand Up @@ -2364,6 +2365,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;
Expand Down
6 changes: 6 additions & 0 deletions include/linux/phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,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.
Expand Down Expand Up @@ -650,6 +653,7 @@ struct phy_device {

struct device_link *devlink;

u32 phyindex;
u32 phy_id;

struct phy_c45_device_ids c45_ids;
Expand Down Expand Up @@ -1754,6 +1758,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,
Expand Down
Loading

0 comments on commit 267e317

Please sign in to comment.