From nobody Sun Feb 8 10:04:27 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 C86072F1FEA for ; Tue, 3 Feb 2026 17:29:00 +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=1770139742; cv=none; b=W9jkYCDZ9VYAmTlPK0QqxXc2zrnqv5NMWYsN09sOMcQfLH1dpkyolpX/lF3qgeRI3s5mGRyMmZXEk3I/SCbtfSnbFG7fxGsH2h0jN4HeO0MbESmI6sYwE1co34A3XzSRDBOEpYS4QcrLlGy/tfUV/IDEFRq47gQj8i99AO5Olog= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770139742; c=relaxed/simple; bh=wRzJK9Yc33onsr1SqP1pSLxAinwQo/XqZP+eAfKnhpg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=L9MG5XJbdDLxa3S1e3rWV5u9hnCIuoHQQ1VIdApIxMtT6ChjMI0YSiiPboSK7rnnMS7f17EH9aBZ8EyFFLVftsHqB/wVqgIgdT8eCw5tTmEhexg8EAr2wYOSTXpNkPvbwo90EeG0p9mgIDstYDKeCo/e06LQUlvDm7FVGT/2UEc= 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=fhYErvC5; 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="fhYErvC5" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id 91428C24385; Tue, 3 Feb 2026 17:28:58 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 4C71A60728; Tue, 3 Feb 2026 17:28:53 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 42561119A88EE; Tue, 3 Feb 2026 18:28:50 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1770139732; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=6yh6DzDkyKCbVY38GzuiVuOPYlrZ+spxgJkz3vkq/kI=; b=fhYErvC5sSdGikVuWpcEzDzDWhLg6o+F2I6+ZKl9KAQzFSHQELyFIv9NJBs/jGXRo2m2Y4 2uw/oIALzzCqzi0vTFiO0+f7p+u30WF8YsT7Sg7Py71fgXftUUfSlDxoD2uFiVKcCd6acN wbtkc9YO5yZil4BQzW7hj+TcGpYqhGfGQEA1Rsliexv1zKyIDtMk9oR++vXBOU/lO08uTx l5SaoIe69TYKNg4/s+crVbNBgC9xJC/J+Uy08ObVKgmiWvln6jQ0d08bcbQ9SDEmewWGO+ hnsn0DdRPWawonXeclX1uM9hkIxrc2kLOpi00WwDkSHtunHOoMSicI4J/b0WlQ== 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 Subject: [PATCH net-next v4 01/13] net: phy: initialize the port support based on the PHY's for OF ports Date: Tue, 3 Feb 2026 18:28:25 +0100 Message-ID: <20260203172839.548524-2-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20260203172839.548524-1-maxime.chevallier@bootlin.com> References: <20260203172839.548524-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 the phy_port infrastructure came an ethernet-connector binding, allowing to represent the MDI of a PHY in devicetree. This allows specifying the mediums and pairs of a port. Let's initialize the port's supported list based on what the PHY reports, so that we can then filter it with what the connector allows using. Signed-off-by: Maxime Chevallier --- drivers/net/phy/phy_device.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 8a3eb1839a3d..9b8eaac63b90 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -3499,6 +3499,9 @@ static int of_phy_ports(struct phy_device *phydev) =20 port->parent_type =3D PHY_PORT_PHY; port->phy =3D phydev; + + linkmode_copy(port->supported, phydev->supported); + err =3D phy_add_port(phydev, port); if (err) { phy_port_destroy(port); --=20 2.49.0 From nobody Sun Feb 8 10:04:27 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 0A45132548E for ; Tue, 3 Feb 2026 17:29:02 +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=1770139744; cv=none; b=cZrJxVhCyxZogvrOI+Bgu7Z+oRs7/EaIuQj7MB2LA4J5/C2yUSS15Oa4I3gRI+jQWDnBdWPX2Hic+vgwyAAyAU1qailcbPZ59Gi+eX0gShO+xLmxe0zEMD6yAO9Dgf8P7tUUQYuan+z/OBVhs1YKq5vlO6gUGHVE/dvqbrHH/PY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770139744; c=relaxed/simple; bh=mWyMx4Wb/HCw51vDYbHGsY4S6C9v30+HtBdi1q1SCSQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Sa9F41irhjodFU/M27/RZmNtMjgNWI8RwVK7sXvn1+UqF/QgPeosLLNEWzBIrmVM/VFjWWbPRormsJtaE28xKSxbmcnwvkEB/SWvuADsFOPbxNkK2a43GBJ9fEaRrs83e6iloOlF89C5lXCPkzEvnu7PNV1A0de2hx0P/LXAygY= 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=0oL+5ezK; 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="0oL+5ezK" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id 5345EC24386; Tue, 3 Feb 2026 17:29:01 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 10D3160728; Tue, 3 Feb 2026 17:28:56 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id A7D8A119A8891; Tue, 3 Feb 2026 18:28:52 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1770139734; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=KecJRPe2Bv70TIJpdknGkVxhofD7qopvizBIlkrUTDs=; b=0oL+5ezK79vBEpfRX9ZMmL99O5b7ITFB+focEqMtyIuOjKO0HftypZfxC+GmiV3wGeyUi+ cNq6JDVr+wFjGMmGwl2pyzaWGOuUED8TuwlsBkrseEX4/I42tfPlMcqFUy2X2I6q07HGUN ZqunLmyZVgbRNhv17NHnaOXAeR8jHrRzOalZJEJEG6mLwbqo3BJ/JlVu9i00dq1XqjLsUK KS4cE2lX+VAc/prdbkzM1huEoO0bbnzzCCxDoZ4TFJ8EIIyPYtiD7omwqPKT2Tm+KtkLWZ EcVQou92bUb9DOvavoBfUp56qddqW/7RGq90K/MMiRGAN6XN14R4sGtfsogORg== 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 Subject: [PATCH net-next v4 02/13] net: phy: phy_port: Cleanup the of-parsing logic for phy_port Date: Tue, 3 Feb 2026 18:28:26 +0100 Message-ID: <20260203172839.548524-3-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20260203172839.548524-1-maxime.chevallier@bootlin.com> References: <20260203172839.548524-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" We don't need to maintain a mediums bitfield, let's drop it and drop a bogus check for empty mediums, as we already check it above. Signed-off-by: Maxime Chevallier --- drivers/net/phy/phy_port.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/phy_port.c b/drivers/net/phy/phy_port.c index ec93c8ca051e..a269e9ea12c4 100644 --- a/drivers/net/phy/phy_port.c +++ b/drivers/net/phy/phy_port.c @@ -53,7 +53,7 @@ struct phy_port *phy_of_parse_port(struct device_node *dn) enum ethtool_link_medium medium; struct phy_port *port; const char *med_str; - u32 pairs =3D 0, mediums =3D 0; + u32 pairs =3D 0; int ret; =20 ret =3D fwnode_property_read_string(fwnode, "media", &med_str); @@ -85,17 +85,12 @@ struct phy_port *phy_of_parse_port(struct device_node *= dn) return ERR_PTR(-EINVAL); } =20 - mediums |=3D BIT(medium); - - if (!mediums) - return ERR_PTR(-EINVAL); - port =3D phy_port_alloc(); if (!port) return ERR_PTR(-ENOMEM); =20 port->pairs =3D pairs; - port->mediums =3D mediums; + port->mediums =3D BIT(medium); =20 return port; } --=20 2.49.0 From nobody Sun Feb 8 10:04:27 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 5426A2C235D; Tue, 3 Feb 2026 17:29:00 +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=1770139741; cv=none; b=CSzHkN8idUStdSnxYL+A8XJcytNvbbI8LXk0bqs8MysdGZ7LIHOjXWeLo+XHhEVQuw5yJ71eyoIZRbJiHLHTfctf+5NEho/ceTaK8/3yoc7uyYp5JOpNJ28U7rPzzNv/XH+yl1w/ExkRwC3FIEWp+9L7s4S4vCoL5mrYriRcse8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770139741; c=relaxed/simple; bh=OXv41CaCxkIeKEa8hsRowKmYkbdiMm3q+IPpNww8qsQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=gH9vzuajhJd95nr84wjGYZDH8JsSbIaUP+K1ncihhxuAMPkl1wnQ87wcjNXzNw1BzSZgwdtMRG5yp24i1qWuFH4zuDL441LnfEwbaekP1qUt4doxIL+d0kBUogZuEochrDa5JZnZt0zLCx8C+0HTWisI/evEv2ywAc1JTsEeQic= 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=he/m4SPG; 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="he/m4SPG" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 1B2141A2BC7; Tue, 3 Feb 2026 17:28:59 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id E4C5B60728; Tue, 3 Feb 2026 17:28:58 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 48D4D119A88F9; Tue, 3 Feb 2026 18:28:55 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1770139737; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=/OC/buRnlLfFzn+BhWblbXGZm2j74ao97s8f0goP980=; b=he/m4SPG6SwfImkVhRBQpI7KhmaWYNu+FaVBgKdu05GezgzzAhw5gWHHOML8LzabWyFEpR i9wyO1wjW9NluRUU4gZaEzjbMMrHyvaLCe4RmIeRY43pPAM+1Wr68orKGf/1NPpnW+hk9a tejbH/GC0+flU8t/uPVbsYWon4r+N0/MNjWlxDFuBoXhsWB5OYCqBdfLb7jiB2+DhFJ+gk ozKEPDOaQCQrEBOTwQqvyOKtLt7WSDF6QgYweDEYG2J50CVE5Umcru/d4Bi3ybk5eLAnIX Nlg9FTf8iXgM1WkdNMi5yFxVfRu7MNWdsrQ1TyoDGzPRSiJAlCJSgQPy9Cbe0A== 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 Subject: [PATCH net-next v4 03/13] net: phy: phy_port: Correctly recompute the port's linkmodes Date: Tue, 3 Feb 2026 18:28:27 +0100 Message-ID: <20260203172839.548524-4-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20260203172839.548524-1-maxime.chevallier@bootlin.com> References: <20260203172839.548524-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" a PHY-driven phy_port contains a 'supported' field containing the linkmodes available on this port. This is populated based on : - The PHY's reported features - The DT representation of the connector - The PHY's attach_mdi() callback As these different attrbution methods work in conjunction, the helper phy_port_update_supported() recomputes the final 'supported' value based on the populated mediums, linkmodes and pairs. However this recompute wasn't correctly implemented, and added more modes than necessary by or'ing the medium-specific modes to the existing support. Let's fix this and properly filter the modes. Fixes: 589e934d2735 ("net: phy: Introduce PHY ports representation") Signed-off-by: Maxime Chevallier --- drivers/net/phy/phy_port.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/phy_port.c b/drivers/net/phy/phy_port.c index a269e9ea12c4..ad2580529842 100644 --- a/drivers/net/phy/phy_port.c +++ b/drivers/net/phy/phy_port.c @@ -108,16 +108,10 @@ EXPORT_SYMBOL_GPL(phy_of_parse_port); */ void phy_port_update_supported(struct phy_port *port) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) =3D { 0 }; + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) =3D {0}; unsigned long mode; int i; =20 - for_each_set_bit(i, &port->mediums, __ETHTOOL_LINK_MEDIUM_LAST) { - linkmode_zero(supported); - phy_caps_medium_get_supported(supported, i, port->pairs); - linkmode_or(port->supported, port->supported, supported); - } - /* If there's no pairs specified, we grab the default number of * pairs as the max of the default pairs for each linkmode */ @@ -127,6 +121,23 @@ void phy_port_update_supported(struct phy_port *port) port->pairs =3D max_t(int, port->pairs, ethtool_linkmode_n_pairs(mode)); =20 + for_each_set_bit(i, &port->mediums, __ETHTOOL_LINK_MEDIUM_LAST) { + pr_info("Adding mode for medium %d pairs %d\n", i, port->pairs); + __ETHTOOL_DECLARE_LINK_MODE_MASK(med_supported) =3D {0}; + + phy_caps_medium_get_supported(med_supported, i, port->pairs); + linkmode_or(supported, supported, med_supported); + } + + /* If port->supported is already populated, filter it out with the + * medium/pair support. Otherwise, let's just use this medium-based + * support as the port's supported list. + */ + if (linkmode_empty(port->supported)) + linkmode_copy(port->supported, supported); + else + linkmode_and(port->supported, supported, port->supported); + /* Serdes ports supported through SFP may not have any medium set, * as they will output PHY_INTERFACE_MODE_XXX modes. In that case, derive * the supported list based on these interfaces --=20 2.49.0 From nobody Sun Feb 8 10:04:27 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 5BE68326921 for ; Tue, 3 Feb 2026 17:29:03 +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=1770139744; cv=none; b=hYtc5AQB7iIJiXepY1IGsFx/ubSS6IX8I9l+khbwwGcb2b0nn2R5HQoi9MnQk74nfciVIvMLrpuCSTUqnkMFZQvlPRcgG20Toaw/yjYzTgqpgkvAAiQL+UdzYAPVwRira6E504KKevvJBOdVQhxfxYLXhlFp7sHMSN/issFnKfk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770139744; c=relaxed/simple; bh=FzfPssaMTtMlTXsQB3uWK2d1KPWw1AhO9CiOrY0mA4o=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=feHel3YAvhsQyhf7GjMrzC55DZCoHGLFEPkGksP+wCPX5OKhxHaoxXfLhz2SIR49dRZv6+cpKJ2zDuNfaGIWfk+bOh6OGgMGjW0eOe5qODDrAFhieo/caW+bf2DKgfNv1ttlJgGFjj+ROZ9xD/NUH4W1jtsdhlrlSX8QxIHlHEY= 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=j/ei7iPV; 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="j/ei7iPV" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 164671A2BC5; Tue, 3 Feb 2026 17:29:02 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id DFC8360728; Tue, 3 Feb 2026 17:29:01 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 1F98B119A8888; Tue, 3 Feb 2026 18:28:58 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1770139740; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=LWzrt0LzFGZIeHx1tJakp6EzeCmz9kccT2ixoT2csH8=; b=j/ei7iPVujZdtSqt2MVqSKmjWTR9qur2AkBCVkW54acroTYt60S5FKffooCvAtBwB18Nji IeemVRcuz9ofpGMHH04AA3SKqj+RMPw6MWx3KGajG9I3s+mfQkQfZVnnoYwvvqzXIr2LOw 8YP68oFk4TX/goRnKx2OzRVeYobxvR46NiUZxerW5brdLJ10AMcDDwngIPoy/Uj81r3A3t PC/rz4OwIUnb/zlpj718LGsNNiFrM+Rzi14LqpsW0tMjI5ANAqOnC5aULDoaqqphMZIfUh 31CucLhBvmLIjTrk+aPVbYXsnNHBygED7M51qrUnwXZZt+dO9e5O1pjpyx84Nw== 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 Subject: [PATCH net-next v4 04/13] net: phy: phy_link_topology: Add a helper for opportunistic alloc Date: Tue, 3 Feb 2026 18:28:28 +0100 Message-ID: <20260203172839.548524-5-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20260203172839.548524-1-maxime.chevallier@bootlin.com> References: <20260203172839.548524-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. Signed-off-by: Maxime Chevallier --- drivers/net/phy/phy_link_topology.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/net/phy/phy_link_topology.c b/drivers/net/phy/phy_link= _topology.c index 0e9e987f37dd..7dc2ff10c74a 100644 --- a/drivers/net/phy/phy_link_topology.c +++ b/drivers/net/phy/phy_link_topology.c @@ -27,21 +27,34 @@ 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. + */ + 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 - 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(sizeof(*pdn), GFP_KERNEL); if (!pdn) --=20 2.49.0 From nobody Sun Feb 8 10:04:27 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 77D543033D8 for ; Tue, 3 Feb 2026 17:29:06 +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=1770139748; cv=none; b=R9Ipv+nmMD2xA8BCo8HtEnf1miJrB9071vttrrILdfBr7adkfQMA1tADiBayN8cfLJ9EaqIW1VB4r2ZfpSA3CydMfJvLke5m8T2hA3rV5bzwGlmMpUvH53oAfDRn2q9Z1PtORBVPA5X9bRL1lky5Oxfyf0JuIJTNn0mrxfcyZ8s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770139748; c=relaxed/simple; bh=ceTw3Y+2k1owesKq9VRkvE3lyH9jPSiyaxkXTcGp/Vk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=g9PhFK2ULv0e0+AAAFQUG3/ne1+/Sjq96TLqHTAJGUxyJ+Il5mNsiWnFIJt3hbtxhKIyPkbQi7WEKHtKs3qTQUkrUHLo1YZMWrDsGsUX695SrcFrWYLr+XyWaKYFFwhkF3mfzTE6Y84rRlKfn2+b8XMtiBXrNCuGfFp4zdKVoPc= 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=2Qo+Fxox; 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="2Qo+Fxox" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-04.galae.net (Postfix) with ESMTPS id 56027C24384; Tue, 3 Feb 2026 17:29:10 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 11E2560728; Tue, 3 Feb 2026 17:29:05 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 2CF2C119A88F8; Tue, 3 Feb 2026 18:29:01 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1770139743; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=IRk0K+iuT7PNYANBHukpfnb6ronXpCa6nfbjQJcF9QU=; b=2Qo+Fxox7KBPLX7Nqfyo+aMZtklsrcVkFQo+aaixjG7LQ9Iy+Sc+kqXXr5Gy1jtMKUWjeu fgIYcDZTkCV9IHyAZ0IwY02GVAKeQ22DiTg1XaA2WWa58iJlciNwKz/rpnwtfXcHPr+VDo L/k1gYDhN3EHlwIAy8IlHV/ONG65ngc0khrtpC2m/P/Pi0B/z7x7hHPthFWmdI46VQMQl2 Wh0Nd9HkvxSuzC5Z+zyzlVrKnVw8mD7Z6tAdqnUOAfqS0S4iDqkhK7gYGTJsFnKOIYEOZA LZpDVRBidCWffL/9s8pEAH54JWr+k0GoB9XoXtf5FxB9gAs9VWvpBPv/HAYOKw== 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 Subject: [PATCH net-next v4 05/13] net: phy: phy_link_topology: Track ports in phy_link_topology Date: Tue, 3 Feb 2026 18:28:29 +0100 Message-ID: <20260203172839.548524-6-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20260203172839.548524-1-maxime.chevallier@bootlin.com> References: <20260203172839.548524-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. Signed-off-by: Maxime Chevallier --- 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 + 4 files changed, 74 insertions(+) diff --git a/drivers/net/phy/phy_link_topology.c b/drivers/net/phy/phy_link= _topology.c index 7dc2ff10c74a..456f1144e83e 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 @@ -22,6 +23,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; @@ -44,12 +48,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; +} +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 topo =3D phy_link_topo_get_or_alloc(dev); @@ -89,8 +126,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_from_reverse(port, &phy->ports, head) + phy_link_topo_del_port(dev, port); + + xa_erase(&topo->phys, phy->phyindex); err: kfree(pdn); return ret; @@ -102,10 +151,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 68a59e25821c..66bceff72b19 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 { @@ -43,6 +47,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) { @@ -72,6 +79,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 43de5af0d6ec..fe2dfcc95400 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -11277,6 +11277,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.49.0 From nobody Sun Feb 8 10:04:27 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 5CDD63D522A for ; Tue, 3 Feb 2026 17:29:09 +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=1770139750; cv=none; b=Sqo4r98AFSSeN0Iq/B0LRdZeNqooh6WNwgukZ5nj57JzWfUgGB5Uv6f0/ErAtIcaOUqxqFlR7aJv6AO01+dnnKaHxUPCnK1gZk9L9kNdmsZHgni+Tze1N40/L2GdNNqV9jAlPZqWBkW5wBmyC+ZHeALV1YSQQkfef1Vy5Ot5cSw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770139750; c=relaxed/simple; bh=kxYprBgxtxpbPjIF2eIjiXbk7fRIQtp2OGCSGG0bxyU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ag/xv4wtVAkk6+F6Dlncv3klfg7kIB0KaNM2CX0et258JiCB5YKjiXHnBBQeELcthWy7wI60yOLbjbaEjtY9AqIRSbYaz3qhrWF0aq6xT+p5qwFkderubf530oSwOO3z/hO9soPoo1yWq3OT9HA2KStT9ocH3Mciibz+4J2B9Fk= 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=xZMgkmep; 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="xZMgkmep" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 20E811A2BC5; Tue, 3 Feb 2026 17:29:08 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id E6DE760728; Tue, 3 Feb 2026 17:29:07 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 11DDB119A88FF; Tue, 3 Feb 2026 18:29:03 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1770139746; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=9VMJCSTaZs0qZ6Spe9WxRX/vem2r/rMxnfYcMsORs4w=; b=xZMgkmep7uj1kHyhIDYZ5V1XcTDtVjt0RQms+3rLioJOmz+j2N/UCt4RXR2G62ubSVzvSh Gctz9Iln/fN28oS69I+oKgrMb79S3TB9OX2VOFV3YBhHDMQ68bX+4l9Sddbf5bGBiuVB3w EBLghO7QjOUISnxX+lYyBq8J0MJUJsaS7kVGIlU9Lq7MnQ4BHkztL2BKPf0eF/voVE9MPN UN8qrp0jyCt7qnYrbBm1TzsewzxxM+Gi/6MKagJx6UyGfYmJoLUzcL/qQBzSDAzHHVXvKy 3DTrz6ZS/7/bFstJ+kXAbn0X1/XBk/MlMIyaQBE2zqo5ihU1gnXRhPD6IDCAkA== 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 Subject: [PATCH net-next v4 06/13] net: phylink: Register a phy_port for MAC-driven SFP busses Date: Tue, 3 Feb 2026 18:28:30 +0100 Message-ID: <20260203172839.548524-7-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20260203172839.548524-1-maxime.chevallier@bootlin.com> References: <20260203172839.548524-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 driver 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. Signed-off-by: Maxime Chevallier --- drivers/net/phy/phylink.c | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index e1f01d7fc4da..7a07de1d42bb 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_bus_port; =20 struct eee_config eee_cfg; =20 @@ -1758,6 +1761,46 @@ 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_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_bus_port =3D port; + + return ret; +} + +static void phylink_destroy_sfp_port(struct phylink *pl) +{ + if (pl->netdev && pl->sfp_bus_port) + phy_link_topo_del_port(pl->netdev, pl->sfp_bus_port); + + if (pl->sfp_bus_port) + phy_port_destroy(pl->sfp_bus_port); + + pl->sfp_bus_port =3D NULL; +} + static int phylink_register_sfp(struct phylink *pl, const struct fwnode_handle *fwnode) { @@ -1775,9 +1818,18 @@ static int phylink_register_sfp(struct phylink *pl, =20 pl->sfp_bus =3D bus; =20 + ret =3D phylink_create_sfp_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_port(pl); + return ret; } =20 @@ -1939,6 +1991,7 @@ EXPORT_SYMBOL_GPL(phylink_create); void phylink_destroy(struct phylink *pl) { sfp_bus_del_upstream(pl->sfp_bus); + phylink_destroy_sfp_port(pl); if (pl->link_gpio) gpiod_put(pl->link_gpio); =20 --=20 2.49.0 From nobody Sun Feb 8 10:04:27 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 C6F2E3D5249; Tue, 3 Feb 2026 17:29:11 +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=1770139753; cv=none; b=ZcoOdL5/WqDJ1+4f0hVP/PqN6y1Rth5oRF+8yPBa2MZE06bL4TO6jS8HIACRWGV5v94TjnPB/82slauBTS1V2Ggwo7t/RI+yjXKjg5GphkpV0hPWV/ETLAtpe1T1HWd+8d5frF3zAMKNBXG7C5fkVMjeAaB4D3z08r1nxT9nWDY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770139753; c=relaxed/simple; bh=cMWmsJsTCCclDJN4POGmYUlnnYV29W+lsKydlQUZvEc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=qfZ6AbpWoUxVUtWxKoi6I5O/2EE9mTZ/veyjMvr/ATTEg2ctsB+thGyMB6E1AtyTt0IvnGmJ5r63tU+VsfvTTV/VmBThFvCr8XmTgOIiBLUbIVb4sxGSQrQ7m6smwu+Un12RZAjziYZHSV1Y7siKgebjQ0sYxYnpqApqWIVClrU= 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=jmcCuCPI; 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="jmcCuCPI" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-03.galae.net (Postfix) with ESMTPS id 6E5B14E423E7; Tue, 3 Feb 2026 17:29:10 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 3FD5060728; Tue, 3 Feb 2026 17:29:10 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id E4D9A119A88F7; Tue, 3 Feb 2026 18:29:06 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1770139749; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=lcFBegye7Q2h6C5rnPUpxufhF5WGGgfujHXqDQjkhIY=; b=jmcCuCPIjY/ml5i5EW1HFkrRRGIET8pqjx5NVsP+Jg0zJyHwrwbiwrbJzXxf+jGjkAVGct tpXdxvqWDaE3lruA/ptw3x9ZS5RUTMTfwoSDwwwBfQGxkVMY34VNpHINN1U+3HcO2RW145 Rzts+MACbaYnueFH1D0scshBJoap/eK5t8XMI4/D8hldIBqjYTEGYSLZsw9FkCshdKPHNs wLjpOe3riXmP9GZX9SS9+pDNU0Jl1RGiu2Y7cKsv1ZA1HoGVia7hMC54+3l44UpNAuV2Tq N+tdQSbSoUEUINpy4qhQqr1oCeFKqDFGgoWu4Z87XBOeVV/1jzLjua+hm8ErYA== 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 Subject: [PATCH net-next v4 07/13] net: phy: Create SFP phy_port before registering upstream Date: Tue, 3 Feb 2026 18:28:31 +0100 Message-ID: <20260203172839.548524-8-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20260203172839.548524-1-maxime.chevallier@bootlin.com> References: <20260203172839.548524-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. Signed-off-by: Maxime Chevallier --- drivers/net/phy/phy_device.c | 42 ++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 9b8eaac63b90..b0971f57686c 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1665,13 +1665,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; @@ -1686,10 +1686,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 /** @@ -1698,22 +1700,34 @@ static int phy_setup_sfp_port(struct phy_device *ph= ydev) */ static int phy_sfp_probe(struct phy_device *phydev) { + struct phy_port *port; 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; + + bus =3D sfp_bus_find_fwnode(phydev->mdio.dev.fwnode); + if (IS_ERR(bus)) + return PTR_ERR(bus); =20 - phydev->sfp_bus =3D bus; + phydev->sfp_bus =3D bus; =20 - ret =3D sfp_bus_add_upstream(bus, phydev, &sfp_phydev_ops); - sfp_bus_put(bus); + if (bus) { + port =3D phy_setup_sfp_port(phydev); + if (IS_ERR(port)) { + sfp_bus_put(bus); + return PTR_ERR(port); + } } =20 - if (!ret && phydev->sfp_bus) - ret =3D phy_setup_sfp_port(phydev); + ret =3D sfp_bus_add_upstream(bus, phydev, &sfp_phydev_ops); + sfp_bus_put(bus); + + if (ret && port) { + phy_del_port(phydev, port); + phy_port_destroy(port); + } =20 return ret; } --=20 2.49.0 From nobody Sun Feb 8 10:04:27 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 51A603D6481 for ; Tue, 3 Feb 2026 17:29:14 +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=1770139756; cv=none; b=DtNmADqw+YF/KcrNusIUbBUDxm90iIBeCS3LAzFWhiKpfG6xvm6Mr1jULXjZmsJX8hDErhi/x9xzEX2s4WBSQEbCD5r+IZ4tyo4hbPB8exhNqKi+LgMhx2azaYAzXOPlKurRRd/I+nycLv8gKNWJFnNOIyfcaIFNZDFGEeXlfZY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770139756; c=relaxed/simple; bh=87MFXiFkk57mMapgG9uoW13QrYmOd3C+FJSgUizKqEE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IeGv3wVtc9xRpWtOta07vp7COOMAMeL9Ul2hzI+LfPt2QaLNoxWp6gUvdUtAvA2cgLaPxOkKU4RcCO+DijkfamYIqLYbqqu5IpjRe3oLNnVLIswvWkZlU+mNepBS9RupghdAAa+Jwz+lHEWMs3P8jq3w0OY4cn5c4IzZ57Dgvd8= 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=SgB/IdUU; 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="SgB/IdUU" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 378D11A2BC5; Tue, 3 Feb 2026 17:29:13 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 0131660745; Tue, 3 Feb 2026 17:29:13 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 77A36119A88EE; Tue, 3 Feb 2026 18:29:09 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1770139751; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=ilIeijsxZ5skMN8uWsWhLA/1VS0G2rhNiOAud4MFSuY=; b=SgB/IdUUsd4HDbCaefeariYXqszs1nv8cBvPt9KNt92ZZe/GoEjqW6q7y/UKMZEHPnrq1B pKy1tTXvVaYjMsYHHnAr8Dqqf6rOanbJV6pAtymg6qVd3hqY9PSyC2T7BvzTkOx9JLrQCo /Wz/B+MpmZnHrT+BxkLt1qiJxGBZqh/VyyN1SFw85X9/3oCEWduBxpSkW6D2IjwnbQK9OC qjRlYQw1n3ygxFh016yY8hUgTtowyVx7G6djwW8FP4hMYwHkTLkSnSxNrvpld4DOEqltuL 39oDI1PKvPJ79ZjJaHkYfYR+lt7W3EofdmPjsSdUCLfPc5JHQO5R+Bw/rrpJkg== 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 Subject: [PATCH net-next v4 08/13] net: phy: Represent PHY-less SFP modules with phy_port Date: Tue, 3 Feb 2026 18:28:32 +0100 Message-ID: <20260203172839.548524-9-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20260203172839.548524-1-maxime.chevallier@bootlin.com> References: <20260203172839.548524-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. Signed-off-by: Maxime Chevallier --- drivers/net/phy/phy-caps.h | 2 + drivers/net/phy/phy_caps.c | 26 +++++++++++ drivers/net/phy/phy_device.c | 88 +++++++++++++++++++++++++++++++++++- include/linux/phy.h | 6 +++ 4 files changed, 121 insertions(+), 1 deletion(-) 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 b0971f57686c..0daa87413c66 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1483,6 +1483,8 @@ static int phy_sfp_connect_phy(void *upstream, struct= phy_device *phy) struct phy_device *phydev =3D upstream; struct net_device *dev =3D phydev->attached_dev; =20 + phydev->has_sfp_mod_phy =3D true; + if (dev) return phy_link_topo_add_phy(dev, phy, PHY_UPSTREAM_PHY, phydev); =20 @@ -1504,6 +1506,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); } @@ -1609,6 +1613,74 @@ 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_bus_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; + + 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, @@ -1618,6 +1690,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) @@ -1700,7 +1774,7 @@ static struct phy_port *phy_setup_sfp_port(struct phy= _device *phydev) */ static int phy_sfp_probe(struct phy_device *phydev) { - struct phy_port *port; + struct phy_port *port =3D NULL; struct sfp_bus *bus; int ret; =20 @@ -1727,8 +1801,11 @@ static int phy_sfp_probe(struct phy_device *phydev) if (ret && port) { phy_del_port(phydev, port); phy_port_destroy(port); + port =3D NULL; } =20 + phydev->sfp_bus_port =3D port; + return ret; } =20 @@ -1818,6 +1895,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 @@ -1991,6 +2074,8 @@ void phy_detach(struct phy_device *phydev) 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; @@ -3821,6 +3906,7 @@ static int phy_remove(struct device *dev) =20 sfp_bus_del_upstream(phydev->sfp_bus); phydev->sfp_bus =3D NULL; + phydev->sfp_bus_port =3D NULL; =20 if (phydev->drv && phydev->drv->remove) phydev->drv->remove(phydev); diff --git a/include/linux/phy.h b/include/linux/phy.h index 6f9979a26892..9114fdff4c4f 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -582,6 +582,7 @@ struct phy_oatc14_sqi_capability { * @wol_enabled: Set to true if the PHY or the attached MAC have Wake-on-L= AN * enabled. * @is_genphy_driven: PHY is driven by one of the generic PHY drivers + * @has_sfp_mod_phy: Set true if downstream SFP bus's module contains a PHY * @state: State of the PHY for management purposes * @dev_flags: Device-specific flags used by the PHY driver. * @@ -594,6 +595,8 @@ struct phy_oatc14_sqi_capability { * @phylink: Pointer to phylink instance for this PHY * @sfp_bus_attached: Flag indicating whether the SFP bus has been attached * @sfp_bus: SFP bus attached to this PHY's fiber port + * @sfp_bus_port: The phy_port connected to the downstream SFP bus + * @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. @@ -704,6 +707,7 @@ struct phy_device { unsigned irq_rerun:1; =20 unsigned default_timestamp:1; + unsigned has_sfp_mod_phy:1; =20 int rate_matching; =20 @@ -782,6 +786,8 @@ struct phy_device { /* This may be modified under the rtnl lock */ bool sfp_bus_attached; struct sfp_bus *sfp_bus; + struct phy_port *sfp_bus_port; + struct phy_port *mod_port; struct phylink *phylink; struct net_device *attached_dev; struct mii_timestamper *mii_ts; --=20 2.49.0 From nobody Sun Feb 8 10:04:27 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 7FFC33D6484 for ; Tue, 3 Feb 2026 17:29:17 +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=1770139758; cv=none; b=CeT2GxfEg3TPhmM+k0Lkk94WIXfL1f63DvrJ04fNRhnLscLxm9xR7wgNwZsQtmpPXMaBPH9kKP9m8Qn3CATvp2OXy9GFtd7Ep8a6O0xGbiLch0PeYlDerNJO0oMhdBEwYDiqj3u2vwANOxZGtf44E8W80KszHs96gXDW4RR2ciY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770139758; c=relaxed/simple; bh=hxU9uTphzVdXJ546kTYHYBQ7irsclrcINT2p/qgcf/g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mqWA1zHUxF4L4SjOBYFkDTvWr2RZjLY78gvIXq4QEtQtU5dSY2Tc3nINMXAOQbQr+akFkwkkCdgagITP+FC5tsRGvMZNl0ahEGaMFmsOb/zUlMgnHYmElPfvpnaq8GGQWGHcIvw/hAKVshRWwAizG+cunLabLldAcF9/3kQOaO0= 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=Hk23vlIG; 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="Hk23vlIG" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-03.galae.net (Postfix) with ESMTPS id 1F4A84E423E3; Tue, 3 Feb 2026 17:29:16 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id E7D3260728; Tue, 3 Feb 2026 17:29:15 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 3CDFB119A88FB; Tue, 3 Feb 2026 18:29:12 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1770139754; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=bRI683hcvnEM35da1o7ilYv7pwt3yyezbBrMJM8t6dQ=; b=Hk23vlIGaKQSCFCsWMYeTx/2kVHZslTepJTqcO56IlWaH+srUMTvsUDMyrJJahalVHFqyh IZGJzPAfQouVNY2hFBxA8ovHD4vCJ/gIYvz08dRwWgZb6gAXHcC4xaauQSe6CFnxAujlYi 5Yl6SeXZNIdA45j/5VdkS0fasTNXPmi6c5u88x6V9ox7uPwi9WGDBVyKSaJdo2dquH7mCP VQJCfCNVmSLNVV/bBHIQsyVQhAJdbmjyCCshZiXZsmP7aGGsCa3lH6CxpvWHDqm3piX9zL DuLpLBQmovPrnbGc8+8rdO0FGxsH2eCL+H5y2FUnzPTHQLjUYz2spv8HMZbNtg== 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 Subject: [PATCH net-next v4 09/13] net: phylink: Represent PHY-less SFP modules with phy_port Date: Tue, 3 Feb 2026 18:28:33 +0100 Message-ID: <20260203172839.548524-10-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20260203172839.548524-1-maxime.chevallier@bootlin.com> References: <20260203172839.548524-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" Let phylink handle the phy_port for PHY-less modules, and register it to the topology. Signed-off-by: Maxime Chevallier --- drivers/net/phy/phylink.c | 76 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 7a07de1d42bb..d65fa6cd1ebb 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_bus_port; + struct phy_port *mod_port; =20 struct eee_config eee_cfg; =20 @@ -1787,13 +1788,31 @@ static int phylink_create_sfp_port(struct phylink *= pl) else pl->sfp_bus_port =3D port; =20 + if (pl->mod_port) { + ret =3D phy_link_topo_add_port(pl->netdev, pl->mod_port); + if (ret) + goto out_bus_port; + } + + return 0; +out_bus_port: + phy_link_topo_del_port(pl->netdev, port); + phy_port_destroy(port); return ret; } =20 static void phylink_destroy_sfp_port(struct phylink *pl) { - if (pl->netdev && pl->sfp_bus_port) - phy_link_topo_del_port(pl->netdev, pl->sfp_bus_port); + if (pl->netdev) { + if (pl->sfp_bus_port) + phy_link_topo_del_port(pl->netdev, pl->sfp_bus_port); + + /* Only remove it from the topology, it will be destroyed at + * module removal. + */ + if (pl->mod_port) + phy_link_topo_del_port(pl->netdev, pl->mod_port); + } =20 if (pl->sfp_bus_port) phy_port_destroy(pl->sfp_bus_port); @@ -3919,14 +3938,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_bus_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_bus_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 @@ -3945,6 +4015,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) --=20 2.49.0 From nobody Sun Feb 8 10:04:27 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 0A6B73D6674; Tue, 3 Feb 2026 17:29:19 +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=1770139761; cv=none; b=DB1iYoeUFJrzoDicxcNexg6sSUQXOLU85flJd5rzxwloW0ihGynz42LEjAL+CO2KbIw/i0DsLtJmFHJFj7WUCouWEMtZ2TiOnYFTaLh3UsGp5eKNgewANVVGCPe1A9FRDP52aMh9lAjpZS0ag+KhhP56oGTIVnpTGcMQyMvsWP0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770139761; c=relaxed/simple; bh=KlAsEWvA3363FrNS+2Nlte/PjM1rH7VgyQaAVPvlnR4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=aS1w6Nk5VcYs2EV1J3X5ESPzFj/bjI03aM+RlZNb/8sC2Njk8HsFLQ/NjZ6RRDpa6Mvq0GIE5R9/s8p9yzBL4FAjlKuMnm7AVDJ50ji3FtBjhCIBO355Tc2wfuvix9fd/dldCyebf7wjIKb8u9QB/kfBMYc1ZaZ93PgkSyJlgGs= 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=VxxkHQJc; 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="VxxkHQJc" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id CF2801A2BC4; Tue, 3 Feb 2026 17:29:18 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 9D7C260728; Tue, 3 Feb 2026 17:29:18 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 1D8DE119A88FE; Tue, 3 Feb 2026 18:29:15 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1770139757; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=WQTc03wXVeXzBiPDeKo+9AlpFri0HZrwFw5n6xQambk=; b=VxxkHQJcWeeu/V89DCIhiGVmFgWLt41kU4scYsJnQrwUJvk2A+GgGA7++OPpuUUczEZ0q/ gDS6NpZ97kZltERY43UrUwSFunO/52atndz2GHpDYj2tp9XionzYqJxIE/ReLgzM+F114Y HpvUqA0oPXb4HSGppEGaDzd7vKmcmJQQGeeREfA0pfzFQbViuMTctG0hyPfKcNPN9HUeGA vsSJTkUzBU7kG9xsncPVjc0LyBUjU7Erms/3fYEXrrYYa/2W+NTGj9O8NZJTpMy4wvuYOX I1tWQNqwmzj6fpr6Dcb5T2syUKvXldGjondiXs2D6UTjw+RdY06CNB3VZDnTEw== 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 Subject: [PATCH net-next v4 10/13] net: phy: phy_port: Store information about a MII port's vacant state Date: Tue, 3 Feb 2026 18:28:34 +0100 Message-ID: <20260203172839.548524-11-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20260203172839.548524-1-maxime.chevallier@bootlin.com> References: <20260203172839.548524-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, so far we only support SFP modules for that. We have information about what MII they can handle, however we don't store anything about whether they are currently connected to an SFP module or not. As phy_port aims at listing the front-facing ports, let's store an "vacant" bit to know whether or not a MII port is currently vacant (i.e. there's no module in the SFP cage), or not (i.e. there's an SFP module). Signed-off-by: Maxime Chevallier --- drivers/net/phy/phy_device.c | 11 +++++++++-- drivers/net/phy/phylink.c | 6 ++++++ include/linux/phy_port.h | 4 ++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 0daa87413c66..4ce8b6baf3a1 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1580,6 +1580,8 @@ static int phy_sfp_module_insert(void *upstream, cons= t struct sfp_eeprom_id *id) if (port->ops && port->ops->configure_mii) return port->ops->configure_mii(port, true, iface); =20 + port->vacant =3D false; + return 0; } =20 @@ -1588,8 +1590,12 @@ static void phy_sfp_module_remove(void *upstream) struct phy_device *phydev =3D upstream; struct phy_port *port =3D phy_get_sfp_port(phydev); =20 - if (port && port->ops && port->ops->configure_mii) - port->ops->configure_mii(port, false, PHY_INTERFACE_MODE_NA); + if (port) { + if (port->ops && port->ops->configure_mii) + port->ops->configure_mii(port, false, + PHY_INTERFACE_MODE_NA); + port->vacant =3D true; + } =20 if (phydev->n_ports =3D=3D 1) phydev->port =3D PORT_NONE; @@ -1755,6 +1761,7 @@ static struct phy_port *phy_setup_sfp_port(struct phy= _device *phydev) */ port->is_mii =3D true; port->is_sfp =3D true; + port->vacant =3D true; =20 /* The port->supported and port->interfaces list will be populated * when attaching the port to the phydev. diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index d65fa6cd1ebb..95bd86d43f19 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -3924,6 +3924,9 @@ static int phylink_sfp_module_insert(void *upstream, pl->sfp_may_have_phy =3D caps->may_have_phy; pl->sfp_port =3D caps->port; =20 + if (pl->sfp_bus_port) + pl->sfp_bus_port->vacant =3D false; + /* If this module may have a PHY connecting later, defer until later */ if (pl->sfp_may_have_phy) return 0; @@ -3935,6 +3938,9 @@ static void phylink_sfp_module_remove(void *upstream) { struct phylink *pl =3D upstream; =20 + if (pl->sfp_bus_port) + pl->sfp_bus_port->vacant =3D true; + phy_interface_zero(pl->sfp_interfaces); } =20 diff --git a/include/linux/phy_port.h b/include/linux/phy_port.h index 4e2a3fdd2f2e..b0b1fa6a67a9 100644 --- a/include/linux/phy_port.h +++ b/include/linux/phy_port.h @@ -51,6 +51,9 @@ struct phy_port_ops { * @is_mii: Indicates if this port is MII (Media Independent Interface), * or MDI (Media Dependent Interface). * @is_sfp: Indicates if this port drives an SFP cage. + * @vacant: For MII ports, indicates whether or not the port has a connect= ion to + * another device (e.g. for SFP ports, indicates the absence or presen= ce + * of an SFP module) */ struct phy_port { u32 id; @@ -71,6 +74,7 @@ struct phy_port { unsigned int active:1; unsigned int is_mii:1; unsigned int is_sfp:1; + unsigned int vacant:1; }; =20 struct phy_port *phy_port_alloc(void); --=20 2.49.0 From nobody Sun Feb 8 10:04:27 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 A6C653D668E for ; Tue, 3 Feb 2026 17:29:23 +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=1770139765; cv=none; b=QQ6ZRESzoxfgfEcHSIRQVYKy0/ak4+hW+iSmvQKqJV1H6PmtHhKqsTLoWbjo2PJnypXMdtHFS+7YAhrLolRjd6QrvrX0udrxcOfvFBy800hgJcSfubu6vT7uBle4jjd+hANZ1dAsq6gj1r4jLS9uAnLlh5ytPZWCknQ42zJbXAw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770139765; c=relaxed/simple; bh=f0/3/mrCPrJHqIVyCbu48EYwHwbuoZwmfW42AbHjWH0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=u/a0aLHjsxP82pIYIdnkzrNzRqPs+WIVgcl0N/W6RNODJ27iglizXxwunxqIp9wfXSJtqW5hB1FW0WVNjnDgklk6Z06095xZyfCBOM8h55ZA88kfAOH9KfbKOsKbsLxxocB6ZRfabi5skk8ENIQcwHbChUSwgH1hKpSG5WFXDIg= 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=f/yU4DiL; 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="f/yU4DiL" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-03.galae.net (Postfix) with ESMTPS id 5ABC44E423E4; Tue, 3 Feb 2026 17:29:22 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 2D88E60745; Tue, 3 Feb 2026 17:29:22 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id B6159119A8888; Tue, 3 Feb 2026 18:29:17 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1770139760; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=oE3YHgEOb7zMldz7W5UEO4lNk/cteuFmDnyVlbi0dJs=; b=f/yU4DiLyOzT8BvaPzw3kWJj+kxMQaiHRR9TZC0DQI8pYnkd8eGk7kr+Kg9N3dcPaFVnjc 9KhPxKc6Qu5945dqqEyfV6y5GFOZbV40GBaHv7rxzVVfWmEHPtUYOw5KtcK9XYMOMceGtF pBZILeQtTEi1crlas/XodVDRPbqUzOb7PCA/4+oJAQgwZWWkc+BV7RXL+4wY7MCE0EivV0 cp1MNK7TNpe9Jj5yJdIlSGguZb9wNbieK8cDZ9tRCscgLP4ox4tD68iiyQrETE+FDl2l8c xV7xSSoI69VR3s4L2SnEuYVBPqlKFKAwUEluIBydnOY79fi5nb8N+yBlgqmcwA== 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 Subject: [PATCH net-next v4 11/13] net: phy: phy_link_topology: Add a helper to retrieve ports Date: Tue, 3 Feb 2026 18:28:35 +0100 Message-ID: <20260203172839.548524-12-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20260203172839.548524-1-maxime.chevallier@bootlin.com> References: <20260203172839.548524-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. Signed-off-by: Maxime Chevallier --- include/linux/phy_link_topology.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/include/linux/phy_link_topology.h b/include/linux/phy_link_top= ology.h index 66bceff72b19..ac2f5c80f1a6 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; @@ -66,6 +67,20 @@ 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; + + ASSERT_RTNL(); + + if (!topo) + return NULL; + + /* Caller must hold RTNL while handling the phy_port */ + return xa_load(&topo->ports, port_id); +} + #else static inline int phy_link_topo_add_phy(struct net_device *dev, struct phy_device *phy, @@ -95,6 +110,12 @@ phy_link_topo_get_phy(struct net_device *dev, u32 phyin= dex) { 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.49.0 From nobody Sun Feb 8 10:04:27 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 EDB69361660 for ; Tue, 3 Feb 2026 17:29:25 +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=1770139767; cv=none; b=nwBI++48cVqKZG59ast5IXgXJlyS8gSUDLD3er1yWKg/8q9Ee3edPKILxIoQ+UZJoctjqRn3AFV+K4W7Yh/vbZQ7hv7DVpNS2walaig+Is7D7p1RHZ8IKJ9361WJxjp4q2x5x4m+cbmcyDOLwlO8VE/fdWWrGd6IJPMy0RRw0+M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770139767; c=relaxed/simple; bh=JmeyZP2XJnadfC35COHDh6HiAn7o8KK+VXUud0kFGYU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LzXOZ6es1ZEJXaTs5SlW0U+AJuEJ9F9YJkA0obVGVIrAsb4OAq3JNSvXM4EJDRYW/YieMTfWgnIVH4N48fc6RKFyOnwyzwdChTS0e9yttXfRtrjPmpKEImEHxc5VYXIeWAGUeo1NiimtN2eSXotXMaREpK6eFIaphffT9uLnn/M= 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=XA1JRQJg; 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="XA1JRQJg" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-03.galae.net (Postfix) with ESMTPS id 9843D4E423E6; Tue, 3 Feb 2026 17:29:24 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 68C2C60728; Tue, 3 Feb 2026 17:29:24 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 260F8119A8891; Tue, 3 Feb 2026 18:29:21 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1770139763; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=fV05yTnaZQbr3/Mc/jXTwhNoF6lv/b+UIVWjOaPX+ZQ=; b=XA1JRQJgnIb9934pEXM/UfvWJy4d8BRZRpiOGLTMv7Zc2KpvDLb/L1mEq5YZz2/IPsdZX/ DhCKgkmcktdCkmVKcuUGWkN7nw5mPkXhDSdlNdJ6/JyFLP12zRClc00UeCERg85MGb9jDA +LF1vCw7QpIpSo3uE+y5BNv+cJmbMRoYbTkeuecykZwb4+D5Z2TG7paItpqjnIAqe+f0IP n5iN63ZGzoS7uA3HrLP1QkaGJ2lwmOxX7KwZvwE41gtMA4Uir6U55DMv9U8tfWarF5d/Y+ cBjeQvSm9NWJjqFxslrVI7CUlD0t7YB9mVlHxkadxyrl5k58JshrZT31/Dgd9Q== 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 Subject: [PATCH net-next v4 12/13] netlink: specs: Add ethernet port listing with ethtool Date: Tue, 3 Feb 2026 18:28:36 +0100 Message-ID: <20260203172839.548524-13-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20260203172839.548524-1-maxime.chevallier@bootlin.com> References: <20260203172839.548524-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. Signed-off-by: Maxime Chevallier --- Documentation/netlink/specs/ethtool.yaml | 50 +++++++++++++++++++ Documentation/networking/ethtool-netlink.rst | 35 +++++++++++++ .../uapi/linux/ethtool_netlink_generated.h | 19 +++++++ 3 files changed, 104 insertions(+) diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netli= nk/specs/ethtool.yaml index 0a2d2343f79a..ab0ff17d9195 100644 --- a/Documentation/netlink/specs/ethtool.yaml +++ b/Documentation/netlink/specs/ethtool.yaml @@ -210,6 +210,10 @@ definitions: - name: discard value: 31 + - + name: port-type + type: enum + entries: [mdi, sfp] =20 attribute-sets: - @@ -1890,6 +1894,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: u8 + enum: port-type + - + name: vacant + type: u8 =20 operations: enum-model: directional @@ -2842,6 +2872,26 @@ operations: - worst-channel - link dump: *mse-get-op + - + name: port-get + doc: Get ports attached to an interface + + attribute-set: port + + do: &port-get-op + request: + attributes: + - header + - id + reply: + attributes: + - header + - id + - supported-modes + - supported-interfaces + - type + - vacant + dump: *port-get-op =20 mcast-groups: list: diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/n= etworking/ethtool-netlink.rst index af56c304cef4..3e673755800b 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -2522,6 +2522,40 @@ 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=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 + +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 + ``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`` u8 the port type + ``ETHTOOL_A_PORT_VACANT`` u8 for non-mdi ports, indic= ates + if the port is connected= to + another device that could + expose a MDI + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D =3D=3D=3D=3D= =3D=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 @@ -2632,4 +2666,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 556a0c834df5..f2a5bb666c4c 100644 --- a/include/uapi/linux/ethtool_netlink_generated.h +++ b/include/uapi/linux/ethtool_netlink_generated.h @@ -78,6 +78,11 @@ enum ethtool_pse_event { ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR =3D 64, }; =20 +enum ethtool_port_type { + ETHTOOL_PORT_TYPE_MDI, + ETHTOOL_PORT_TYPE_SFP, +}; + enum { ETHTOOL_A_HEADER_UNSPEC, ETHTOOL_A_HEADER_DEV_INDEX, @@ -837,6 +842,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_VACANT, + + __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, @@ -890,6 +907,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) @@ -951,6 +969,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.49.0 From nobody Sun Feb 8 10:04:27 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 91BDD3D7D86; Tue, 3 Feb 2026 17:29:28 +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=1770139770; cv=none; b=smQ7hFl8Q1/gKBlM0/81n/iBzQMxKGa4xEy/cwCYCLn7lLb3sYCVzsMtrcrap0ks890rMRGZqOqt7TOLXqvn0hh5pW6i2ldO9kjQEYHUQiyxh+1UY66V1Ydn8jzo0qLV/95vV4jUFaGiX8X0H7pS4D3wI+s92L1wK6nuL8s9xa4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770139770; c=relaxed/simple; bh=Hi0hgp7iKwULXA+T7jYAxiFylxpAewRHWgQsebJPiqs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EOpYHc9Wf75C9pGpQMhM6yEeHva7miq6K1U9YTCpERcJ/qcoL1TngZTPB1dt4KxW9uN4U2MAm+TqXNuu0euJBcisKYg9BEiCn8wCV+e8Hc1T75utL6T3gDDvaidvUYOwaYF4xBWMK1Xi5T1X2T2YuJkPw2SanBnlc9jjJ/MaeCM= 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=G5BpxZ7x; 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="G5BpxZ7x" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 5ADF01A2BC4; Tue, 3 Feb 2026 17:29:27 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 3037060728; Tue, 3 Feb 2026 17:29:27 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 99A65119A88EE; Tue, 3 Feb 2026 18:29:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1770139766; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=5hkEWysS/CxNma2DqiXrYZTcYVE95ux9SHUasDRkTEk=; b=G5BpxZ7xPwUj6GwTQlyeML9E6z6wKYFk3I0yXEjYCax/6lvV3O0GmDwEvFql/t1c8vTaxA X5LBFQgCXQnv8axV1QKMl7bveyN+NlL8+SR/7B6BkjrEMwfb41NsqPo41pnEvftnoTgfax b+9WSxmItWbBIWsCSmu8hRrPoiW79EJbteJEx8GTY5DWx0FFcoBEH0rtReIzONi08wgz6/ LbpZTFojxX6YluC0W39bZwcPjZYyO7TD3lt7UvnbPbEMkq6Q4/AHO0o0QBPSTyVvpCbcja me7yq85GszYL8mwIJCs2Mjg7mf2jploqlTTRSA3jdofnZrONBD+siDxg1qmZNg== 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 Subject: [PATCH net-next v4 13/13] net: ethtool: Introduce ethtool command to list ports Date: Tue, 3 Feb 2026 18:28:37 +0100 Message-ID: <20260203172839.548524-14-maxime.chevallier@bootlin.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20260203172839.548524-1-maxime.chevallier@bootlin.com> References: <20260203172839.548524-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. Signed-off-by: Maxime Chevallier --- MAINTAINERS | 1 + net/ethtool/Makefile | 2 +- net/ethtool/netlink.c | 26 +++ net/ethtool/netlink.h | 8 + net/ethtool/port.c | 376 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 412 insertions(+), 1 deletion(-) create mode 100644 net/ethtool/port.c diff --git a/MAINTAINERS b/MAINTAINERS index 0caa8aee5840..cc8999191357 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18219,6 +18219,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 6e5f0f4f815a..90674aed7777 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -18,6 +18,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, @@ -421,6 +423,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) @@ -1544,6 +1547,16 @@ 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 { @@ -1566,10 +1579,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 89010eaa67df..e849bc63ac58 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]; + u32 ethnl_bcast_seq_next(void); int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info, const struct nlattr *nest, struct net *net, @@ -443,6 +446,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]; @@ -499,6 +503,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); @@ -514,6 +519,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..9e6821a8d00b --- /dev/null +++ b/net/ethtool/port.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2026 Bootlin + * + */ +#include "common.h" +#include "bitset.h" +#include "netlink.h" + +#include +#include +#include +#include + +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; + bool vacant; +}; + +#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 { .type =3D NLA_U32}, +}; + +static int port_parse_request(struct ethnl_req_info *req_info, + struct nlattr **tb, + struct netlink_ext_ack *extack) +{ + struct port_req_info *request =3D PORT_REQINFO(req_info); + + /* PORT id is required for GET requests */ + if (tb[ETHTOOL_A_PORT_ID]) + request->port_id =3D nla_get_u32(tb[ETHTOOL_A_PORT_ID]); + + if (!request->port_id) { + NL_SET_ERR_MSG(extack, "port id missing"); + return -ENODEV; + } + + 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->vacant =3D port->vacant; + + 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(u8)); + + /* ETHTOOL_A_PORT_VACANT */ + size +=3D nla_total_size(sizeof(u8)); + + 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 -EMSGSIZE; + } 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 -EMSGSIZE; + } + + if (reply->mii || reply->sfp) + port_type =3D ETHTOOL_PORT_TYPE_SFP; + + if (nla_put_u8(skb, ETHTOOL_A_PORT_TYPE, port_type) || + nla_put_u8(skb, ETHTOOL_A_PORT_VACANT, reply->vacant)) + 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_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(sizeof(*req_info), GFP_KERNEL); + if (!req_info) + return -ENOMEM; + + reply_data =3D kmalloc(sizeof(*reply_data), GFP_KERNEL); + 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; + + /* 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_A_PORT_HEADER); + 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) + 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->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.49.0