drivers/net/phy/phy_link_topology.c

Source file repositories/reference/linux-study-clean/drivers/net/phy/phy_link_topology.c

File Facts

System
Linux kernel
Corpus path
drivers/net/phy/phy_link_topology.c
Extension
.c
Size
2668 bytes
Lines
116
Domain
Driver Families
Bucket
drivers/net
Inferred role
Driver Families: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

// 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<maxime.chevallier@bootlin.com>
 */

#include <linux/phy_link_topology.h>
#include <linux/phy.h>
#include <linux/rtnetlink.h>
#include <linux/xarray.h>
#include <net/netdev_lock.h>

static int netdev_alloc_phy_link_topology(struct net_device *dev)
{
	struct phy_link_topology *topo;

	topo = kzalloc_obj(*topo);
	if (!topo)
		return -ENOMEM;

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

	dev->link_topo = topo;

	return 0;
}

int phy_link_topo_add_phy(struct net_device *dev,
			  struct phy_device *phy,
			  enum phy_upstream upt, void *upstream)
{
	struct phy_link_topology *topo = dev->link_topo;
	struct phy_device_node *pdn;
	int ret;

	/* ethtool ops may run without rtnl_lock, and rtnl_lock is what
	 * currently protects the PHY topology. No driver currently mixes
	 * the two, flag if someone tries. See also:
	 *  - ethnl_req_get_phydev()
	 *  - phy_detach()
	 */
	if (WARN_ON_ONCE(netdev_need_ops_lock(dev)))
		return -EOPNOTSUPP;

	if (!topo) {
		ret = netdev_alloc_phy_link_topology(dev);
		if (ret)
			return ret;

		topo = dev->link_topo;
	}

	pdn = kzalloc_obj(*pdn);
	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);
	else
		ret = xa_alloc_cyclic(&topo->phys, &phy->phyindex, pdn,
				      xa_limit_32b, &topo->next_phy_index,
				      GFP_KERNEL);

	if (ret < 0)
		goto err;

	return 0;

Annotation

Implementation Notes