From nobody Fri Oct 2 12:24:37 2026 Received: from smtpout-02.galae.net (smtpout-02.galae.net [185.246.84.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9623D231A23; Sun, 27 Sep 2026 13:36:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.84.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516203; cv=none; b=ZQoUGvt2hiT8nb07r3e7BAtsfVDmel1/7BUAsAntqHkhtWtA+Lvfz7Mc4a7Yuxin7DF2rLOp4qi+bi72hc31s8pV1quEqm0AYonn032FEmQcnPbf5fZy+u1OP2g+gSIrKTXRJAGIZtYcd8MPM1/iaPEvG3QAiiOyh6O68sljO3U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516203; c=relaxed/simple; bh=yOD1Vqn0ylp/91aDdIHUy1yhbQ2hiv2D3x0BHI2eM5U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LBN2nFk4fagLPFRvQItlfTIC0KKU/lfAZ9THUNJccTMWq6dTp7T9OfbE5SCdOBeTFfAlJFoMhoW4Q67h6PyerCs1TeWDBbuegtvUQrTkz73vE4e5ufZRt+dQLq8K/BD7h7GXPNWwT8+fKsFWooJlRad+8R7WiCNmnFjQ84TEdoo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=S7xCwRrZ; arc=none smtp.client-ip=185.246.84.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="S7xCwRrZ" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 2060B1A0FF7; Sun, 27 Sep 2026 13:36:39 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id E6CFA604FF; Sun, 27 Sep 2026 13:36:38 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 810EA102F1E7B; Sun, 27 Sep 2026 15:36:33 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1790516197; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=udrToWyNeWC9/MRCzapUPmT7Lrhb0mIKn3UX+ueHobY=; b=S7xCwRrZXrlqrHkzfHf0m0FONZapgcwt1lYvYmla/FDa1VcNoEEYqDv6JgMJTWHEQMiUnu b3NvcAls6vg5cyki1r8cBXuQyaskiy7JutVnJti4+vb6VmFKgRxn+Pjtwk7zeQWwow/yii 7WLnTnteNeH0Ks62ZSIcHIzkK8C6xadH3ghvw/GuwbSdeNGRMl4BS+ssVUF7DCTf8zf1ct Y2dGvSiSSFG+LTNSWTDCOxUWXfcXLbp05UR6+FsOpHQGPAJSH3yPNq96IFSMOfLCsPWNLI 8b661XCl3A2PttVVyZwf5TbJBKkNjqRtmZ7dIoVoclemhfxZitnsA+1Cxb27jw== From: Maxime Chevallier To: davem@davemloft.net, Andrew Lunn , Jakub Kicinski , Eric Dumazet , Paolo Abeni , Russell King , Heiner Kallweit Cc: Maxime Chevallier , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, thomas.petazzoni@bootlin.com, Christophe Leroy , Herve Codina , Florian Fainelli , Vladimir Oltean , =?UTF-8?q?K=C3=B6ry=20Maincent?= , =?UTF-8?q?Marek=20Beh=C3=BAn?= , Oleksij Rempel , =?UTF-8?q?Nicol=C3=B2=20Veronese?= , Simon Horman , mwojtas@chromium.org, Romain Gantois , Daniel Golle , Dimitri Fedrau , Frank Wunderlich , Pietro Ameruoso , Aleksei Sviridkin Subject: [PATCH net-next v18 01/10] net: phy: phy_link_topology: Add a helper for opportunistic alloc Date: Sun, 27 Sep 2026 15:36:10 +0200 Message-ID: <20260927133619.955236-2-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260927133619.955236-1-maxime.chevallier@bootlin.com> References: <20260927133619.955236-1-maxime.chevallier@bootlin.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" The phy_link_topology structure stores information about the PHY-related components connected to a net_device. It is opportunistically allocated, when we add the first item to the topology, as this is not relevant for all kinds of net_devices. In preparation for the addition of phy_port tracking in the topology, let's make a dedicated helper for that allocation sequence. Reviewed-by: Andrew Lunn Tested-by: Aleksei Sviridkin Signed-off-by: Maxime Chevallier Reviewed-by: Christophe Leroy (CS GROUP) Tested-by: Christophe Leroy (CS GROUP) --- drivers/net/phy/phy_link_topology.c | 40 +++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/net/phy/phy_link_topology.c b/drivers/net/phy/phy_link= _topology.c index 4134de7ae313..0462283c8020 100644 --- a/drivers/net/phy/phy_link_topology.c +++ b/drivers/net/phy/phy_link_topology.c @@ -28,11 +28,39 @@ static int netdev_alloc_phy_link_topology(struct net_de= vice *dev) return 0; } =20 +static struct phy_link_topology *phy_link_topo_get_or_alloc(struct net_dev= ice *dev) +{ + int ret; + + if (dev->link_topo) + return dev->link_topo; + + /* The topology is allocated the first time we add an object to it. + * It is freed alongside the netdev. It can be called on multiple + * contexts: + * - It can be called from .probe() : No rtnl, no netdev_lock + * - .ndo_open() : rtnl and possibly netdev_lock + * - SFP state machine : rtnl held or not + * + * However, we can't really have races : + * - If we have a PHY, phy_link_topo_add_phy() will always run first + * and trigger the alloc. Only then the ports can be added through + * phylib or sfp. + * - If we don't, the SFP port for the cage is registered first, and + * only then other ports/PHYs can be registered. + */ + ret =3D netdev_alloc_phy_link_topology(dev); + if (ret) + return ERR_PTR(ret); + + return dev->link_topo; +} + 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 =3D dev->link_topo; + struct phy_link_topology *topo; struct phy_device_node *pdn; int ret; =20 @@ -45,13 +73,9 @@ int phy_link_topo_add_phy(struct net_device *dev, if (WARN_ON_ONCE(netdev_need_ops_lock(dev))) return -EOPNOTSUPP; =20 - if (!topo) { - ret =3D netdev_alloc_phy_link_topology(dev); - if (ret) - return ret; - - topo =3D dev->link_topo; - } + topo =3D phy_link_topo_get_or_alloc(dev); + if (IS_ERR(topo)) + return PTR_ERR(topo); =20 pdn =3D kzalloc_obj(*pdn); if (!pdn) --=20 2.55.0 From nobody Fri Oct 2 12:24:37 2026 Received: from smtpout-02.galae.net (smtpout-02.galae.net [185.246.84.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5904B3F1AA1; Sun, 27 Sep 2026 13:36:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.84.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516211; cv=none; b=PjODq5/neQB6AisH45oxcBqm+lqu24sQX6c8J1oOyirhK0iEZWhlOGYk0wWicTi1Otwc6TZXZbCLyaxqD17f3Mw73Jayeyyd9KiQCcM1D1r4V/XrhdguT1qSU+CpeL75fzsvzOYi+kVW+wBdYZoFbwfFbxGWoWQeEdz8HRpkq4g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516211; c=relaxed/simple; bh=eHRVqTtokUQK7kbJiquuspIg2+fq/oZxhsDfHEf231E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Nc1z1HCyf4o/+ElsN37KwpkvYctSYFr1pg3h/JF5JoBE6B+tuskf30VYmdAAjs7X842zflJaTJ8aSY1pkRQ67U2iMSoQ/UrW67qaRseLdV3c3Iuwstz+gZLaTjR0y8w129FXYjyinJPqCH1y7Sz5blcGmY/1R1WTncHmT9qynpo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=AxaMuc0l; arc=none smtp.client-ip=185.246.84.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="AxaMuc0l" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id E850F1A0FDB; Sun, 27 Sep 2026 13:36:43 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id B413F604FF; Sun, 27 Sep 2026 13:36:43 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id AAF3B102F1E66; Sun, 27 Sep 2026 15:36:37 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1790516201; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=mr/lduEbMS8Z7Za/wylkmnGELqRb6bhbkGjrCKopX7E=; b=AxaMuc0lmB8xZZvWg6m9YwZJd3C4youdsB8WntWr3Z8FqtxgiwwFx0hVsfrmMsof6zvRCw W8a6N6M0jl/TNXbpPnvF06PR4qPCMjZl1ZwkNvsfqCyjZ9bDfzYKaSvbXYWXpdM5IqjuJz wa112hlVcvqk0SCC4DKB+jX1044cEy+Q5uMH5/fvfTgLXA6On16lXush8G6r3d14zv/LhA QQlbxkiymqIWRxwjsk09CwLaqypCpk2Nnn1fHnvk6W8nKo0rWg5qHfpgLWc6TXd64nXKAe 1hOjMiP4AIlQkmKQRFEr7r5nnjqZAs5osN33Sty6pUuXPCNlNkW6l/auaN4MnA== From: Maxime Chevallier To: davem@davemloft.net, Andrew Lunn , Jakub Kicinski , Eric Dumazet , Paolo Abeni , Russell King , Heiner Kallweit Cc: Maxime Chevallier , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, thomas.petazzoni@bootlin.com, Christophe Leroy , Herve Codina , Florian Fainelli , Vladimir Oltean , =?UTF-8?q?K=C3=B6ry=20Maincent?= , =?UTF-8?q?Marek=20Beh=C3=BAn?= , Oleksij Rempel , =?UTF-8?q?Nicol=C3=B2=20Veronese?= , Simon Horman , mwojtas@chromium.org, Romain Gantois , Daniel Golle , Dimitri Fedrau , Frank Wunderlich , Pietro Ameruoso , Aleksei Sviridkin Subject: [PATCH net-next v18 02/10] net: phy: phy_link_topology: Track ports in phy_link_topology Date: Sun, 27 Sep 2026 15:36:11 +0200 Message-ID: <20260927133619.955236-3-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260927133619.955236-1-maxime.chevallier@bootlin.com> References: <20260927133619.955236-1-maxime.chevallier@bootlin.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" phy_port is aimed at representing the various physical interfaces of a net_device. They can be controlled by various components in the link, such as the Ethernet PHY, the Ethernet MAC, and SFP module, etc. Let's therefore make so we keep track of all the ports connected to a netdev in phy_link_topology. The only ports added for now are phy-driven ports. Reviewed-by: Andrew Lunn Tested-by: Aleksei Sviridkin Signed-off-by: Maxime Chevallier Reviewed-by: Christophe Leroy (CS GROUP) Tested-by: Christophe Leroy (CS GROUP) --- drivers/net/phy/phy_device.c | 3 ++ drivers/net/phy/phy_link_topology.c | 53 +++++++++++++++++++++++++++++ include/linux/phy_link_topology.h | 18 ++++++++++ include/linux/phy_port.h | 2 ++ net/core/dev.c | 1 + 5 files changed, 77 insertions(+) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 5b13a74e2fa9..c4d2c03b8282 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1580,6 +1580,9 @@ static void phy_del_port(struct phy_device *phydev, s= truct phy_port *port) =20 list_del(&port->head); =20 + if (phydev->attached_dev) + phy_link_topo_del_port(phydev->attached_dev, port); + phydev->n_ports--; } =20 diff --git a/drivers/net/phy/phy_link_topology.c b/drivers/net/phy/phy_link= _topology.c index 0462283c8020..28c5bf3a8849 100644 --- a/drivers/net/phy/phy_link_topology.c +++ b/drivers/net/phy/phy_link_topology.c @@ -7,6 +7,7 @@ */ =20 #include +#include #include #include #include @@ -23,6 +24,9 @@ static int netdev_alloc_phy_link_topology(struct net_devi= ce *dev) xa_init_flags(&topo->phys, XA_FLAGS_ALLOC1); topo->next_phy_index =3D 1; =20 + xa_init_flags(&topo->ports, XA_FLAGS_ALLOC1); + topo->next_port_index =3D 1; + dev->link_topo =3D topo; =20 return 0; @@ -56,12 +60,45 @@ static struct phy_link_topology *phy_link_topo_get_or_a= lloc(struct net_device *d return dev->link_topo; } =20 +int phy_link_topo_add_port(struct net_device *dev, struct phy_port *port) +{ + struct phy_link_topology *topo; + int ret; + + topo =3D phy_link_topo_get_or_alloc(dev); + if (IS_ERR(topo)) + return PTR_ERR(topo); + + /* Attempt to re-use a previously allocated port_id */ + if (port->id) + ret =3D xa_insert(&topo->ports, port->id, port, GFP_KERNEL); + else + ret =3D xa_alloc_cyclic(&topo->ports, &port->id, port, + xa_limit_32b, &topo->next_port_index, + GFP_KERNEL); + + return ret < 0 ? ret : 0; +} +EXPORT_SYMBOL_GPL(phy_link_topo_add_port); + +void phy_link_topo_del_port(struct net_device *dev, struct phy_port *port) +{ + struct phy_link_topology *topo =3D dev->link_topo; + + if (!topo) + return; + + xa_erase(&topo->ports, port->id); +} +EXPORT_SYMBOL_GPL(phy_link_topo_del_port); + 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; struct phy_device_node *pdn; + struct phy_port *port; int ret; =20 /* ethtool ops may run without rtnl_lock, and rtnl_lock is what @@ -110,8 +147,20 @@ int phy_link_topo_add_phy(struct net_device *dev, if (ret < 0) goto err; =20 + /* Add all the PHY's ports to the topology */ + list_for_each_entry(port, &phy->ports, head) { + ret =3D phy_link_topo_add_port(dev, port); + if (ret) + goto del_ports; + } + return 0; =20 +del_ports: + list_for_each_entry_continue_reverse(port, &phy->ports, head) + phy_link_topo_del_port(dev, port); + + xa_erase(&topo->phys, phy->phyindex); err: kfree(pdn); return ret; @@ -123,10 +172,14 @@ void phy_link_topo_del_phy(struct net_device *dev, { struct phy_link_topology *topo =3D dev->link_topo; struct phy_device_node *pdn; + struct phy_port *port; =20 if (!topo) return; =20 + list_for_each_entry(port, &phy->ports, head) + phy_link_topo_del_port(dev, port); + pdn =3D xa_erase(&topo->phys, phy->phyindex); =20 /* We delete the PHY from the topology, however we don't re-set the diff --git a/include/linux/phy_link_topology.h b/include/linux/phy_link_top= ology.h index 95575f68d5bc..296ee514ba46 100644 --- a/include/linux/phy_link_topology.h +++ b/include/linux/phy_link_topology.h @@ -16,11 +16,15 @@ =20 struct xarray; struct phy_device; +struct phy_port; struct sfp_bus; =20 struct phy_link_topology { struct xarray phys; u32 next_phy_index; + + struct xarray ports; + u32 next_port_index; }; =20 struct phy_device_node { @@ -48,6 +52,9 @@ int phy_link_topo_add_phy(struct net_device *dev, =20 void phy_link_topo_del_phy(struct net_device *dev, struct phy_device *phy); =20 +int phy_link_topo_add_port(struct net_device *dev, struct phy_port *port); +void phy_link_topo_del_port(struct net_device *dev, struct phy_port *port); + static inline struct phy_device * phy_link_topo_get_phy(struct net_device *dev, u32 phyindex) { @@ -77,6 +84,17 @@ static inline void phy_link_topo_del_phy(struct net_devi= ce *dev, { } =20 +static inline int phy_link_topo_add_port(struct net_device *dev, + struct phy_port *port) +{ + return 0; +} + +static inline void phy_link_topo_del_port(struct net_device *dev, + struct phy_port *port) +{ +} + static inline struct phy_device * phy_link_topo_get_phy(struct net_device *dev, u32 phyindex) { diff --git a/include/linux/phy_port.h b/include/linux/phy_port.h index 0ef0f5ce4709..4e2a3fdd2f2e 100644 --- a/include/linux/phy_port.h +++ b/include/linux/phy_port.h @@ -36,6 +36,7 @@ struct phy_port_ops { /** * struct phy_port - A representation of a network device physical interfa= ce * + * @id: Unique identifier for the port within the topology * @head: Used by the port's parent to list ports * @parent_type: The type of device this port is directly connected to * @phy: If the parent is PHY_PORT_PHYDEV, the PHY controlling that port @@ -52,6 +53,7 @@ struct phy_port_ops { * @is_sfp: Indicates if this port drives an SFP cage. */ struct phy_port { + u32 id; struct list_head head; enum phy_port_parent parent_type; union { diff --git a/net/core/dev.c b/net/core/dev.c index f660fccfc0db..97caa6846333 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -11378,6 +11378,7 @@ static void netdev_free_phy_link_topology(struct ne= t_device *dev) =20 if (IS_ENABLED(CONFIG_PHYLIB) && topo) { xa_destroy(&topo->phys); + xa_destroy(&topo->ports); kfree(topo); dev->link_topo =3D NULL; } --=20 2.55.0 From nobody Fri Oct 2 12:24:37 2026 Received: from smtpout-03.galae.net (smtpout-03.galae.net [185.246.85.4]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7F97B3C13FB; Sun, 27 Sep 2026 13:36:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.85.4 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516212; cv=none; b=sB564Wq6wWikQ7ByPfZsXTDCL8s8NB2JYIOZzEb7kdMXiNoeF4lKUlS13A8RGWkNUZ01BZcaGiivP1+pC9ZWxxw6U2PH6uBiQq8jmwShwxVeOzOzZJDm0ZK19sR2oECT3Fb10Vw6K2zv0ywPws9YWvzQY9AlD+U5oBvY9jSRUbE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516212; c=relaxed/simple; bh=/2kxVKGWTH+s2LZeNa1lQrqi+alHqnowGjRctArlKvM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ai33dtI5XPHLQjPN29eSrnl7eoltlPpF+xzYIyYLQmuwsxB5r13zFGUqSPjPUiLRvXT+Y4o+AexP8zhYUZA2glgv7YuIB9Lyc4T7wGtA528yA6UNDiVY39joMMh3Ok1K782aJ+VkXxfN3PpEo5/P8PV8WJIqPgBS+Pnj/9sbDtM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=usVGUKVI; arc=none smtp.client-ip=185.246.85.4 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="usVGUKVI" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-03.galae.net (Postfix) with ESMTPS id 838A44E40ECD; Sun, 27 Sep 2026 13:36:48 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 562F1604FF; Sun, 27 Sep 2026 13:36:48 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 5E1D9102F1E7C; Sun, 27 Sep 2026 15:36:42 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1790516206; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=JnS+iZYWz2r8fNfsTOvx0zHQcjMEJWO5MVdrzj/bQPw=; b=usVGUKVIpR//oAS6snezR3DPJAnrwiNo221cEdu1epXcE5FPPJ2Esw1wWGF2hkwMT1oC/C eEWXFraZ3sHl0OcM734g/lV9fBs1soxTsY1vrYYqQ2ixJBYHgfPivTVymO47Ic8CqqajFX LAgN3QV2MW8bDpLERPc3cJSumHP+l7PoAGfgYoTJ4gwoNCca7LYxDFkrOULtgWMIRzVq+L 4Ltg6epA7cIrkg46sARhPjE5ecEJhHWVV+9vvsf9AmacHl2p4DNfW7ThGWRIn6zcl+E5+r /mXAaiUcv4HzQ2kaE+TYtoTT4YBgznWo+VLc24B6EDPyGRoEfNfv+FyFdHAVWA== From: Maxime Chevallier To: davem@davemloft.net, Andrew Lunn , Jakub Kicinski , Eric Dumazet , Paolo Abeni , Russell King , Heiner Kallweit Cc: Maxime Chevallier , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, thomas.petazzoni@bootlin.com, Christophe Leroy , Herve Codina , Florian Fainelli , Vladimir Oltean , =?UTF-8?q?K=C3=B6ry=20Maincent?= , =?UTF-8?q?Marek=20Beh=C3=BAn?= , Oleksij Rempel , =?UTF-8?q?Nicol=C3=B2=20Veronese?= , Simon Horman , mwojtas@chromium.org, Romain Gantois , Daniel Golle , Dimitri Fedrau , Frank Wunderlich , Pietro Ameruoso , Aleksei Sviridkin Subject: [PATCH net-next v18 03/10] net: phylink: Register a phy_port for MAC-driven SFP cages Date: Sun, 27 Sep 2026 15:36:12 +0200 Message-ID: <20260927133619.955236-4-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260927133619.955236-1-maxime.chevallier@bootlin.com> References: <20260927133619.955236-1-maxime.chevallier@bootlin.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" phy_port tracks the interfaces that a netdevice feeds into. SFP cages are such ports, but so far we are only tracking the ones that are driven by PHYs acting as media-converters. Let's populate a phy_port for MAC driven SFP cages, handled by phylink. This phy_port represents the SFP cage itself, and not the module that may be plugged into it. It's therefore not an MDI interface, so only the 'interfaces' field is relevant here. The phy_port is only populated for 'NETDEV' phylink instances, as otherwise we don't have any topology to attach the port to. Reviewed-by: Andrew Lunn Tested-by: Aleksei Sviridkin Signed-off-by: Maxime Chevallier Reviewed-by: Christophe Leroy (CS GROUP) Tested-by: Christophe Leroy (CS GROUP) --- drivers/net/phy/phylink.c | 57 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index a7d086cdc9b2..19090916af75 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -93,6 +95,7 @@ struct phylink { DECLARE_PHY_INTERFACE_MASK(sfp_interfaces); __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); u8 sfp_port; + struct phy_port *sfp_cage_port; =20 struct eee_config eee_cfg; =20 @@ -1767,6 +1770,50 @@ static void phylink_fixed_poll(struct timer_list *t) =20 static const struct sfp_upstream_ops sfp_phylink_ops; =20 +static int phylink_create_sfp_cage_port(struct phylink *pl) +{ + struct phy_port *port; + int ret =3D 0; + + if (!pl->netdev || !pl->sfp_bus) + return 0; + + port =3D phy_port_alloc(); + if (!port) + return -ENOMEM; + + port->is_sfp =3D true; + port->is_mii =3D true; + port->active =3D true; + + phy_interface_and(port->interfaces, pl->config->supported_interfaces, + phylink_sfp_interfaces); + phy_port_update_supported(port); + + ret =3D phy_link_topo_add_port(pl->netdev, port); + if (ret) + phy_port_destroy(port); + else + pl->sfp_cage_port =3D port; + + return ret; +} + +static void phylink_destroy_sfp_cage_port(struct phylink *pl) +{ + if (pl->netdev && pl->sfp_cage_port) { + /* The port may still be accessed under RTNL */ + rtnl_lock(); + phy_link_topo_del_port(pl->netdev, pl->sfp_cage_port); + rtnl_unlock(); + } + + if (pl->sfp_cage_port) + phy_port_destroy(pl->sfp_cage_port); + + pl->sfp_cage_port =3D NULL; +} + static int phylink_register_sfp(struct phylink *pl, const struct fwnode_handle *fwnode) { @@ -1784,9 +1831,18 @@ static int phylink_register_sfp(struct phylink *pl, =20 pl->sfp_bus =3D bus; =20 + ret =3D phylink_create_sfp_cage_port(pl); + if (ret) { + sfp_bus_put(bus); + return ret; + } + ret =3D sfp_bus_add_upstream(bus, pl, &sfp_phylink_ops); sfp_bus_put(bus); =20 + if (ret) + phylink_destroy_sfp_cage_port(pl); + return ret; } =20 @@ -2069,6 +2125,7 @@ EXPORT_SYMBOL_GPL(phylink_create); void phylink_destroy(struct phylink *pl) { sfp_bus_del_upstream(pl->sfp_bus); + phylink_destroy_sfp_cage_port(pl); if (pl->link_gpio) gpiod_put(pl->link_gpio); =20 --=20 2.55.0 From nobody Fri Oct 2 12:24:37 2026 Received: from smtpout-03.galae.net (smtpout-03.galae.net [185.246.85.4]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 77CBF3EFD27 for ; Sun, 27 Sep 2026 13:36:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.85.4 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516220; cv=none; b=srNtF83LU1F1JTv4onilHM9VMwNIh4chdqxosLgILqRKppOF2L8TN72/5Byirm8rpnyqACF7Z9ovOZBhO35W0vsoOQojJ32J4jg8ghxSg4cr08XceK5aq88coxtNfOGpoB4wmeL9G7JOBWWQShuROFi2r3xo97Hn1nfC8XGPv+U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516220; c=relaxed/simple; bh=bQNgM4eq9h64Nc0UH0i3ywAIEhRYOQPkCpOty2iXUbc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=K957CtorSloAltIEFa7+0GJalZXoNm+VCGrd8EYqx5bnCCejda8Lu52Ez1cb0Wtxgv7TfNuG0lEqatSlqLnKhsvZ2ak02uZa74jCQDG+FXOIgaOoI5sOSRFhtzv3XhTLnkWJjphd0PXCJuCbUaUgWqkJOM7dy5WJx+4a9AWkkD0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=vxU4OZtG; arc=none smtp.client-ip=185.246.85.4 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="vxU4OZtG" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-03.galae.net (Postfix) with ESMTPS id E6CC44E41021; Sun, 27 Sep 2026 13:36:52 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id B91D5604FF; Sun, 27 Sep 2026 13:36:52 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id E570A102F1E80; Sun, 27 Sep 2026 15:36:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1790516210; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=blZ34XVYQNO0cXBIL4+i00DtZBkXAFxqEMuicGPwSTI=; b=vxU4OZtGwg6VAHXTC/zoXgJcqR+EMPNpyrGJ6y7kNMp3jpWUePFWxXJbFoJIWZ44zPTvoy x1d9W80GPhWH5RJUa5HZc1MohpyIOvPqjyGJN1637jDYEJIR9GAe7oT7fqonu0FCelT/5W 0kyIFRuk6HcipEHCNqlMk/bZM/tPeX0B4+ZTCGtylBuyhdIv5PPWZGDwlClkA6n1CGniO7 Ye4R/cmQqgA0lK5njs2VW0A0HCyGM5Nf/e7SC/hXpHIpxAyUKKBtVDy00dXAgL3eUFVOc2 zr6RjGLkxh6zl+tYA84Uc30QZWQ8gJcHjJxM9Kv/xP9GwVBM0+dYptN61Q4M1w== From: Maxime Chevallier To: davem@davemloft.net, Andrew Lunn , Jakub Kicinski , Eric Dumazet , Paolo Abeni , Russell King , Heiner Kallweit Cc: Maxime Chevallier , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, thomas.petazzoni@bootlin.com, Christophe Leroy , Herve Codina , Florian Fainelli , Vladimir Oltean , =?UTF-8?q?K=C3=B6ry=20Maincent?= , =?UTF-8?q?Marek=20Beh=C3=BAn?= , Oleksij Rempel , =?UTF-8?q?Nicol=C3=B2=20Veronese?= , Simon Horman , mwojtas@chromium.org, Romain Gantois , Daniel Golle , Dimitri Fedrau , Frank Wunderlich , Pietro Ameruoso , Aleksei Sviridkin Subject: [PATCH net-next v18 04/10] net: phy: Create SFP phy_port before registering upstream Date: Sun, 27 Sep 2026 15:36:13 +0200 Message-ID: <20260927133619.955236-5-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260927133619.955236-1-maxime.chevallier@bootlin.com> References: <20260927133619.955236-1-maxime.chevallier@bootlin.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" When dealing with PHY-driven SFP, we create a phy_port representing the SFP bus when we know we have such a bus. We can move the port creation before registering the sfp upstream ops, as long as we know the SFP bus is there. This will allow passing the phy_port along with the upstream information to the SFP bus. Reviewed-by: Andrew Lunn Tested-by: Aleksei Sviridkin Signed-off-by: Maxime Chevallier Reviewed-by: Christophe Leroy (CS GROUP) Tested-by: Christophe Leroy (CS GROUP) --- drivers/net/phy/phy_device.c | 58 +++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index c4d2c03b8282..3c20d4cc1a79 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1586,13 +1586,13 @@ static void phy_del_port(struct phy_device *phydev,= struct phy_port *port) phydev->n_ports--; } =20 -static int phy_setup_sfp_port(struct phy_device *phydev) +static struct phy_port *phy_setup_sfp_port(struct phy_device *phydev) { struct phy_port *port =3D phy_port_alloc(); int ret; =20 if (!port) - return -ENOMEM; + return ERR_PTR(-ENOMEM); =20 port->parent_type =3D PHY_PORT_PHY; port->phy =3D phydev; @@ -1607,10 +1607,12 @@ static int phy_setup_sfp_port(struct phy_device *ph= ydev) * when attaching the port to the phydev. */ ret =3D phy_add_port(phydev, port); - if (ret) + if (ret) { phy_port_destroy(port); + return ERR_PTR(ret); + } =20 - return ret; + return port; } =20 /** @@ -1619,30 +1621,46 @@ static int phy_setup_sfp_port(struct phy_device *ph= ydev) */ static int phy_sfp_probe(struct phy_device *phydev) { + struct phy_port *port =3D NULL; struct sfp_bus *bus; - int ret =3D 0; + int ret; =20 - if (phydev->mdio.dev.fwnode) { - bus =3D sfp_bus_find_fwnode(phydev->mdio.dev.fwnode); - if (IS_ERR(bus)) - return PTR_ERR(bus); + if (!phydev->mdio.dev.fwnode) + return 0; =20 - phydev->sfp_bus =3D bus; + bus =3D sfp_bus_find_fwnode(phydev->mdio.dev.fwnode); + if (IS_ERR(bus)) + return PTR_ERR(bus); =20 - ret =3D sfp_bus_add_upstream(bus, phydev, &sfp_phydev_ops); - sfp_bus_put(bus); + phydev->sfp_bus =3D bus; =20 - if (ret) - phydev->sfp_bus =3D NULL; + if (bus) { + port =3D phy_setup_sfp_port(phydev); + if (IS_ERR(port)) { + ret =3D PTR_ERR(port); + goto out_sfp; + } } =20 - if (!ret && phydev->sfp_bus) { - ret =3D phy_setup_sfp_port(phydev); - if (ret) { - sfp_bus_del_upstream(phydev->sfp_bus); - phydev->sfp_bus =3D NULL; - } + ret =3D sfp_bus_add_upstream(bus, phydev, &sfp_phydev_ops); + if (ret) + goto out_port; + + /* sfp_bus_add_upstream() grabs a ref to the sfp bus on success, it's + * safe to release it now. + */ + sfp_bus_put(bus); + + return ret; + +out_port: + if (port) { + phy_del_port(phydev, port); + phy_port_destroy(port); } +out_sfp: + sfp_bus_put(bus); + phydev->sfp_bus =3D NULL; =20 return ret; } --=20 2.55.0 From nobody Fri Oct 2 12:24:37 2026 Received: from smtpout-02.galae.net (smtpout-02.galae.net [185.246.84.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6F8793F12D8 for ; Sun, 27 Sep 2026 13:36:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.84.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516221; cv=none; b=qoMo9DO9SZkxwo8GTbPBIUlmgBU9uQo6/QY6mTwH3jaUcm/3YM6IjwLrMIJwx9DYuAa3BYBte2TUPQymYM96WlGfg11EsmOOugFjG9Xa3nasCTbnq5E3/XYoJ35aA5mxh8iRYpq/DnSlK6NTFi5oKcIF22NV1dOAeimL5RAL+Mg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516221; c=relaxed/simple; bh=stE3Wgmr2kz7sktX2AKEhRlYlFEjuIVBTDMbPC9w3RQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MiV1c308j53VD5aJjwsuKXKr/wJ67YaHfWJIUhwopm2felCavWuDWJNUvhhcux/PKX/IKnmt1ShcBJlcrlNhiw4VJLGmOQ8Mcr0//vTt40OcfuQ11FuP3JoboBrRKAULrbcbcgrXCck8v9A3CHLTexAN8qQG19alPr6yNfK/gZc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=22cxqvZN; arc=none smtp.client-ip=185.246.84.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="22cxqvZN" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id DA48E1A0FF7; Sun, 27 Sep 2026 13:36:57 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id AD29D604FF; Sun, 27 Sep 2026 13:36:57 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 40E73102F1E7E; Sun, 27 Sep 2026 15:36:51 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1790516215; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=hgB4wFLFjjb70ZB+8++YQqPEkRFXSxvnH2zaK2vuG4M=; b=22cxqvZNZLYhXwFpKpmiIXU59ymDEQ+RVo4PXqkDFuubsk7aLTEQy6N63sWYdwD8XNrABC mv86j81KPIkaaSqAJEBKT5rh3XatuBeNGJUsz116O659MjLIaiMO32d/xLdyn9g+cFaw7s a/U6wg2zoX5AIeLxZzTTi6RobdQf8Ez1klRvC1FBdlbn13wXG3g0apALLPbSCB7V54E/cR 5iMhj+yjeqkZClOIq88jYhjTQ9pnA1iRY6Xy+ekI4eNKzj47sZbhcS6BrjJdmnGSWigepJ qqObYqX2+5I2C9ARvhGJoE6W8yrEGZMeQZzEHVj9TWEVQ6YTHShtcnt9Npu2Lw== From: Maxime Chevallier To: davem@davemloft.net, Andrew Lunn , Jakub Kicinski , Eric Dumazet , Paolo Abeni , Russell King , Heiner Kallweit Cc: Maxime Chevallier , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, thomas.petazzoni@bootlin.com, Christophe Leroy , Herve Codina , Florian Fainelli , Vladimir Oltean , =?UTF-8?q?K=C3=B6ry=20Maincent?= , =?UTF-8?q?Marek=20Beh=C3=BAn?= , Oleksij Rempel , =?UTF-8?q?Nicol=C3=B2=20Veronese?= , Simon Horman , mwojtas@chromium.org, Romain Gantois , Daniel Golle , Dimitri Fedrau , Frank Wunderlich , Pietro Ameruoso , Aleksei Sviridkin Subject: [PATCH net-next v18 05/10] net: phy: Represent PHY-less SFP modules with phy_port Date: Sun, 27 Sep 2026 15:36:14 +0200 Message-ID: <20260927133619.955236-6-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260927133619.955236-1-maxime.chevallier@bootlin.com> References: <20260927133619.955236-1-maxime.chevallier@bootlin.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" Now that the SFP bus infrastructure notifies when PHY-less modules are connected, we can create a phy_port to represent it. Instead of letting the SFP subsystem handle that, the Bus' upstream is in charge of maintaining that phy_port and register it to the topology, as the upstream (in this case a phy device) is directly interacting with the underlying net_device. Add a phy_caps helper to get the achievable modes on this module based on what the phy_port representing the bus supports. Reviewed-by: Andrew Lunn Tested-by: Aleksei Sviridkin Signed-off-by: Maxime Chevallier Reviewed-by: Christophe Leroy (CS GROUP) Tested-by: Christophe Leroy (CS GROUP) --- drivers/net/phy/phy-caps.h | 2 + drivers/net/phy/phy_caps.c | 26 +++++++++ drivers/net/phy/phy_device.c | 100 +++++++++++++++++++++++++++++++++-- drivers/net/phy/phylink.c | 76 ++++++++++++++++++++++++-- include/linux/phy.h | 6 +++ 5 files changed, 203 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/phy-caps.h b/drivers/net/phy/phy-caps.h index 421088e6f6e8..ec3d39a0ae06 100644 --- a/drivers/net/phy/phy-caps.h +++ b/drivers/net/phy/phy-caps.h @@ -66,5 +66,7 @@ void phy_caps_medium_get_supported(unsigned long *support= ed, enum ethtool_link_medium medium, int lanes); u32 phy_caps_mediums_from_linkmodes(unsigned long *linkmodes); +void phy_caps_linkmode_filter_ifaces(unsigned long *to, const unsigned lon= g *from, + const unsigned long *interfaces); =20 #endif /* __PHY_CAPS_H */ diff --git a/drivers/net/phy/phy_caps.c b/drivers/net/phy/phy_caps.c index 942d43191561..558e4df4d63c 100644 --- a/drivers/net/phy/phy_caps.c +++ b/drivers/net/phy/phy_caps.c @@ -445,3 +445,29 @@ u32 phy_caps_mediums_from_linkmodes(unsigned long *lin= kmodes) return mediums; } EXPORT_SYMBOL_GPL(phy_caps_mediums_from_linkmodes); + +/** + * phy_caps_linkmode_filter_ifaces() - Filter linkmodes with an interface = list + * @to: Stores the filtered linkmodes + * @from: Linkmodes to filter + * @interfaces: Bitfield of phy_interface_t that we use for filtering + * + * Filter the provided linkmodes, only to keep the ones we can possibly ac= hieve + * when using any of the provided MII interfaces. + */ +void phy_caps_linkmode_filter_ifaces(unsigned long *to, + const unsigned long *from, + const unsigned long *interfaces) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(ifaces_supported) =3D {}; + unsigned int ifaces_caps =3D 0; + phy_interface_t interface; + + for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) + ifaces_caps |=3D phy_caps_from_interface(interface); + + phy_caps_linkmodes(ifaces_caps, ifaces_supported); + + linkmode_and(to, from, ifaces_supported); +} +EXPORT_SYMBOL_GPL(phy_caps_linkmode_filter_ifaces); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 3c20d4cc1a79..ab9631b161bc 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1400,11 +1400,21 @@ static int phy_sfp_connect_phy(void *upstream, stru= ct phy_device *phy) { struct phy_device *phydev =3D upstream; struct net_device *dev =3D phydev->attached_dev; + int ret; =20 - if (dev) - return phy_link_topo_add_phy(dev, phy, PHY_UPSTREAM_PHY, phydev); + phydev->has_sfp_mod_phy =3D true; =20 - return 0; + /* If we aren't attached to a netdev, we can't add the SFP PHY to its + * topology. + */ + if (!dev) + return 0; + + ret =3D phy_link_topo_add_phy(dev, phy, PHY_UPSTREAM_PHY, phydev); + if (ret) + phydev->has_sfp_mod_phy =3D false; + + return ret; } =20 /** @@ -1422,6 +1432,8 @@ static void phy_sfp_disconnect_phy(void *upstream, st= ruct phy_device *phy) struct phy_device *phydev =3D upstream; struct net_device *dev =3D phydev->attached_dev; =20 + phydev->has_sfp_mod_phy =3D false; + if (dev) phy_link_topo_del_phy(dev, phy); } @@ -1527,6 +1539,75 @@ static void phy_sfp_link_down(void *upstream) port->ops->link_down(port); } =20 +static int phy_add_sfp_mod_port(struct phy_device *phydev) +{ + const struct sfp_module_caps *caps; + struct phy_port *port; + int ret =3D 0; + + /* Create mod port */ + port =3D phy_port_alloc(); + if (!port) + return -ENOMEM; + + port->active =3D true; + + caps =3D sfp_get_module_caps(phydev->sfp_bus); + + phy_caps_linkmode_filter_ifaces(port->supported, caps->link_modes, + phydev->sfp_cage_port->interfaces); + + if (phydev->attached_dev) { + ret =3D phy_link_topo_add_port(phydev->attached_dev, port); + if (ret) { + phy_port_destroy(port); + return ret; + } + } + + /* we don't use phy_add_port() here as the module port isn't a direct + * interface from the PHY, but rather an extension to the sfp-bus, that + * is already represented by its own phy_port + */ + phydev->mod_port =3D port; + + return 0; +} + +static void phy_del_sfp_mod_port(struct phy_device *phydev) +{ + if (!phydev->mod_port) + return; + + if (phydev->attached_dev) + phy_link_topo_del_port(phydev->attached_dev, phydev->mod_port); + + phy_port_destroy(phydev->mod_port); + phydev->mod_port =3D NULL; +} + +static int phy_sfp_module_start(void *upstream) +{ + struct phy_device *phydev =3D upstream; + + /* If there's a downstream SFP module, and it doesn't contain a PHY + * device, let's create a phy_port to represent that module. + */ + if (!phydev->has_sfp_mod_phy) + return phy_add_sfp_mod_port(phydev); + + return 0; +} + +static void phy_sfp_module_stop(void *upstream) +{ + struct phy_device *phydev =3D upstream; + + /* Called upon module removal or upstream removal */ + if (!phydev->has_sfp_mod_phy) + phy_del_sfp_mod_port(phydev); +} + static const struct sfp_upstream_ops sfp_phydev_ops =3D { .attach =3D phy_sfp_attach, .detach =3D phy_sfp_detach, @@ -1536,6 +1617,8 @@ static const struct sfp_upstream_ops sfp_phydev_ops = =3D { .link_down =3D phy_sfp_link_down, .connect_phy =3D phy_sfp_connect_phy, .disconnect_phy =3D phy_sfp_disconnect_phy, + .module_start =3D phy_sfp_module_start, + .module_stop =3D phy_sfp_module_stop, }; =20 static int phy_add_port(struct phy_device *phydev, struct phy_port *port) @@ -1638,6 +1721,7 @@ static int phy_sfp_probe(struct phy_device *phydev) port =3D phy_setup_sfp_port(phydev); if (IS_ERR(port)) { ret =3D PTR_ERR(port); + port =3D NULL; goto out_sfp; } } @@ -1651,6 +1735,8 @@ static int phy_sfp_probe(struct phy_device *phydev) */ sfp_bus_put(bus); =20 + phydev->sfp_cage_port =3D port; + return ret; =20 out_port: @@ -1744,6 +1830,8 @@ static void phy_detach_internal(struct phy_device *ph= ydev, bool notify_bus) phydev->attached_dev->phydev =3D NULL; phydev->attached_dev =3D NULL; phy_link_topo_del_phy(dev, phydev); + if (phydev->mod_port) + phy_link_topo_del_port(dev, phydev->mod_port); } =20 phydev->phy_link_change =3D NULL; @@ -1873,6 +1961,12 @@ int phy_attach_direct(struct net_device *dev, struct= phy_device *phydev, err =3D phy_link_topo_add_phy(dev, phydev, PHY_UPSTREAM_MAC, dev); if (err) goto error; + + if (phydev->mod_port) { + err =3D phy_link_topo_add_port(dev, phydev->mod_port); + if (err) + goto error; + } } =20 /* Some Ethernet drivers try to connect to a PHY device before diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 19090916af75..db05d2dda429 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -96,6 +96,7 @@ struct phylink { __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); u8 sfp_port; struct phy_port *sfp_cage_port; + struct phy_port *mod_port; =20 struct eee_config eee_cfg; =20 @@ -1792,10 +1793,15 @@ static int phylink_create_sfp_cage_port(struct phyl= ink *pl) =20 ret =3D phy_link_topo_add_port(pl->netdev, port); if (ret) - phy_port_destroy(port); - else - pl->sfp_cage_port =3D port; + goto out_destroy_port; + + pl->sfp_cage_port =3D port; + + return 0; =20 +out_destroy_port: + phy_port_destroy(port); + pl->sfp_cage_port =3D NULL; return ret; } =20 @@ -4012,14 +4018,65 @@ static void phylink_sfp_module_remove(void *upstrea= m) phy_interface_zero(pl->sfp_interfaces); } =20 +static int phylink_add_sfp_mod_port(struct phylink *pl) +{ + const struct sfp_module_caps *caps; + struct phy_port *port; + int ret =3D 0; + + if (!pl->sfp_cage_port) + return 0; + + /* Create mod port */ + port =3D phy_port_alloc(); + if (!port) + return -ENOMEM; + + port->active =3D true; + + caps =3D sfp_get_module_caps(pl->sfp_bus); + + phy_caps_linkmode_filter_ifaces(port->supported, caps->link_modes, + pl->sfp_cage_port->interfaces); + + if (pl->netdev) { + ret =3D phy_link_topo_add_port(pl->netdev, port); + if (ret) { + phy_port_destroy(port); + return ret; + } + } + + pl->mod_port =3D port; + + return 0; +} + +static void phylink_del_sfp_mod_port(struct phylink *pl) +{ + if (!pl->mod_port) + return; + + if (pl->netdev) + phy_link_topo_del_port(pl->netdev, pl->mod_port); + + phy_port_destroy(pl->mod_port); + pl->mod_port =3D NULL; +} + static int phylink_sfp_module_start(void *upstream) { struct phylink *pl =3D upstream; + int ret; =20 /* If this SFP module has a PHY, start the PHY now. */ if (pl->phydev) { phy_start(pl->phydev); return 0; + } else { + ret =3D phylink_add_sfp_mod_port(pl); + if (ret) + return ret; } =20 /* If the module may have a PHY but we didn't detect one we @@ -4028,7 +4085,16 @@ static int phylink_sfp_module_start(void *upstream) if (!pl->sfp_may_have_phy) return 0; =20 - return phylink_sfp_config_optical(pl); + ret =3D phylink_sfp_config_optical(pl); + if (ret) + goto del_mod_port; + + return 0; + +del_mod_port: + phylink_del_sfp_mod_port(pl); + + return ret; } =20 static void phylink_sfp_module_stop(void *upstream) @@ -4038,6 +4104,8 @@ static void phylink_sfp_module_stop(void *upstream) /* If this SFP module has a PHY, stop it. */ if (pl->phydev) phy_stop(pl->phydev); + else + phylink_del_sfp_mod_port(pl); } =20 static void phylink_sfp_link_down(void *upstream) diff --git a/include/linux/phy.h b/include/linux/phy.h index 7c5098a0dd6c..dbd6ed5864f2 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -611,7 +611,10 @@ struct phy_oatc14_sqi_capability { * @irq: IRQ number of the PHY's interrupt (-1 if none) * @phylink: Pointer to phylink instance for this PHY * @sfp_bus_attached: Flag indicating whether the SFP bus has been attached + * @has_sfp_mod_phy: Set true if downstream SFP bus's module contains a PHY * @sfp_bus: SFP bus attached to this PHY's fiber port + * @sfp_cage_port: The phy_port connected to the downstream SFP cage + * @mod_port: phy_port representing the SFP module, if it is phy-less * @attached_dev: The attached enet driver's device instance ptr * @adjust_link: Callback for the enet controller to respond to changes: i= n the * link state. @@ -802,7 +805,10 @@ struct phy_device { =20 /* This may be modified under the rtnl lock */ bool sfp_bus_attached; + bool has_sfp_mod_phy; struct sfp_bus *sfp_bus; + struct phy_port *sfp_cage_port; + struct phy_port *mod_port; struct phylink *phylink; struct net_device *attached_dev; struct mii_timestamper *mii_ts; --=20 2.55.0 From nobody Fri Oct 2 12:24:37 2026 Received: from smtpout-04.galae.net (smtpout-04.galae.net [185.171.202.116]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 71C2E3F1076 for ; Sun, 27 Sep 2026 13:37:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.171.202.116 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516226; cv=none; b=Bn6oN5+ItpM2mc6hkS4paIfVlgG1PEKL14qMkzQn7s2O7yRby47Yy/jHmgfDTz6YDlOasSkEVIRKdbDW3QpyJJAxjNxhQPEQM681r/yzfsxkpmfjQEUeWZVQBMmiFN8Ss+Hf3nhCTQr1zeQ2G6gr8HeaCADt79RdYDpQxDWrvBE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516226; c=relaxed/simple; bh=HuPQNtDrKXtQodmJyCHIZYeuOILTXObYVvpp5ZdvQZo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HR96EKP3GHfgRD2ba3S8JwVpJN1y31vBuxTDPit1BFgaOg64o4/oDRhUh6vNvkIU2UgXY4RvgUj6eb4XVY+oi9qGhSd24dULhCiPv9SS5ZFudsSqHFCODWUNL27GtOThHG+X6QHmVRW1amPmMPzShriHIvJdpYlTuYN69zlipBY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=CULR4WYW; arc=none smtp.client-ip=185.171.202.116 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="CULR4WYW" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id EC90BC63454; Sun, 27 Sep 2026 13:37:50 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 2F21B604FF; Sun, 27 Sep 2026 13:37:02 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id D1BE2102F1E7F; Sun, 27 Sep 2026 15:36:55 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1790516220; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=0x/Y7CGuRP/6GHTAS5nFlfJJkE7qQg7R4aqO29cLM3M=; b=CULR4WYWogt0pJlwJis3W4mJwM/dFSW+Ru88kGMWd+IH7WxYoQ0HNwnOgAjHQ4O1usJBMT YUYqdtm3tTriNKarLm1Tj6zoE1x0ER2XC6L5oVDIvL3Dafki+Y3MKo7MYFkFJJsGS4wien ic6gXePGK4pe2d2LQKvFvX+NhYNwGEGlvzWs373art1SAzSxhi5v13n+psc/uGvYQpG1sW Om4kC3IrEZJFfsOdyZc6GaSl6x/KGXwbdTT/xPudlyyi07Mx4psoIGiRFnT0VJncwwIDiu /+mL8gHcvvpci3xHEUeBo/pYC7sCRyhKQowk9duJ+dq5Ok5/8ilFHGQkzdDHqA== From: Maxime Chevallier To: davem@davemloft.net, Andrew Lunn , Jakub Kicinski , Eric Dumazet , Paolo Abeni , Russell King , Heiner Kallweit Cc: Maxime Chevallier , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, thomas.petazzoni@bootlin.com, Christophe Leroy , Herve Codina , Florian Fainelli , Vladimir Oltean , =?UTF-8?q?K=C3=B6ry=20Maincent?= , =?UTF-8?q?Marek=20Beh=C3=BAn?= , Oleksij Rempel , =?UTF-8?q?Nicol=C3=B2=20Veronese?= , Simon Horman , mwojtas@chromium.org, Romain Gantois , Daniel Golle , Dimitri Fedrau , Frank Wunderlich , Pietro Ameruoso , Aleksei Sviridkin Subject: [PATCH net-next v18 06/10] net: phy: phy_port: Store information about a port's upstream Date: Sun, 27 Sep 2026 15:36:15 +0200 Message-ID: <20260927133619.955236-7-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260927133619.955236-1-maxime.chevallier@bootlin.com> References: <20260927133619.955236-1-maxime.chevallier@bootlin.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" MII phy_ports are not meant to be connected directly to a link partner. They are meant to feed into some media converter devices that will expose an MDI phy_port, so far we only support SFP modules for that. In the case an MDI phy_port is backed by an MII port (e.g. a SFP module's port, backed by the SFP cage port), let's keep track of the port id of the MII port backing it. Reviewed-by: Andrew Lunn Tested-by: Aleksei Sviridkin Signed-off-by: Maxime Chevallier Reviewed-by: Christophe Leroy (CS GROUP) Tested-by: Christophe Leroy (CS GROUP) --- drivers/net/phy/phy_device.c | 29 +++++++++++++++++++++++++++-- drivers/net/phy/phylink.c | 5 +++++ include/linux/phy.h | 4 ++++ include/linux/phy_port.h | 3 +++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index ab9631b161bc..2bb4abd0d392 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1403,6 +1403,7 @@ static int phy_sfp_connect_phy(void *upstream, struct= phy_device *phy) int ret; =20 phydev->has_sfp_mod_phy =3D true; + phy_set_upstream_port(phy, phydev->sfp_cage_port); =20 /* If we aren't attached to a netdev, we can't add the SFP PHY to its * topology. @@ -1436,6 +1437,8 @@ static void phy_sfp_disconnect_phy(void *upstream, st= ruct phy_device *phy) =20 if (dev) phy_link_topo_del_phy(dev, phy); + + phy_set_upstream_port(phy, NULL); } =20 /** @@ -1571,6 +1574,8 @@ static int phy_add_sfp_mod_port(struct phy_device *ph= ydev) */ phydev->mod_port =3D port; =20 + port->upstream_port =3D phydev->sfp_cage_port; + return 0; } =20 @@ -1726,6 +1731,8 @@ static int phy_sfp_probe(struct phy_device *phydev) } } =20 + phydev->sfp_cage_port =3D port; + ret =3D sfp_bus_add_upstream(bus, phydev, &sfp_phydev_ops); if (ret) goto out_port; @@ -1735,14 +1742,13 @@ static int phy_sfp_probe(struct phy_device *phydev) */ sfp_bus_put(bus); =20 - phydev->sfp_cage_port =3D port; - return ret; =20 out_port: if (port) { phy_del_port(phydev, port); phy_port_destroy(port); + phydev->sfp_cage_port =3D NULL; } out_sfp: sfp_bus_put(bus); @@ -3687,6 +3693,25 @@ struct phy_port *phy_get_sfp_port(struct phy_device = *phydev) } EXPORT_SYMBOL_GPL(phy_get_sfp_port); =20 +/** + * phy_set_upstream_port() - Sets the phy_port controlling the MII this PH= Y is + * attached to. + * @phydev: pointer to the PHY device we set the upstream of. + * @port: The phy_port upstream of this PHY, can be NULL. + */ +void phy_set_upstream_port(struct phy_device *phydev, struct phy_port *por= t) +{ + struct phy_port *local_port; + + ASSERT_RTNL(); + + phydev->upstream_port =3D port; + + phy_for_each_port(phydev, local_port) + local_port->upstream_port =3D port; +} +EXPORT_SYMBOL_GPL(phy_set_upstream_port); + /** * fwnode_mdio_find_device - Given a fwnode, find the mdio_device * @fwnode: pointer to the mdio_device's fwnode diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index db05d2dda429..2583e913cff6 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -4047,6 +4047,8 @@ static int phylink_add_sfp_mod_port(struct phylink *p= l) } } =20 + port->upstream_port =3D pl->sfp_cage_port; + pl->mod_port =3D port; =20 return 0; @@ -4150,6 +4152,8 @@ static int phylink_sfp_connect_phy(void *upstream, st= ruct phy_device *phy) phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces, pl->config->supported_interfaces); =20 + phy_set_upstream_port(phy, pl->sfp_cage_port); + /* Do the initial configuration */ return phylink_sfp_config_phy(pl, phy); } @@ -4158,6 +4162,7 @@ static void phylink_sfp_disconnect_phy(void *upstream, struct phy_device *phydev) { phylink_disconnect_phy(upstream); + phy_set_upstream_port(phydev, NULL); } =20 static const struct sfp_upstream_ops sfp_phylink_ops =3D { diff --git a/include/linux/phy.h b/include/linux/phy.h index dbd6ed5864f2..1fb87d12367b 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -615,6 +615,7 @@ struct phy_oatc14_sqi_capability { * @sfp_bus: SFP bus attached to this PHY's fiber port * @sfp_cage_port: The phy_port connected to the downstream SFP cage * @mod_port: phy_port representing the SFP module, if it is phy-less + * @upstream_port: phy_port this PHY's MII attaches to, if any * @attached_dev: The attached enet driver's device instance ptr * @adjust_link: Callback for the enet controller to respond to changes: i= n the * link state. @@ -809,6 +810,7 @@ struct phy_device { struct sfp_bus *sfp_bus; struct phy_port *sfp_cage_port; struct phy_port *mod_port; + struct phy_port *upstream_port; struct phylink *phylink; struct net_device *attached_dev; struct mii_timestamper *mii_ts; @@ -2493,6 +2495,8 @@ int __phy_hwtstamp_set(struct phy_device *phydev, =20 struct phy_port *phy_get_sfp_port(struct phy_device *phydev); =20 +void phy_set_upstream_port(struct phy_device *phydev, struct phy_port *por= t); + /** * phy_module_driver() - Helper macro for registering PHY drivers * @__phy_drivers: array of PHY drivers to register diff --git a/include/linux/phy_port.h b/include/linux/phy_port.h index 4e2a3fdd2f2e..8f45c031cbc1 100644 --- a/include/linux/phy_port.h +++ b/include/linux/phy_port.h @@ -40,6 +40,8 @@ struct phy_port_ops { * @head: Used by the port's parent to list ports * @parent_type: The type of device this port is directly connected to * @phy: If the parent is PHY_PORT_PHYDEV, the PHY controlling that port + * @upstream_port: Indicates the MII port that feeds this port, if any, + * e.g. the SFP cage port for a SFP module port. * @ops: Callback ops implemented by the port controller * @pairs: The number of pairs this port has, 0 if not applicable * @mediums: Bitmask of the physical mediums this port provides access to @@ -59,6 +61,7 @@ struct phy_port { union { struct phy_device *phy; }; + struct phy_port *upstream_port; =20 const struct phy_port_ops *ops; =20 --=20 2.55.0 From nobody Fri Oct 2 12:24:37 2026 Received: from smtpout-04.galae.net (smtpout-04.galae.net [185.171.202.116]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E48153C13FB for ; Sun, 27 Sep 2026 13:37:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.171.202.116 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516230; cv=none; b=aGy4fYuLCqsdwvQQ0hWXrRaiGceiub/O1pxeOZMJP+dhguX9gPGd2OVAm8wy6kTe51tHnmzDjiz3/x0rlSPf71ntT3zeOZCmvz22AFc2AAZMzYVJKezxDGxAKLMS8FRZoC8Hx98LqGI/LPZfgsAvdwmsfTPgWnWsrthtSUaKHHg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516230; c=relaxed/simple; bh=EBW9JDcIIUnZNsgR8ZYwAVVS23O5RWhwgNyBXsRw/oQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ASzbMGDdCTnie09QMyTtFF8R6BNdoNkULfwdY1WZ5EfNAgO3S8DueYQfNpGrauFXlBO/C/RYL/FSNko/4c5OiMVzFq9aY268I5ROqReK1o9CUlykdsQNVpBM0hN9Kjskx5nzm5smjH6Z7BtJG69DQTr2we9N/W64aXhuxdsOB1g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=fZpEoAt4; arc=none smtp.client-ip=185.171.202.116 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="fZpEoAt4" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id D0F2AC6220D; Sun, 27 Sep 2026 13:37:55 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 1EA6F604FF; Sun, 27 Sep 2026 13:37:07 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 851CC102F1E7D; Sun, 27 Sep 2026 15:37:00 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1790516225; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=DRWG5dVl16McE/UB+GcT6u4bht/KXV2eLnoJ83MZf1I=; b=fZpEoAt4ORkOltXk1qs6XVCT3le9ciGYM107SLxi+fzoaeealnBjqKgTSF86m4WR5WiCDK WaJIq0PJR7d5tN+s6+085TlXpmVQCP2m9PCNfF6ssSo4On9MRmS4Q44B5ILMTvFSEtVkxT agLaxtCyPqfUzUTltpz2wTPXfGLgNbLhTaa5ENJI8fFgZaDp5+YbumJZFB3mm/Hb3SyfCj uYaN+cyZQ9prBVEQ6NjTg3y67TG6fig4BmQeYMNoUAN26YB6RGmojkAd/mHgHwqcRQZWfi FjJIDbnFuoWrQNOayGtOlEPR960++NR/je9jKFJdGdZ4Mw1LrcT2tbR0x8QT+Q== From: Maxime Chevallier To: davem@davemloft.net, Andrew Lunn , Jakub Kicinski , Eric Dumazet , Paolo Abeni , Russell King , Heiner Kallweit Cc: Maxime Chevallier , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, thomas.petazzoni@bootlin.com, Christophe Leroy , Herve Codina , Florian Fainelli , Vladimir Oltean , =?UTF-8?q?K=C3=B6ry=20Maincent?= , =?UTF-8?q?Marek=20Beh=C3=BAn?= , Oleksij Rempel , =?UTF-8?q?Nicol=C3=B2=20Veronese?= , Simon Horman , mwojtas@chromium.org, Romain Gantois , Daniel Golle , Dimitri Fedrau , Frank Wunderlich , Pietro Ameruoso , Aleksei Sviridkin Subject: [PATCH net-next v18 07/10] net: phy: phy_link_topology: Add a helper to retrieve ports Date: Sun, 27 Sep 2026 15:36:16 +0200 Message-ID: <20260927133619.955236-8-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260927133619.955236-1-maxime.chevallier@bootlin.com> References: <20260927133619.955236-1-maxime.chevallier@bootlin.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" In order to allow netlink access to phy_ports, let's add a helper to retrieve them. When handling a port coming from phy_link_topology, the caller must hold rtnl until it's done with it. We don't really support PHY and phy_port operation on ops-locked devices yet, warn when we try to do so on such an interface. Reviewed-by: Andrew Lunn Tested-by: Aleksei Sviridkin Signed-off-by: Maxime Chevallier Reviewed-by: Christophe Leroy (CS GROUP) Tested-by: Christophe Leroy (CS GROUP) --- drivers/net/phy/phy_link_topology.c | 8 ++++++++ include/linux/phy_link_topology.h | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/drivers/net/phy/phy_link_topology.c b/drivers/net/phy/phy_link= _topology.c index 28c5bf3a8849..214ccce23cf6 100644 --- a/drivers/net/phy/phy_link_topology.c +++ b/drivers/net/phy/phy_link_topology.c @@ -65,6 +65,14 @@ int phy_link_topo_add_port(struct net_device *dev, struc= t phy_port *port) struct phy_link_topology *topo; int ret; =20 + /* Ports can now be queried without rtnl for ops-locked devices, which + * we don't support now as port retrieval is done under rtnl. + * We don't have phy_port enabled devices yet, let's make sure + * we are loudly warned about that when it happens. + */ + if (WARN_ON_ONCE(dev->netdev_ops && netdev_need_ops_lock(dev))) + return -EOPNOTSUPP; + topo =3D phy_link_topo_get_or_alloc(dev); if (IS_ERR(topo)) return PTR_ERR(topo); diff --git a/include/linux/phy_link_topology.h b/include/linux/phy_link_top= ology.h index 296ee514ba46..f2e493c433ff 100644 --- a/include/linux/phy_link_topology.h +++ b/include/linux/phy_link_topology.h @@ -13,6 +13,7 @@ =20 #include #include +#include =20 struct xarray; struct phy_device; @@ -71,6 +72,24 @@ phy_link_topo_get_phy(struct net_device *dev, u32 phyind= ex) return NULL; } =20 +static inline struct phy_port * +phy_link_topo_get_port(struct net_device *dev, u32 port_id) +{ + struct phy_link_topology *topo =3D dev->link_topo; + + if (!topo) + return NULL; + + /* ports in the topo are RTNL protected, but may be accessed under + * netdev_lock for ops-locked devices. For now keep them under rtnl + * protection, as no ops-locked devices have phy_port capabilities + * yet. + */ + ASSERT_RTNL(); + + return xa_load(&topo->ports, port_id); +} + #else static inline int phy_link_topo_add_phy(struct net_device *dev, struct phy_device *phy, @@ -100,6 +119,12 @@ phy_link_topo_get_phy(struct net_device *dev, u32 phyi= ndex) { return NULL; } + +static inline struct phy_port * +phy_link_topo_get_port(struct net_device *dev, u32 port_id) +{ + return NULL; +} #endif =20 #endif /* __PHY_LINK_TOPOLOGY_H */ --=20 2.55.0 From nobody Fri Oct 2 12:24:37 2026 Received: from smtpout-04.galae.net (smtpout-04.galae.net [185.171.202.116]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BE9DD3F1AD5 for ; Sun, 27 Sep 2026 13:37:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.171.202.116 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516237; cv=none; b=pupTJgr4MUKaGQf62le3F4l/kVU8vYfUmUfy7IBFA25skG+Yqh1G4QzRK2b+IB3N7ou4W9CSwgj3Eq08F5/41ova2ncQEyolzokLS09DYP2RkSYqNmq3PaC5r2hYHsZ81CoVTq4OhlQq7bhhXttu5DAYG+MTNCgHz40ICd1K5S8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516237; c=relaxed/simple; bh=XjdMjdbNryzH/p56jpIZHC0URawcHZJP8NEiTTVMQKE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rwn28GTIEJ1qEo0pdQDKSUKSNhf++P3WMQkWH2Q5OTmRpGZYf7Md4pYBGeJxqo3Lh5S/qkTlxMkcimeLUiqU3AV5TkzeXIBVacXYy2wbA21vYB/pxE+nf+LWSAxJcmTCHRiJqByUVLn3YZ1z72/LsnFhYPY3kixAvGZtP1JjDdA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=st9cofMu; arc=none smtp.client-ip=185.171.202.116 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="st9cofMu" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id 99193C63454; Sun, 27 Sep 2026 13:38:01 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id D62A9604FF; Sun, 27 Sep 2026 13:37:12 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id AEE27102F1E7B; Sun, 27 Sep 2026 15:37:05 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1790516230; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=+wqdEu2XjHr9MBUryD7ahkZW78s8r46Q6rq1LUNTqEM=; b=st9cofMuC7AL0kvyWsHxOJ5g/nEI3V5cAnJyN47UcxdKxAiyBrLgdyrUaz9rOaZ4D142r/ RdkGvMGLEObMyV2Yww33QEh5T8UPCEYBO61k2fiu3ijioIxiXaoVGn2JboBB4AzIIBFaI+ W6TSTDYN6yHJx97UJ/xOR4NomeeGZVGZg7QS2A83D5XVAK30/ERMgC51gtCV4tWWG40C9e ls/AJEoPXP7qdtm2hWtQNcsrEHPAULshL2JfsdLQ0ZyonnUt2FbQOLXrPV8R0xr+DVQYS6 E3seGPEfahh6QCutwmheymBuhkgxAeXjLlql1wYxYb2rabG0cF8cx/xev1kZqw== From: Maxime Chevallier To: davem@davemloft.net, Andrew Lunn , Jakub Kicinski , Eric Dumazet , Paolo Abeni , Russell King , Heiner Kallweit Cc: Maxime Chevallier , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, thomas.petazzoni@bootlin.com, Christophe Leroy , Herve Codina , Florian Fainelli , Vladimir Oltean , =?UTF-8?q?K=C3=B6ry=20Maincent?= , =?UTF-8?q?Marek=20Beh=C3=BAn?= , Oleksij Rempel , =?UTF-8?q?Nicol=C3=B2=20Veronese?= , Simon Horman , mwojtas@chromium.org, Romain Gantois , Daniel Golle , Dimitri Fedrau , Frank Wunderlich , Pietro Ameruoso , Aleksei Sviridkin Subject: [PATCH net-next v18 08/10] netlink: specs: Add ethernet port listing with ethtool Date: Sun, 27 Sep 2026 15:36:17 +0200 Message-ID: <20260927133619.955236-9-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260927133619.955236-1-maxime.chevallier@bootlin.com> References: <20260927133619.955236-1-maxime.chevallier@bootlin.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" Ethernet network interfaces may have more than one front-facing port. The phy_port infrastructure was introduced to keep track of these ports, and allow userspace to know about the presence and capability of these ports. Add a ethnl netlink message to report this information. Tested-by: Aleksei Sviridkin Signed-off-by: Maxime Chevallier Reviewed-by: Christophe Leroy (CS GROUP) Tested-by: Christophe Leroy (CS GROUP) --- Documentation/netlink/specs/ethtool.yaml | 55 +++++++++++++++++++ Documentation/networking/ethtool-netlink.rst | 36 ++++++++++++ .../uapi/linux/ethtool_netlink_generated.h | 22 ++++++++ 3 files changed, 113 insertions(+) diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netli= nk/specs/ethtool.yaml index 5dd4d1b5d94b..35e028c0be29 100644 --- a/Documentation/netlink/specs/ethtool.yaml +++ b/Documentation/netlink/specs/ethtool.yaml @@ -210,6 +210,11 @@ definitions: - name: discard value: 31 + - + name: port-type + doc: type of physical connector + type: enum + entries: [mdi, sfp] =20 attribute-sets: - @@ -1905,6 +1910,32 @@ attribute-sets: name: link type: nest nested-attributes: mse-snapshot + - + name: port + attr-cnt-name: --ethtool-a-port-cnt + attributes: + - + name: header + type: nest + nested-attributes: header + - + name: id + type: u32 + - + name: supported-modes + type: nest + nested-attributes: bitset + - + name: supported-interfaces + type: nest + nested-attributes: bitset + - + name: type + type: u32 + enum: port-type + - + name: upstream-port + type: u32 =20 operations: enum-model: directional @@ -2859,6 +2890,30 @@ operations: - worst-channel - link dump: *mse-get-op + - + name: port-get + doc: Get ports attached to an interface + + attribute-set: port + + do: + request: + attributes: + - header + - id + reply: &port-get-op-reply + attributes: + - header + - id + - supported-modes + - supported-interfaces + - type + - upstream-port + dump: + request: + attributes: + - header + reply: *port-get-op-reply =20 mcast-groups: list: diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/n= etworking/ethtool-netlink.rst index 393f2b6d3863..eba226c79ec3 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -247,6 +247,7 @@ Userspace to kernel: ``ETHTOOL_MSG_RSS_CREATE_ACT`` create an additional RSS context ``ETHTOOL_MSG_RSS_DELETE_ACT`` delete an additional RSS context ``ETHTOOL_MSG_MSE_GET`` get MSE diagnostic data + ``ETHTOOL_MSG_PORT_GET`` get port information =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 Kernel to userspace: @@ -305,6 +306,7 @@ Kernel to userspace: ``ETHTOOL_MSG_RSS_CREATE_NTF`` additional RSS context created ``ETHTOOL_MSG_RSS_DELETE_NTF`` additional RSS context deleted ``ETHTOOL_MSG_MSE_GET_REPLY`` MSE diagnostic data + ``ETHTOOL_MSG_PORT_GET_REPLY`` port information =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 ``GET`` requests are sent by userspace applications to retrieve device @@ -2537,6 +2539,39 @@ Within each channel nest, only the metrics supported= by the PHY will be present. See ``struct phy_mse_snapshot`` kernel documentation in ``include/linux/phy.h``. =20 +PORT_GET +=3D=3D=3D=3D=3D=3D=3D=3D + +Retrieve information about the physical connection points of a network dev= ice, +referred to as "ports". User needs to specify a PORT_ID for the DO operati= on, +in which case the DO request returns information about that specific port. + +As there can be more than one port, the DUMP operation can be used to list= the +ports present on a given interface, by passing an interface index or name = in +the dump request. + +Request contents: + + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D + ``ETHTOOL_A_PORT_HEADER`` nested request header + ``ETHTOOL_A_PORT_ID`` u32 port id + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D + +Kernel response contents: + + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D =3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D + ``ETHTOOL_A_PORT_HEADER`` nested request header + ``ETHTOOL_A_PORT_ID`` u32 the port's unique identi= fier, + per netdevice. + ``ETHTOOL_A_PORT_SUPPORTED_MODES`` bitset bitset of supported link= modes + ``ETHTOOL_A_PORT_SUPPORTED_INTERFACES`` bitset bitset of supported MII + interfaces + ``ETHTOOL_A_PORT_TYPE`` u32 the port type + ``ETHTOOL_A_PORT_UPSTREAM_PORT`` u32 If any, the index of the= MII + port that feeds into this + port. + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D =3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D + Request translation =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 @@ -2647,4 +2682,5 @@ are netlink only. n/a ``ETHTOOL_MSG_PHY_GET`` ``SIOCGHWTSTAMP`` ``ETHTOOL_MSG_TSCONFIG_GET`` ``SIOCSHWTSTAMP`` ``ETHTOOL_MSG_TSCONFIG_SET`` + n/a ``ETHTOOL_MSG_PORT_GET`` =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/= linux/ethtool_netlink_generated.h index 8134baf7860f..660e0c08a766 100644 --- a/include/uapi/linux/ethtool_netlink_generated.h +++ b/include/uapi/linux/ethtool_netlink_generated.h @@ -78,6 +78,14 @@ enum ethtool_pse_event { ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR =3D 64, }; =20 +/* + * type of physical connector + */ +enum ethtool_port_type { + ETHTOOL_PORT_TYPE_MDI, + ETHTOOL_PORT_TYPE_SFP, +}; + enum { ETHTOOL_A_HEADER_UNSPEC, ETHTOOL_A_HEADER_DEV_INDEX, @@ -840,6 +848,18 @@ enum { ETHTOOL_A_MSE_MAX =3D (__ETHTOOL_A_MSE_CNT - 1) }; =20 +enum { + ETHTOOL_A_PORT_HEADER =3D 1, + ETHTOOL_A_PORT_ID, + ETHTOOL_A_PORT_SUPPORTED_MODES, + ETHTOOL_A_PORT_SUPPORTED_INTERFACES, + ETHTOOL_A_PORT_TYPE, + ETHTOOL_A_PORT_UPSTREAM_PORT, + + __ETHTOOL_A_PORT_CNT, + ETHTOOL_A_PORT_MAX =3D (__ETHTOOL_A_PORT_CNT - 1) +}; + enum { ETHTOOL_MSG_USER_NONE =3D 0, ETHTOOL_MSG_STRSET_GET =3D 1, @@ -893,6 +913,7 @@ enum { ETHTOOL_MSG_RSS_CREATE_ACT, ETHTOOL_MSG_RSS_DELETE_ACT, ETHTOOL_MSG_MSE_GET, + ETHTOOL_MSG_PORT_GET, =20 __ETHTOOL_MSG_USER_CNT, ETHTOOL_MSG_USER_MAX =3D (__ETHTOOL_MSG_USER_CNT - 1) @@ -954,6 +975,7 @@ enum { ETHTOOL_MSG_RSS_CREATE_NTF, ETHTOOL_MSG_RSS_DELETE_NTF, ETHTOOL_MSG_MSE_GET_REPLY, + ETHTOOL_MSG_PORT_GET_REPLY, =20 __ETHTOOL_MSG_KERNEL_CNT, ETHTOOL_MSG_KERNEL_MAX =3D (__ETHTOOL_MSG_KERNEL_CNT - 1) --=20 2.55.0 From nobody Fri Oct 2 12:24:37 2026 Received: from smtpout-04.galae.net (smtpout-04.galae.net [185.171.202.116]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5D55B3F1643 for ; Sun, 27 Sep 2026 13:37:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.171.202.116 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516240; cv=none; b=GhQPs5aJlrD47RGL3jdwjCHJog4H2+FlFdY6F4SBlR+uLS28VkUhLxoQq5Iw1qnqzBypxXzd1MnIIdoZ8qDtPYouTH7C0BdScW8DziAryAWiKpPhtzb7qh+abKT5kEk+v5x9HaW2w2DKSgcwSJvMkCsARTZ9y55Sr7AmbIIzPNc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516240; c=relaxed/simple; bh=cj2aWmYZTXlFkxkm48bpaMQhfPNfdA1WPV6FUciU5Ps=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=aijNrugNeMpBq/zpXp4ObxHSyGxPLFEtBU2ku9JX7WJ26WLH1uNr+FAMMuVviJ/fFAnJcpYPLE3X4JidWfttbTUVmFa4MH+tqNqfY7JPRffNm17GvKj3BNGE6gRNsXnMbi+Uopv5zfVT9FB1u74TGhkiWwPNjhjyk5StUBUe9Mw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=ZGkGajvU; arc=none smtp.client-ip=185.171.202.116 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="ZGkGajvU" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id AACFDC6220D; Sun, 27 Sep 2026 13:38:05 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id EEA73604FF; Sun, 27 Sep 2026 13:37:16 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id C1022102F1E7C; Sun, 27 Sep 2026 15:37:10 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1790516235; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=+FYwT9qP6yX3q03bZhrXs2ACVX2xAAiHWIfqd3n4+BQ=; b=ZGkGajvU4GIqHVBPVx7PXJmbauK9ndOjMANBxf7uXePsfmsnWX5wZhuqhL8aSa9a+sgVL1 JczXl1qH6zhHYgG+PJb9VG4Jduxkr3uNLxp6m7WkC7+6rlTVxh9vOOSGqo+sI9HPM12ENY smyWQfKBdOv/cKpZeiLfMNbAqUAfQTWn7ty+hI4tXbhKxQxZ9eBcabq9KQPw/B8a4xb5SF oWMsM7nCXYrsIprEGx8+H90ov81KZiFP+e8D1lqaz8HewlLD2mUML47MsW276KcJtOezsV EGzbjxrLX1+jI8NBD4JgOptYyC0A3xpoooR5DFBSPuTC+nCyx84D9h1sxqpxvw== From: Maxime Chevallier To: davem@davemloft.net, Andrew Lunn , Jakub Kicinski , Eric Dumazet , Paolo Abeni , Russell King , Heiner Kallweit Cc: Maxime Chevallier , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, thomas.petazzoni@bootlin.com, Christophe Leroy , Herve Codina , Florian Fainelli , Vladimir Oltean , =?UTF-8?q?K=C3=B6ry=20Maincent?= , =?UTF-8?q?Marek=20Beh=C3=BAn?= , Oleksij Rempel , =?UTF-8?q?Nicol=C3=B2=20Veronese?= , Simon Horman , mwojtas@chromium.org, Romain Gantois , Daniel Golle , Dimitri Fedrau , Frank Wunderlich , Pietro Ameruoso , Aleksei Sviridkin Subject: [PATCH net-next v18 09/10] net: ethtool: Introduce ethtool command to list ports Date: Sun, 27 Sep 2026 15:36:18 +0200 Message-ID: <20260927133619.955236-10-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260927133619.955236-1-maxime.chevallier@bootlin.com> References: <20260927133619.955236-1-maxime.chevallier@bootlin.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" Expose the phy_port information to userspace, so that we can know how many ports are available on a given interface, as well as their capabilities. For MDI ports, we report the list of supported linkmodes based on what the PHY that drives this port says. For MII ports, i.e. empty SFP cages, we report the MII linkmodes that we can output on this port. Tested-by: Aleksei Sviridkin Signed-off-by: Maxime Chevallier Reviewed-by: Christophe Leroy (CS GROUP) Tested-by: Christophe Leroy (CS GROUP) --- MAINTAINERS | 1 + net/ethtool/Makefile | 2 +- net/ethtool/netlink.c | 25 +++ net/ethtool/netlink.h | 8 + net/ethtool/port.c | 382 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 417 insertions(+), 1 deletion(-) create mode 100644 net/ethtool/port.c diff --git a/MAINTAINERS b/MAINTAINERS index 6de1ff058db6..877f3316b5ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19010,6 +19010,7 @@ F: Documentation/devicetree/bindings/net/ethernet-c= onnector.yaml F: Documentation/networking/phy-port.rst F: drivers/net/phy/phy_port.c F: include/linux/phy_port.h +F: net/ethtool/port.c K: struct\s+phy_port|phy_port_ =20 NETWORKING [GENERAL] diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index 629c10916670..9b5b09670008 100644 --- a/net/ethtool/Makefile +++ b/net/ethtool/Makefile @@ -9,4 +9,4 @@ ethtool_nl-y :=3D netlink.o bitset.o strset.o linkinfo.o li= nkmodes.o rss.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 cmis_fw_update.o cmis_cdb.o pse-pd.o plca.o \ - phy.o tsconfig.o mse.o + phy.o tsconfig.o mse.o port.o diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 1af395b54330..c076c07d0a08 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -26,6 +26,8 @@ static u32 ethnl_bcast_seq; ETHTOOL_FLAG_OMIT_REPLY) #define ETHTOOL_FLAGS_STATS (ETHTOOL_FLAGS_BASIC | ETHTOOL_FLAG_STATS) =20 +char phy_interface_names[PHY_INTERFACE_MODE_MAX][ETH_GSTRING_LEN] __ro_aft= er_init; + const struct nla_policy ethnl_header_policy[] =3D { [ETHTOOL_A_HEADER_DEV_INDEX] =3D { .type =3D NLA_U32 }, [ETHTOOL_A_HEADER_DEV_NAME] =3D { .type =3D NLA_NUL_STRING, @@ -431,6 +433,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] =3D { [ETHTOOL_MSG_TSCONFIG_SET] =3D ðnl_tsconfig_request_ops, [ETHTOOL_MSG_PHY_GET] =3D ðnl_phy_request_ops, [ETHTOOL_MSG_MSE_GET] =3D ðnl_mse_request_ops, + [ETHTOOL_MSG_PORT_GET] =3D ðnl_port_request_ops, }; =20 static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *= cb) @@ -1572,6 +1575,15 @@ static const struct genl_ops ethtool_genl_ops[] =3D { .policy =3D ethnl_mse_get_policy, .maxattr =3D ARRAY_SIZE(ethnl_mse_get_policy) - 1, }, + { + .cmd =3D ETHTOOL_MSG_PORT_GET, + .doit =3D ethnl_default_doit, + .start =3D ethnl_port_dump_start, + .dumpit =3D ethnl_port_dumpit, + .done =3D ethnl_port_dump_done, + .policy =3D ethnl_port_get_policy, + .maxattr =3D ARRAY_SIZE(ethnl_port_get_policy) - 1, + }, }; =20 static const struct genl_multicast_group ethtool_nl_mcgrps[] =3D { @@ -1594,10 +1606,23 @@ static struct genl_family ethtool_genl_family __ro_= after_init =3D { =20 /* module setup */ =20 +static void __init ethnl_phy_names_populate(void) +{ + const char *name; + int i; + + for (i =3D 0; i < PHY_INTERFACE_MODE_MAX; i++) { + name =3D phy_modes(i); + strscpy(phy_interface_names[i], name, ETH_GSTRING_LEN); + } +} + static int __init ethnl_init(void) { int ret; =20 + ethnl_phy_names_populate(); + ret =3D genl_register_family(ðtool_genl_family); if (WARN(ret < 0, "ethtool: genetlink family registration failed")) return ret; diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 3e969a070f9f..dc7211f67c4d 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -5,11 +5,14 @@ =20 #include #include +#include #include #include =20 struct ethnl_req_info; =20 +extern char phy_interface_names[PHY_INTERFACE_MODE_MAX][ETH_GSTRING_LEN]; + int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info, const struct nlattr *nest, struct net *net, struct netlink_ext_ack *extack, @@ -474,6 +477,7 @@ extern const struct ethnl_request_ops ethnl_mm_request_= ops; extern const struct ethnl_request_ops ethnl_phy_request_ops; extern const struct ethnl_request_ops ethnl_tsconfig_request_ops; extern const struct ethnl_request_ops ethnl_mse_request_ops; +extern const struct ethnl_request_ops ethnl_port_request_ops; =20 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]; @@ -530,6 +534,7 @@ extern const struct nla_policy ethnl_phy_get_policy[ETH= TOOL_A_PHY_HEADER + 1]; extern const struct nla_policy ethnl_tsconfig_get_policy[ETHTOOL_A_TSCONFI= G_HEADER + 1]; extern const struct nla_policy ethnl_tsconfig_set_policy[ETHTOOL_A_TSCONFI= G_MAX + 1]; extern const struct nla_policy ethnl_mse_get_policy[ETHTOOL_A_MSE_HEADER += 1]; +extern const struct nla_policy ethnl_port_get_policy[ETHTOOL_A_PORT_ID + 1= ]; =20 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); @@ -545,6 +550,9 @@ int ethnl_tsinfo_dumpit(struct sk_buff *skb, struct net= link_callback *cb); int ethnl_tsinfo_done(struct netlink_callback *cb); int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info); int ethnl_rss_delete_doit(struct sk_buff *skb, struct genl_info *info); +int ethnl_port_dump_start(struct netlink_callback *cb); +int ethnl_port_dumpit(struct sk_buff *skb, struct netlink_callback *cb); +int ethnl_port_dump_done(struct netlink_callback *cb); =20 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_G= STRING_LEN]; diff --git a/net/ethtool/port.c b/net/ethtool/port.c new file mode 100644 index 000000000000..0d6150e5f66a --- /dev/null +++ b/net/ethtool/port.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2026 Bootlin + */ +#include +#include +#include +#include + +#include "bitset.h" +#include "common.h" +#include "netlink.h" + +struct port_req_info { + struct ethnl_req_info base; + u32 port_id; +}; + +struct port_reply_data { + struct ethnl_reply_data base; + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); + DECLARE_PHY_INTERFACE_MASK(interfaces); + u32 port_id; + bool mii; + bool sfp; + u32 upstream_port; +}; + +#define PORT_REQINFO(__req_base) \ + container_of(__req_base, struct port_req_info, base) + +#define PORT_REPDATA(__reply_base) \ + container_of(__reply_base, struct port_reply_data, base) + +const struct nla_policy ethnl_port_get_policy[ETHTOOL_A_PORT_ID + 1] =3D { + [ETHTOOL_A_PORT_HEADER] =3D NLA_POLICY_NESTED(ethnl_header_policy), + [ETHTOOL_A_PORT_ID] =3D NLA_POLICY_MIN(NLA_U32, 1), +}; + +static int port_parse_request(struct ethnl_req_info *req_info, + const struct genl_info *info, + struct nlattr **tb, + struct netlink_ext_ack *extack) +{ + struct port_req_info *request =3D PORT_REQINFO(req_info); + + if (GENL_REQ_ATTR_CHECK(info, ETHTOOL_A_PORT_ID)) + return -EINVAL; + + request->port_id =3D nla_get_u32(tb[ETHTOOL_A_PORT_ID]); + + return 0; +} + +static int port_prepare_data(const struct ethnl_req_info *req_info, + struct ethnl_reply_data *reply_data, + const struct genl_info *info) +{ + struct port_reply_data *reply =3D PORT_REPDATA(reply_data); + struct port_req_info *request =3D PORT_REQINFO(req_info); + struct phy_port *port; + + /* RTNL must be held while holding a ref to the phy_port. Here, caller + * holds RTNL. + */ + port =3D phy_link_topo_get_port(req_info->dev, request->port_id); + if (!port) + return -ENODEV; + + linkmode_copy(reply->supported, port->supported); + phy_interface_copy(reply->interfaces, port->interfaces); + reply->port_id =3D port->id; + reply->mii =3D port->is_mii; + reply->sfp =3D port->is_sfp; + reply->upstream_port =3D port->upstream_port ? port->upstream_port->id : = 0; + + return 0; +} + +static int port_reply_size(const struct ethnl_req_info *req_info, + const struct ethnl_reply_data *reply_data) +{ + bool compact =3D req_info->flags & ETHTOOL_FLAG_COMPACT_BITSETS; + struct port_reply_data *reply =3D PORT_REPDATA(reply_data); + size_t size =3D 0; + int ret; + + /* ETHTOOL_A_PORT_ID */ + size +=3D nla_total_size(sizeof(u32)); + + if (!reply->mii) { + /* ETHTOOL_A_PORT_SUPPORTED_MODES */ + ret =3D ethnl_bitset_size(reply->supported, NULL, + __ETHTOOL_LINK_MODE_MASK_NBITS, + link_mode_names, compact); + if (ret < 0) + return ret; + + size +=3D ret; + } else { + /* ETHTOOL_A_PORT_SUPPORTED_INTERFACES */ + ret =3D ethnl_bitset_size(reply->interfaces, NULL, + PHY_INTERFACE_MODE_MAX, + phy_interface_names, compact); + if (ret < 0) + return ret; + + size +=3D ret; + } + + /* ETHTOOL_A_PORT_TYPE */ + size +=3D nla_total_size(sizeof(u32)); + + /* ETHTOOL_A_PORT_UPSTREAM_PORT */ + if (reply->upstream_port) + size +=3D nla_total_size(sizeof(u32)); + + return size; +} + +static int port_fill_reply(struct sk_buff *skb, + const struct ethnl_req_info *req_info, + const struct ethnl_reply_data *reply_data) +{ + bool compact =3D req_info->flags & ETHTOOL_FLAG_COMPACT_BITSETS; + struct port_reply_data *reply =3D PORT_REPDATA(reply_data); + int ret, port_type =3D ETHTOOL_PORT_TYPE_MDI; + + if (nla_put_u32(skb, ETHTOOL_A_PORT_ID, reply->port_id)) + return -EMSGSIZE; + + if (!reply->mii) { + ret =3D ethnl_put_bitset(skb, ETHTOOL_A_PORT_SUPPORTED_MODES, + reply->supported, NULL, + __ETHTOOL_LINK_MODE_MASK_NBITS, + link_mode_names, compact); + if (ret < 0) + return ret; + } else { + ret =3D ethnl_put_bitset(skb, ETHTOOL_A_PORT_SUPPORTED_INTERFACES, + reply->interfaces, NULL, + PHY_INTERFACE_MODE_MAX, + phy_interface_names, compact); + if (ret < 0) + return ret; + } + + if (reply->mii || reply->sfp) + port_type =3D ETHTOOL_PORT_TYPE_SFP; + + if (nla_put_u32(skb, ETHTOOL_A_PORT_TYPE, port_type)) + return -EMSGSIZE; + + if (reply->upstream_port && + nla_put_u32(skb, ETHTOOL_A_PORT_UPSTREAM_PORT, + reply->upstream_port)) + return -EMSGSIZE; + + return 0; +} + +struct port_dump_ctx { + struct port_req_info *req_info; + struct port_reply_data *reply_data; + unsigned long ifindex; + unsigned long pos_ifindex; + unsigned long pos_portid; +}; + +static struct port_dump_ctx * +port_dump_ctx_get(struct netlink_callback *cb) +{ + return (struct port_dump_ctx *)cb->ctx; +} + +int ethnl_port_dump_start(struct netlink_callback *cb) +{ + const struct genl_dumpit_info *info =3D genl_dumpit_info(cb); + struct port_dump_ctx *ctx =3D port_dump_ctx_get(cb); + struct nlattr **tb =3D info->info.attrs; + struct port_reply_data *reply_data; + struct port_req_info *req_info; + int ret; + + BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); + + req_info =3D kzalloc_obj(*req_info); + if (!req_info) + return -ENOMEM; + + reply_data =3D kmalloc_obj(*reply_data); + if (!reply_data) { + ret =3D -ENOMEM; + goto free_req_info; + } + + ret =3D ethnl_parse_header_dev_get(&req_info->base, tb[ETHTOOL_A_PORT_HEA= DER], + genl_info_net(&info->info), + info->info.extack, false); + if (ret < 0) + goto free_rep_data; + + ctx->ifindex =3D 0; + ctx->pos_ifindex =3D 0; + + /* For filtered DUMP requests, let's just store the ifindex. We'll check + * again if the netdev is still there when looping over the netdev list + * in the DUMP loop. + */ + if (req_info->base.dev) { + ctx->ifindex =3D req_info->base.dev->ifindex; + netdev_put(req_info->base.dev, &req_info->base.dev_tracker); + req_info->base.dev =3D NULL; + } + + ctx->req_info =3D req_info; + ctx->reply_data =3D reply_data; + + return 0; + +free_rep_data: + kfree(reply_data); +free_req_info: + kfree(req_info); + + return ret; +} + +static int port_dump_one(struct sk_buff *skb, struct net_device *dev, + struct netlink_callback *cb) +{ + struct port_dump_ctx *ctx =3D port_dump_ctx_get(cb); + void *ehdr; + int ret; + + ehdr =3D ethnl_dump_put(skb, cb, ETHTOOL_MSG_PORT_GET_REPLY); + if (!ehdr) + return -EMSGSIZE; + + memset(ctx->reply_data, 0, sizeof(struct port_reply_data)); + ctx->reply_data->base.dev =3D dev; + + rtnl_lock(); + netdev_lock_ops(dev); + + ret =3D port_prepare_data(&ctx->req_info->base, &ctx->reply_data->base, + genl_info_dump(cb)); + + netdev_unlock_ops(dev); + rtnl_unlock(); + + if (ret < 0) + goto out; + + ret =3D ethnl_fill_reply_header(skb, dev, ETHTOOL_A_PORT_HEADER); + if (ret < 0) + goto out; + + ret =3D port_fill_reply(skb, &ctx->req_info->base, &ctx->reply_data->base= ); + +out: + ctx->reply_data->base.dev =3D NULL; + if (ret < 0) + genlmsg_cancel(skb, ehdr); + else + genlmsg_end(skb, ehdr); + + return ret; +} + +static int port_dump_one_dev(struct sk_buff *skb, struct netlink_callback = *cb) +{ + struct port_dump_ctx *ctx =3D port_dump_ctx_get(cb); + struct net_device *dev; + struct phy_port *port; + int ret; + + dev =3D ctx->req_info->base.dev; + + if (!dev->link_topo) + return 0; + + xa_for_each_start(&dev->link_topo->ports, ctx->pos_portid, port, + ctx->pos_portid) { + ctx->req_info->port_id =3D ctx->pos_portid; + + ret =3D port_dump_one(skb, dev, cb); + if (ret =3D=3D -ENODEV) + continue; + if (ret) + return ret; + } + + ctx->pos_portid =3D 0; + + return 0; +} + +static int port_dump_all_dev(struct sk_buff *skb, struct netlink_callback = *cb) +{ + struct port_dump_ctx *ctx =3D port_dump_ctx_get(cb); + struct net *net =3D sock_net(skb->sk); + netdevice_tracker dev_tracker; + struct net_device *dev; + int ret =3D 0; + + rcu_read_lock(); + for_each_netdev_dump(net, dev, ctx->pos_ifindex) { + netdev_hold(dev, &dev_tracker, GFP_ATOMIC); + rcu_read_unlock(); + + ctx->req_info->base.dev =3D dev; + ret =3D port_dump_one_dev(skb, cb); + + rcu_read_lock(); + netdev_put(dev, &dev_tracker); + ctx->req_info->base.dev =3D NULL; + + if (ret < 0 && ret !=3D -EOPNOTSUPP) { + if (likely(skb->len)) + ret =3D skb->len; + break; + } + + ret =3D 0; + } + rcu_read_unlock(); + + return ret; +} + +int ethnl_port_dumpit(struct sk_buff *skb, struct netlink_callback *cb) +{ + const struct genl_dumpit_info *info =3D genl_dumpit_info(cb); + struct port_dump_ctx *ctx =3D port_dump_ctx_get(cb); + int ret =3D 0; + + if (ctx->ifindex) { + netdevice_tracker dev_tracker; + struct net_device *dev; + + dev =3D netdev_get_by_index(genl_info_net(&info->info), + ctx->ifindex, &dev_tracker, + GFP_KERNEL); + if (!dev) + return -ENODEV; + + ctx->req_info->base.dev =3D dev; + ret =3D port_dump_one_dev(skb, cb); + if (ret < 0 && ret !=3D -EOPNOTSUPP && likely(skb->len)) + ret =3D skb->len; + + netdev_put(dev, &dev_tracker); + } else { + ret =3D port_dump_all_dev(skb, cb); + } + + return ret; +} + +int ethnl_port_dump_done(struct netlink_callback *cb) +{ + struct port_dump_ctx *ctx =3D port_dump_ctx_get(cb); + + kfree(ctx->req_info); + kfree(ctx->reply_data); + + return 0; +} + +const struct ethnl_request_ops ethnl_port_request_ops =3D { + .request_cmd =3D ETHTOOL_MSG_PORT_GET, + .reply_cmd =3D ETHTOOL_MSG_PORT_GET_REPLY, + .hdr_attr =3D ETHTOOL_A_PORT_HEADER, + .req_info_size =3D sizeof(struct port_req_info), + .reply_data_size =3D sizeof(struct port_reply_data), + + .parse_request =3D port_parse_request, + .prepare_data =3D port_prepare_data, + .reply_size =3D port_reply_size, + .fill_reply =3D port_fill_reply, +}; --=20 2.55.0 From nobody Fri Oct 2 12:24:37 2026 Received: from smtpout-03.galae.net (smtpout-03.galae.net [185.246.85.4]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F32503D411D for ; Sun, 27 Sep 2026 13:37:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.85.4 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516246; cv=none; b=DXWGByFLSmHI1jv/bZmy0Ybq2xcmv9eWzSSeWfNuvdol2PRe8grLmnnA1ChCwHCSltBYl+7URSA7t/V6D/kwOgAoryHTOQark+b0Hz6qB6elKTI0iLafzpESFnoPmA0feqbjVNglHfSQ5zl8dl3nKoRIuR2HJggFnXVzgu3ulMU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790516246; c=relaxed/simple; bh=y2XQNiedrD/GbweFwiRwG63lCrwaAPfIBOifZlSTGTg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Z6yBptT93KNq7pSDPOv24Pcivt/AC5Gbtcj7LnL43G3jSAaLo+6TDc6AL2kCvdgKXNF49qk94xoZP5cjukOsAgEJbmdtmrL1vbgocBrgOKAI6ErvFF4i6984QD4TclgP9oRhaZch4k32McKZ1n4t4QGKLXJ3OYgknVadt02Lbw4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=yuji6sq8; arc=none smtp.client-ip=185.246.85.4 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="yuji6sq8" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-03.galae.net (Postfix) with ESMTPS id 7C2184E41021; Sun, 27 Sep 2026 13:37:21 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 5048F604FF; Sun, 27 Sep 2026 13:37:21 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 93528102F1E66; Sun, 27 Sep 2026 15:37:15 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1790516239; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=Z13EOcnX8B1/8BD2O0e2wgJx2zHuu7HyJIbYUX/IvJ8=; b=yuji6sq87xQa34o57ctC8bpZqRxm3Xg83qGNyrkZi4fbw28Y93IKuTFZNxzAmiELimkyw4 9EpJEJv/pA9Vezd19TwJAq6fXk9crOgjZelrj/nVYtG/9iqTfWg8Y5gE1dCK+tXjkYn7W4 xS5e+3bGwLlIyIPyy3aezs/TfQEAVA9wJjjYHABo1x9XS502IvGmrzyHYjysuzuh2ouhsz C4zhuc5bz5w3tj6Z/rHwqAfUwM4SJmUJzrnbFGHUWyAVu2j+6veNpFB+Zy2aJQmM+KJoo5 Yvo0Ug2l9dhlBfYYzZJjeDcWvJs04dEze9bpcT52ENyyBmfA6z48sVTpn/VAwA== From: Maxime Chevallier To: davem@davemloft.net, Andrew Lunn , Jakub Kicinski , Eric Dumazet , Paolo Abeni , Russell King , Heiner Kallweit Cc: Maxime Chevallier , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, thomas.petazzoni@bootlin.com, Christophe Leroy , Herve Codina , Florian Fainelli , Vladimir Oltean , =?UTF-8?q?K=C3=B6ry=20Maincent?= , =?UTF-8?q?Marek=20Beh=C3=BAn?= , Oleksij Rempel , =?UTF-8?q?Nicol=C3=B2=20Veronese?= , Simon Horman , mwojtas@chromium.org, Romain Gantois , Daniel Golle , Dimitri Fedrau , Frank Wunderlich , Pietro Ameruoso , Aleksei Sviridkin Subject: [PATCH net-next v18 10/10] Documentation: networking: Update the phy_port infrastructure description Date: Sun, 27 Sep 2026 15:36:19 +0200 Message-ID: <20260927133619.955236-11-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260927133619.955236-1-maxime.chevallier@bootlin.com> References: <20260927133619.955236-1-maxime.chevallier@bootlin.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" With SFP now properly supported with phy_port, add some details in the documentation. Fix a typo along the way (driver -> driven). Reviewed-by: Andrew Lunn Tested-by: Aleksei Sviridkin Signed-off-by: Maxime Chevallier Reviewed-by: Christophe Leroy (CS GROUP) Tested-by: Christophe Leroy (CS GROUP) --- Documentation/networking/phy-port.rst | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/Documentation/networking/phy-port.rst b/Documentation/networki= ng/phy-port.rst index 6e28d9094bce..2a9b1ec93b70 100644 --- a/Documentation/networking/phy-port.rst +++ b/Documentation/networking/phy-port.rst @@ -99,13 +99,29 @@ will eventually be able to report its own ksettings:: (_____)-----| Port | +------+ =20 +SFP ports +=3D=3D=3D=3D=3D=3D=3D=3D=3D + +SFP interfaces involve 2 distinct components, each represented by +a :c:type:`struct phy_port ` instance : + + - The SFP cage itself is a :c:type:`struct phy_port `. It's spe= cial + in that it's not an MDI interface, but rather a hot-pluggable MII. + The :c:type:`struct phy_port ` associated to it lists the dif= ferent + MII interfaces we can use on the cage. + + - The SFP module, when inserted, will also be associated to a + :c:type:`struct phy_port `, that represents the various linkm= odes + that it gives access to. The module's :c:type:`struct phy_port ` + doesn't supersede the cage's port, it references it through + the :c:type:`struct phy_port ` :c:member:`upstream_port` fiel= d. + Next steps =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 -As of writing this documentation, only ports controlled by PHY devices are -supported. The next steps will be to add the Netlink API to expose these -to userspace and add support for raw ports (controlled by some firmware, a= nd directly -managed by the NIC driver). +As of writing this documentation, the port's presence and information can = only +be queried, and it's not possible to change any of the port's settings or = select +which one should be used. =20 Another parallel task is the introduction of a MII muxing framework to all= ow the -control of non-PHY driver multi-port setups. +control of non-PHY driven multi-port setups. --=20 2.55.0