From nobody Wed Jun 17 06:13:00 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 C9BB6310785; Thu, 23 Apr 2026 07:42:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776930135; cv=none; b=ksaPklFycg+gu6Wsty63BnmCuXO16qeG5PWE3ZKu2bWoIAB/oQVbkmfq3PXrGFQr9xt1ZCylQmWIDrjdjUqWIA8iJQQmOFF3XR1vTPzyg4iqB+yKM7acPeKlUpj9RM+A9wxQk0b5n5eVrXi82Z5oBr3LBb/P11EEtZHQrplhOio= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776930135; c=relaxed/simple; bh=Ojf2szpvgGjvfr/gigW/pG0LU6lpL971MAn0v2jqSP0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=WYu6naOsCDBMPirmwtsM5SkYF0NjjXLwr1G7kEddoijWAj/eUhNgOIHvmyQAVHt9xMEpkNfNcEU+dAhA/dZAOjcxn/Aj/q53FN+7W3+0LFqY5kbWwrnsj1cq5s14JhUMVuOsVSl31mz0bq2Gf/jEtXmPUiPjaUQayu2PEwOAcRA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=UrboMypB; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="UrboMypB" Received: by smtp.kernel.org (Postfix) with ESMTPS id 6928AC2BCAF; Thu, 23 Apr 2026 07:42:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776930135; bh=Ojf2szpvgGjvfr/gigW/pG0LU6lpL971MAn0v2jqSP0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=UrboMypBEjrZTVEvd2UX6eupsf+UE1stak3mJNs5hz5zzEyyT+dCTFONAdIbKpbUo F+LPm/RHEJCm9vZjdGr0vvUkYNGB7+7Ie1jf/B02Nlb6Fckj6Kyst9Bp4937JzvpIT DFJgsGwzzISagym+2xLtcheiyYxvJwwAv3GnL1KqNGsgixYF7Yhxq3CDytq4b0nwIv SkJuRbt8pJ6+0/zt7tAKfKV2JNa0nWYNdne0FTqynxzwp7i19J3NnE7o2CZCUy/ftn chifZZ4t60SPMuUQB7K/WPXC1F1t+iRAy2+EEWL0+atSocMalPSGz2wOmbKSTbV2B2 7gjRYvqv1VluA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 524C3F99340; Thu, 23 Apr 2026 07:42:15 +0000 (UTC) From: Corey Leavitt via B4 Relay Date: Thu, 23 Apr 2026 01:42:14 -0600 Subject: [PATCH RFC net-next 1/4] net: pse-pd: scope pse_control regulator handle to kref lifetime Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260423-pse-notifier-decouple-v1-1-86ed750a9d62@leavitt.info> References: <20260423-pse-notifier-decouple-v1-0-86ed750a9d62@leavitt.info> In-Reply-To: <20260423-pse-notifier-decouple-v1-0-86ed750a9d62@leavitt.info> To: Oleksij Rempel , Kory Maincent , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Heiner Kallweit , Russell King Cc: Andrew Lunn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Corey Leavitt X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1776930134; l=2530; i=corey@leavitt.info; s=default; h=from:subject:message-id; bh=1SkINJ/nZBrraccoAmh/b7VGiZC9fvXohnssgn1M1aQ=; b=DJqNV0oJz/qT88y7/6+YzTfu6gTeAUnhGF+c3jLz9zwDFcRq+OmnIiibYstzqYB9CadmgTgCE +Q1xmsDzvqrD2FpJkevcaZ/yu17seQ/3wNEWPP8ky+WWP3nZYVoxkas X-Developer-Key: i=corey@leavitt.info; a=ed25519; pk=Q3mmZT+4CRYT8qp8RWvab30X2v2JnLdhHuK7YIHKzpI= X-Endpoint-Received: by B4 Relay for corey@leavitt.info/default with auth_id=748 X-Original-From: Corey Leavitt Reply-To: corey@leavitt.info From: Corey Leavitt __pse_control_release() drops psec->ps via devm_regulator_put(), which only succeeds if the devres entry added by the matching devm_regulator_get_exclusive() is still present on pcdev->dev at the time the pse_control's kref hits zero. In practice that assumption does not hold when the controller is unbound while any pse_control still has consumers: pcdev->dev's devres list is released LIFO, so every per-attach regulator-GET devres runs (and regulator_put()s the underlying regulator) before pse_controller_unregister() itself is invoked. Any later pse_control_put() from that unbind path then reads psec->ps as a dangling pointer inside devm_regulator_put() and WARNs at drivers/regulator/devres.c:232 (devres_release() fails to find the already-released match). The pse_control's consumer handle is logically scoped to the pse_control's refcount, not to pcdev->dev's devres lifetime. Switch to the plain regulator_get_exclusive() / regulator_put() pair so __pse_control_release() does the right put regardless of whether the controller's devres has already been unwound. No change to the regulator-framework-visible refcount or lifetime of the underlying regulator: a single get paired with a single put. The existing devm_regulator_register() for the per-PI rails is unchanged (those ARE correctly scoped to the controller's lifetime). Fixes: d83e13761d5b ("net: pse-pd: Use regulator framework within PSE frame= work") Signed-off-by: Corey Leavitt Acked-by: Kory Maincent Tested-by: Carlo Szelinsky --- drivers/net/pse-pd/pse_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c index f6b94ac7a68a..893ec2185947 100644 --- a/drivers/net/pse-pd/pse_core.c +++ b/drivers/net/pse-pd/pse_core.c @@ -1362,7 +1362,7 @@ static void __pse_control_release(struct kref *kref) =20 if (psec->pcdev->pi[psec->id].admin_state_enabled) regulator_disable(psec->ps); - devm_regulator_put(psec->ps); + regulator_put(psec->ps); =20 module_put(psec->pcdev->owner); =20 @@ -1431,8 +1431,8 @@ pse_control_get_internal(struct pse_controller_dev *p= cdev, unsigned int index, goto free_psec; =20 pcdev->pi[index].admin_state_enabled =3D ret; - psec->ps =3D devm_regulator_get_exclusive(pcdev->dev, - rdev_get_name(pcdev->pi[index].rdev)); + psec->ps =3D regulator_get_exclusive(pcdev->dev, + rdev_get_name(pcdev->pi[index].rdev)); if (IS_ERR(psec->ps)) { ret =3D PTR_ERR(psec->ps); goto put_module; --=20 2.53.0 From nobody Wed Jun 17 06:13:00 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 C9CE6311957; Thu, 23 Apr 2026 07:42:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776930135; cv=none; b=t9zFT13JJS7kqaNTlC58yzVsoIzQJ7fnNXoJsZCA2gnUhOBjLn2Gc7JAMJyb9E4XtHpBrROTDGP0B6n1Wd5IV1BU8UF0iKiYDsT0KQzMhiyXvfuaqvveXE6OfSD6onfsy1twtSry/LHGefTiAK9P5izdShAgZfgzzaRpC0Wd0Dk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776930135; c=relaxed/simple; bh=+C27/4TFGd6sUuW+X9doh2+F27pm4Xs+1B5NISDWs4M=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NMBtYZ+iBTzDXq/rEXS36Ky7nzUjBu8WfYlLvi1PcBPzQRp+IgVDXXqcFwzsVGeca/+qNLqpZfBZVndgC0Gg3YuJUA1rS1GV5q6ZamBgRd69t+wzZKNZIkLVuVzv5kxYoskydm7W2eYE2zFJU6G1cezPz+R49O+q7hwhzMXYO2c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=F9DdkDvq; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="F9DdkDvq" Received: by smtp.kernel.org (Postfix) with ESMTPS id 7394CC2BCB5; Thu, 23 Apr 2026 07:42:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776930135; bh=+C27/4TFGd6sUuW+X9doh2+F27pm4Xs+1B5NISDWs4M=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=F9DdkDvq3j334Jk60iFnILBB7nq701FlaNU4smL6Njeaf0y9vOiAVYQbPUfTi4eDR 3vzgePhw5OBOV7f5VkyhB84sBtMxpYwNm5XQo8pse5kPs5suG42X/Mc8KZko6CLOOL 5AlfDLIpDgrOEY74hIF7xh1mGVRI+fQ/s8g4g71KPHgFFKW0mgVbc71oEptqUck8mF Oh+tggu93BEkuCjXcq7lJhUIWC9m4zrSL6nz2GqHKmUM/y67/8hn5bbrhNR+wk30dd f1Ib2vZGeqNjbPYxOBrziTZ8YY1p1GGS6k+X49Pq/4sLKIDULaTAlYajgpxIhdIEuq heDGbCg+aeM1g== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 65527F9934E; Thu, 23 Apr 2026 07:42:15 +0000 (UTC) From: Corey Leavitt via B4 Relay Date: Thu, 23 Apr 2026 01:42:15 -0600 Subject: [PATCH RFC net-next 2/4] net: pse-pd: add notifier chain for controller lifecycle events Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260423-pse-notifier-decouple-v1-2-86ed750a9d62@leavitt.info> References: <20260423-pse-notifier-decouple-v1-0-86ed750a9d62@leavitt.info> In-Reply-To: <20260423-pse-notifier-decouple-v1-0-86ed750a9d62@leavitt.info> To: Oleksij Rempel , Kory Maincent , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Heiner Kallweit , Russell King Cc: Andrew Lunn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Corey Leavitt X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1776930134; l=5160; i=corey@leavitt.info; s=default; h=from:subject:message-id; bh=JPjkGmRXffHFQfC2I2vGQ0xhYy/8ixYQjJi6kY9CoRc=; b=WTrN9hNzFnWYjnegdQXY9EXP6Nb2/tYCtDR/EZpjo+akks/VVElvHkgPuNG14q+u38RSX6vFN dnI5aTIvFlkDsOUJQX4rYpQ0JHF937ScFG5zigPZR22XHiX2ckWv6gO X-Developer-Key: i=corey@leavitt.info; a=ed25519; pk=Q3mmZT+4CRYT8qp8RWvab30X2v2JnLdhHuK7YIHKzpI= X-Endpoint-Received: by B4 Relay for corey@leavitt.info/default with auth_id=748 X-Original-From: Corey Leavitt Reply-To: corey@leavitt.info From: Corey Leavitt Introduce a blocking notifier chain that allows other subsystems to be informed when a PSE controller is registered or unregistered, and provide pse_register_notifier() / pse_unregister_notifier() as the subscriber interface. Subsequent patches will use this to let the phy subsystem own the phydev->psec lifecycle directly, decoupling PSE lookup from fwnode_mdiobus_register_phy() and removing the probe-time -EPROBE_DEFER coupling that currently exists between mdio, phy and pse-pd when the PSE controller driver is modular. A blocking chain (rather than atomic) is used because callbacks will take rtnl_lock and call back into pse_core via of_pse_control_get(). The enum pse_controller_event is placed outside the IS_ENABLED(CONFIG_PSE_CONTROLLER) guard so that subscribers compiled into a kernel without PSE support can still reference the event values in dead-code paths without breaking the build. This patch is pure infrastructure: nothing fires events yet, and nothing subscribes. No observable behavior change. Signed-off-by: Corey Leavitt Tested-by: Carlo Szelinsky --- drivers/net/pse-pd/pse_core.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/pse-pd/pse.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c index 893ec2185947..80c5c6c1758c 100644 --- a/drivers/net/pse-pd/pse_core.c +++ b/drivers/net/pse-pd/pse_core.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,39 @@ static LIST_HEAD(pse_controller_list); static DEFINE_XARRAY_ALLOC(pse_pw_d_map); static DEFINE_MUTEX(pse_pw_d_mutex); =20 +static BLOCKING_NOTIFIER_HEAD(pse_controller_notifier); + +/** + * pse_register_notifier - register a callback for PSE controller events + * @nb: notifier block to register + * + * See enum pse_controller_event for events fired and their subscriber + * contract. Callbacks run in process context; they may sleep, take + * rtnl, and call of_pse_control_get(). The chain fires synchronously, + * so a PSE controller driver's probe/unbind path must not hold any + * such lock when calling pse_controller_register() or + * pse_controller_unregister(). + * + * Return: 0 on success, negative error code otherwise. + */ +int pse_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&pse_controller_notifier, nb); +} +EXPORT_SYMBOL_GPL(pse_register_notifier); + +/** + * pse_unregister_notifier - unregister a previously registered callback + * @nb: notifier block previously passed to pse_register_notifier() + * + * Return: 0 on success, negative error code otherwise. + */ +int pse_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&pse_controller_notifier, nb); +} +EXPORT_SYMBOL_GPL(pse_unregister_notifier); + /** * struct pse_control - a PSE control * @pcdev: a pointer to the PSE controller device diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h index 4e5696cfade7..78fe3a2b1ea8 100644 --- a/include/linux/pse-pd/pse.h +++ b/include/linux/pse-pd/pse.h @@ -21,6 +21,7 @@ struct net_device; struct phy_device; struct pse_controller_dev; struct netlink_ext_ack; +struct notifier_block; =20 /* C33 PSE extended state and substate. */ struct ethtool_c33_pse_ext_state_info { @@ -337,6 +338,24 @@ enum pse_budget_eval_strategies { PSE_BUDGET_EVAL_STRAT_DYNAMIC =3D 1 << 2, }; =20 +/** + * enum pse_controller_event - PSE controller lifecycle events + * + * Event data in callbacks is always a pointer to the struct + * pse_controller_dev firing the event. + * + * @PSE_REGISTERED: controller added to pse_controller_list and + * resolvable by of_pse_control_get(). + * @PSE_UNREGISTERED: controller about to be removed from + * pse_controller_list. Subscribers holding pse_control references + * targeting it must drop them before returning and must not + * acquire new references for it. + */ +enum pse_controller_event { + PSE_REGISTERED, + PSE_UNREGISTERED, +}; + #if IS_ENABLED(CONFIG_PSE_CONTROLLER) int pse_controller_register(struct pse_controller_dev *pcdev); void pse_controller_unregister(struct pse_controller_dev *pcdev); @@ -366,6 +385,9 @@ int pse_ethtool_set_prio(struct pse_control *psec, bool pse_has_podl(struct pse_control *psec); bool pse_has_c33(struct pse_control *psec); =20 +int pse_register_notifier(struct notifier_block *nb); +int pse_unregister_notifier(struct notifier_block *nb); + #else =20 static inline struct pse_control *of_pse_control_get(struct device_node *n= ode, @@ -416,6 +438,16 @@ static inline bool pse_has_c33(struct pse_control *pse= c) return false; } =20 +static inline int pse_register_notifier(struct notifier_block *nb) +{ + return 0; +} + +static inline int pse_unregister_notifier(struct notifier_block *nb) +{ + return 0; +} + #endif =20 #endif --=20 2.53.0 From nobody Wed Jun 17 06:13:00 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 C9C3E31079B; Thu, 23 Apr 2026 07:42:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776930135; cv=none; b=rSxR+4N0TzaUPL53Fk0ZNmaTBSXwNRSz6aLbsyrl7Z/TLARE36IkpzXi2DWbhd66H0wZVhsfXg0B93e1F/Il+0uGY+ayP/2lLrzchfp6IumhHEIjSKn2ASgRs+0o94NOL2/gH/qKkOgZrUZbGfWGogL+i11lgH+/PNuDW6v6xaI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776930135; c=relaxed/simple; bh=z7L1y/lceWNcGtWDS9MvRXoRJYU0HgYaw9u3PFvDJok=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=VVbXqEbQodaU34C20ZMAUjLW/gP2jKTTt04LNH1kfOeVU4n1H+hUpvr4/4IEzdvnHWCJLnGioC5GJmr/H4e1qawJ/W66sogF1hkm/kP0mEqNL4Y4op/QQBfwdzBBXoe+pWqmHsbQohdaS/XDW4aMf3xw+YSMsDYg5vjxO929Pf8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=NGxc5/nF; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="NGxc5/nF" Received: by smtp.kernel.org (Postfix) with ESMTPS id 862D8C2BCB7; Thu, 23 Apr 2026 07:42:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776930135; bh=z7L1y/lceWNcGtWDS9MvRXoRJYU0HgYaw9u3PFvDJok=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=NGxc5/nFW8lNyT/dv2GXpRR2KxHjXt6MtTrjgIvoV4VOqQaSI0onUoW2SMJ7nvYMY QnpRKIHwR+8p5s65Dx7mDrNbXd7HgnblzgGvqiQALPapc7kk/8uyAQwmuD13+aCNEs nlcsigzRyIWgQ/6VkSa8jwOhPhcHSmkYpwjE9fSw+ppXMc12283FP1lT2zmbtl+OrU 756LK96NYFFgMewVMk4ifFMIsUS4HL7DaMcoOIHhxb8sjK177F84lstKIDV7AeRdQ5 yexvgAEXgJLFA5g60cMcUTanaT9cAlGQtp1hAQgxMjku2dg914CBWXg+VCVjK0Bfzv AiCP2V0/7znlg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 77472F9934F; Thu, 23 Apr 2026 07:42:15 +0000 (UTC) From: Corey Leavitt via B4 Relay Date: Thu, 23 Apr 2026 01:42:16 -0600 Subject: [PATCH RFC net-next 3/4] net: pse-pd: fire lifecycle events on controller register/unregister Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260423-pse-notifier-decouple-v1-3-86ed750a9d62@leavitt.info> References: <20260423-pse-notifier-decouple-v1-0-86ed750a9d62@leavitt.info> In-Reply-To: <20260423-pse-notifier-decouple-v1-0-86ed750a9d62@leavitt.info> To: Oleksij Rempel , Kory Maincent , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Heiner Kallweit , Russell King Cc: Andrew Lunn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Corey Leavitt X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1776930134; l=1536; i=corey@leavitt.info; s=default; h=from:subject:message-id; bh=MOnFN08SYHgfOekYGnbnF8wDziGnDMovMc1cNiCo+D8=; b=IUOXxGgqd7R2ArRe+iXdztqayIZwufjmynyMcSsq84IiA15zdQNGk7q1KjJ6DOKNsF8p6Hor/ OLbqnLpd4jXAqXRcQvoPT0J9acMSe3u1Q4Fqwvs5NjOjo49vTHcvUc7 X-Developer-Key: i=corey@leavitt.info; a=ed25519; pk=Q3mmZT+4CRYT8qp8RWvab30X2v2JnLdhHuK7YIHKzpI= X-Endpoint-Received: by B4 Relay for corey@leavitt.info/default with auth_id=748 X-Original-From: Corey Leavitt Reply-To: corey@leavitt.info From: Corey Leavitt Hook the newly-introduced pse_controller_notifier chain so that pse_controller_register() fires PSE_REGISTERED after the controller has been added to pse_controller_list (i.e. is now resolvable by of_pse_control_get()), and pse_controller_unregister() fires PSE_UNREGISTERED before the controller is removed from the list (while it is still valid to dereference from a subscriber's pse_control pointer targeting it). With no subscribers yet, this is observably a no-op. A later change wires the phy subsystem in as the first subscriber. Signed-off-by: Corey Leavitt Tested-by: Carlo Szelinsky --- drivers/net/pse-pd/pse_core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c index 80c5c6c1758c..82125502a8e3 100644 --- a/drivers/net/pse-pd/pse_core.c +++ b/drivers/net/pse-pd/pse_core.c @@ -1138,6 +1138,9 @@ int pse_controller_register(struct pse_controller_dev= *pcdev) list_add(&pcdev->list, &pse_controller_list); mutex_unlock(&pse_list_mutex); =20 + blocking_notifier_call_chain(&pse_controller_notifier, + PSE_REGISTERED, pcdev); + return 0; } EXPORT_SYMBOL_GPL(pse_controller_register); @@ -1148,6 +1151,9 @@ EXPORT_SYMBOL_GPL(pse_controller_register); */ void pse_controller_unregister(struct pse_controller_dev *pcdev) { + blocking_notifier_call_chain(&pse_controller_notifier, + PSE_UNREGISTERED, pcdev); + pse_flush_pw_ds(pcdev); pse_release_pis(pcdev); if (pcdev->irq) --=20 2.53.0 From nobody Wed Jun 17 06:13:00 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 C9B3430FF37; Thu, 23 Apr 2026 07:42:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776930135; cv=none; b=R6SKgNmdA9RnMaLL1a+JWoakclWlCfSsSwdxaPuBqygXQRW3PpmUHOnjJea+Tjh2Rqqg+nEot/GDwiW0MHZj7TAYQ3XxE3OKCGbPJLiE+l4bXlFMZCPrTtyofC5un2Mq2LMsMzGF4eoUgGHh+13TajBcWc2/gY2aULndARu4hgY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776930135; c=relaxed/simple; bh=0lHQl65JViCcYaSRexKi4sz3ALisV44JRIeCGFa8Lnk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ULWBCnSyI0fj/ghQsemO/e1tuDFUSkETc3pAOFF5DBGXqmoVY9vfsugOQFJtj0UzyDnLADGDOD0xBmzEm56dP1jy7g9D5yNHDqZXWCqUSjotlhlLFHIkvv67R0A8TydqMUPifxvuyS8V0Ehj3IQ4IMW/xZUMBvmeMX4gxpmfUig= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=OkUqYUjb; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="OkUqYUjb" Received: by smtp.kernel.org (Postfix) with ESMTPS id 8EFD8C2BCB9; Thu, 23 Apr 2026 07:42:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1776930135; bh=0lHQl65JViCcYaSRexKi4sz3ALisV44JRIeCGFa8Lnk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=OkUqYUjbwuWR+LYr3DXgoqMV482dX4vQ0RVHEPPeuimk58haF/evc+quFiGr1kyAL jafCNWkNdJJb9VWqenp7BFaI5GKdytnU43DWDu0R2xKiHZnNOhjhsXjTZf70HdscsP I2Rod1dy5BVfYygyribd196jYaeFN7KjTif/kLenyYJ8UdV8OwDMm4SFGdt4iX7sMZ 8wsazioXHj1ya/XeS60pits68i2iTmUwrlS4UQS/3HnB8H33JZp1Vbq2+qSfvzkCwU h1k6v+OKqOBIh1jem+9p/ROdl2fc+jyxcuOXOoxaBo6AiwU0GRS52KL7pmO1Mjya2/ 0EM+YMrsOv8Gw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 888A1F9934D; Thu, 23 Apr 2026 07:42:15 +0000 (UTC) From: Corey Leavitt via B4 Relay Date: Thu, 23 Apr 2026 01:42:17 -0600 Subject: [PATCH RFC net-next 4/4] net: phy: own phydev->psec via PSE notifier and remove fwnode_mdio hook Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260423-pse-notifier-decouple-v1-4-86ed750a9d62@leavitt.info> References: <20260423-pse-notifier-decouple-v1-0-86ed750a9d62@leavitt.info> In-Reply-To: <20260423-pse-notifier-decouple-v1-0-86ed750a9d62@leavitt.info> To: Oleksij Rempel , Kory Maincent , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Heiner Kallweit , Russell King Cc: Andrew Lunn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Corey Leavitt X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1776930134; l=15445; i=corey@leavitt.info; s=default; h=from:subject:message-id; bh=doMA7uwXNEZkymG7Ed0bflskqQqfJ/G13i1ggh380Aw=; b=QY4BAIghTuSOAsdlA3Y8aWcsUOy+PuRg8hq7J4GiJKFGv2hZeeb7k0cAYzILD1/qDkysypc0A disOxvRubHUB1BNCYsOrgq9NtNRYUHuzS9oM7C6DTBO+0lhNLbCVOZG X-Developer-Key: i=corey@leavitt.info; a=ed25519; pk=Q3mmZT+4CRYT8qp8RWvab30X2v2JnLdhHuK7YIHKzpI= X-Endpoint-Received: by B4 Relay for corey@leavitt.info/default with auth_id=748 X-Original-From: Corey Leavitt Reply-To: corey@leavitt.info From: Corey Leavitt Transfer ownership of phydev->psec from fwnode_mdio to the phy subsystem itself. The phy subsystem now subscribes to the pse-pd notifier chain and manages psec attach/detach in response to PSE controller lifecycle events, while fwnode_mdio loses its PSE awareness entirely. Split phy_device_register() into a public entry point that takes rtnl_lock() and a phy_device_register_locked() variant that assumes rtnl is already held. Callers that already hold rtnl (the SFP module state machine via __sfp_sm_event) use the _locked form to avoid deadlock; all other callers use the unchanged public API. This pair mirrors the register_netdevice() / register_netdev() split convention already established in the core networking stack. rtnl must span the full registration sequence through device_add(), not just phy_try_attach_pse(): a PSE_REGISTERED event firing between a narrow attach lock and device_add() would walk mdio_bus_type, find the phy not yet on the bus, and leave it permanently unattached. With rtnl held across the full registration sequence: - At phy_device_register_locked(), phy_try_attach_pse() attempts an of_pse_control_get() for phys whose DT pses phandle resolves now. If the controller is already registered, psec is attached before device_add() makes the phy visible on mdio_bus_type. If the controller is not yet registered, the swallow-error path leaves psec NULL and relies on the subsequent notifier event. - On PSE_REGISTERED: an rtnl-guarded bus walk retries the attach for every registered phy whose psec is still NULL. This is the "phy was enumerated before the PSE controller loaded" case, which is the root cause of the boot-time probe-retry storm on systems with a modular PSE controller driver. Because the pse_controller_notifier is fired synchronously, a concurrent pse_controller_register() either (a) completes list_add and releases pse_list_mutex before this function takes rtnl, in which case phy_try_attach_pse() finds the controller in the list and attaches; or (b) fires its notifier during this function, in which case the callback blocks on rtnl until this function returns, then walks the bus and finds the phy fully registered (attaching if psec is still NULL). - On PSE_UNREGISTERED: an rtnl-guarded bus walk releases every phydev->psec that targets the departing controller before pse_release_pis() frees pcdev->pi. Without this, a phy still holding a pse_control reference would cause a use-after-free in __pse_control_release's pcdev->pi[psec->id] access, and the PSE driver module could not finish unloading while any phy still held a reference via module_put(). Introduce phy_try_attach_pse() as the rtnl-guarded helper used by both the register path and the notifier walk. Holding rtnl across of_pse_control_get() is safe because pse_list_mutex is never held in the opposite order. Expose pse_control_matches_pcdev() as a predicate so subscribers can identify which of their held pse_control references target a given controller, without leaking the struct pse_controller_dev * out of pse_control opacity. Move the final pse_control_put() of phydev->psec from phy_device_remove() to phy_device_release(). The kobject release callback runs only after every reference on the device has been dropped, including the bus iterator references taken by bus_for_each_dev() in the notifier walk, which means by the time release fires no concurrent reader or writer of phydev->psec can exist. The mdio_bus_type klist is set up in bus_register() with klist_devices_get() / klist_devices_put() (drivers/base/bus.c), which bracket each iteration step with get_device() / put_device() on the underlying struct device; that reference defers the release callback from firing until the walk has advanced past this phy. Keeping phy_device_remove() unchanged avoids introducing a new locking contract on its many callers (sfp, fixed_phy, xgbe, hns, netsec, bcm_sf2, mdiobus_unregister). Finally, delete fwnode_find_pse_control() and its call site in fwnode_mdiobus_register_phy(), and drop the PSE header from fwnode_mdio.c. This removes the probe-time -EPROBE_DEFER coupling between mdio and pse-pd that caused the boot-hang regression on systems with a modular PSE controller driver and a DT phy with a pses phandle: the MDIO/DSA probe no longer sees any PSE-originated -EPROBE_DEFER, so the probe-retry storm is gone. fwnode_mdio is now PSE-agnostic. Fixes: fa2f0454174c ("net: pse-pd: Introduce attached_phydev to pse control= ") Signed-off-by: Corey Leavitt Tested-by: Carlo Szelinsky --- drivers/net/mdio/fwnode_mdio.c | 34 ---------- drivers/net/phy/phy_device.c | 144 +++++++++++++++++++++++++++++++++++++= +--- drivers/net/phy/sfp.c | 2 +- drivers/net/pse-pd/pse_core.c | 14 ++++ include/linux/phy.h | 2 + include/linux/pse-pd/pse.h | 9 +++ 6 files changed, 161 insertions(+), 44 deletions(-) diff --git a/drivers/net/mdio/fwnode_mdio.c b/drivers/net/mdio/fwnode_mdio.c index ba7091518265..7bd979b59f49 100644 --- a/drivers/net/mdio/fwnode_mdio.c +++ b/drivers/net/mdio/fwnode_mdio.c @@ -11,33 +11,11 @@ #include #include #include -#include =20 MODULE_AUTHOR("Calvin Johnson "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("FWNODE MDIO bus (Ethernet PHY) accessors"); =20 -static struct pse_control * -fwnode_find_pse_control(struct fwnode_handle *fwnode, - struct phy_device *phydev) -{ - struct pse_control *psec; - struct device_node *np; - - if (!IS_ENABLED(CONFIG_PSE_CONTROLLER)) - return NULL; - - np =3D to_of_node(fwnode); - if (!np) - return NULL; - - psec =3D of_pse_control_get(np, phydev); - if (PTR_ERR(psec) =3D=3D -ENOENT) - return NULL; - - return psec; -} - static struct mii_timestamper * fwnode_find_mii_timestamper(struct fwnode_handle *fwnode) { @@ -118,7 +96,6 @@ int fwnode_mdiobus_register_phy(struct mii_bus *bus, struct fwnode_handle *child, u32 addr) { struct mii_timestamper *mii_ts =3D NULL; - struct pse_control *psec =3D NULL; struct phy_device *phy; bool is_c45; u32 phy_id; @@ -159,14 +136,6 @@ int fwnode_mdiobus_register_phy(struct mii_bus *bus, goto clean_phy; } =20 - psec =3D fwnode_find_pse_control(child, phy); - if (IS_ERR(psec)) { - rc =3D PTR_ERR(psec); - goto unregister_phy; - } - - phy->psec =3D psec; - /* phy->mii_ts may already be defined by the PHY driver. A * mii_timestamper probed via the device tree will still have * precedence. @@ -176,9 +145,6 @@ int fwnode_mdiobus_register_phy(struct mii_bus *bus, =20 return 0; =20 -unregister_phy: - if (is_acpi_node(child) || is_of_node(child)) - phy_device_remove(phy); clean_phy: phy_device_free(phy); clean_mii_ts: diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index c2cdf1ae3542..7948800e6e49 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -223,8 +223,19 @@ static void phy_mdio_device_free(struct mdio_device *m= diodev) =20 static void phy_device_release(struct device *dev) { + struct phy_device *phydev =3D to_phy_device(dev); + + /* bus_for_each_dev() holds get_device() across each iteration + * step, deferring this release callback until any in-flight PSE + * notifier walk has advanced past this phy. pse_control_put() + * takes pse_list_mutex, so this path must run in sleepable + * context. + */ + might_sleep(); + pse_control_put(phydev->psec); + fwnode_handle_put(dev->fwnode); - kfree(to_phy_device(dev)); + kfree(phydev); } =20 static void phy_mdio_device_remove(struct mdio_device *mdiodev) @@ -1102,14 +1113,102 @@ struct phy_device *get_phy_device(struct mii_bus *= bus, int addr, bool is_c45) } EXPORT_SYMBOL(get_phy_device); =20 -/** - * phy_device_register - Register the phy device on the MDIO bus - * @phydev: phy_device structure to be added to the MDIO bus +/* Best-effort attach of phydev->psec from a DT `pses =3D <&...>` phandle. + * Caller must hold rtnl. Errors are swallowed; the notifier retries + * at PSE_REGISTERED time. */ -int phy_device_register(struct phy_device *phydev) +static void phy_try_attach_pse(struct phy_device *phydev) +{ + struct pse_control *psec; + struct device_node *np; + + ASSERT_RTNL(); + + np =3D phydev->mdio.dev.of_node; + if (!np) + return; + + if (phydev->psec) + return; + + psec =3D of_pse_control_get(np, phydev); + if (IS_ERR(psec)) + return; + + phydev->psec =3D psec; +} + +static int phy_pse_attach_one(struct device *dev, void *data __maybe_unuse= d) +{ + ASSERT_RTNL(); + + if (dev->type !=3D &mdio_bus_phy_type) + return 0; + + phy_try_attach_pse(to_phy_device(dev)); + return 0; +} + +static int phy_pse_detach_one(struct device *dev, void *data) +{ + struct pse_controller_dev *pcdev =3D data; + struct phy_device *phydev; + struct pse_control *psec; + + ASSERT_RTNL(); + + if (dev->type !=3D &mdio_bus_phy_type) + return 0; + + phydev =3D to_phy_device(dev); + psec =3D phydev->psec; + if (!psec || !pse_control_matches_pcdev(psec, pcdev)) + return 0; + + phydev->psec =3D NULL; + pse_control_put(psec); + return 0; +} + +static int phy_pse_notifier_event(struct notifier_block *nb, + unsigned long event, void *data) +{ + switch (event) { + case PSE_REGISTERED: + rtnl_lock(); + bus_for_each_dev(&mdio_bus_type, NULL, NULL, + phy_pse_attach_one); + rtnl_unlock(); + return NOTIFY_OK; + case PSE_UNREGISTERED: + rtnl_lock(); + bus_for_each_dev(&mdio_bus_type, NULL, data, + phy_pse_detach_one); + rtnl_unlock(); + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + +static struct notifier_block phy_pse_notifier __read_mostly =3D { + .notifier_call =3D phy_pse_notifier_event, +}; + +/** + * phy_device_register_locked - Register the phy device on the MDIO bus + * @phydev: phy_device structure to be added to the MDIO bus + * + * Same as phy_device_register() but caller must already hold rtnl_lock(). + * + * Return: 0 on success, negative error code on failure. + */ +int phy_device_register_locked(struct phy_device *phydev) { int err; =20 + ASSERT_RTNL(); + err =3D mdiobus_register_device(&phydev->mdio); if (err) return err; @@ -1124,6 +1223,8 @@ int phy_device_register(struct phy_device *phydev) goto out; } =20 + phy_try_attach_pse(phydev); + err =3D device_add(&phydev->mdio.dev); if (err) { phydev_err(phydev, "failed to add\n"); @@ -1133,12 +1234,32 @@ int phy_device_register(struct phy_device *phydev) return 0; =20 out: - /* Assert the reset signal */ + /* If phy_try_attach_pse() set phydev->psec before device_add() + * failed, the caller's phy_device_free() -> phy_device_release() + * chain will drop it. + */ phy_device_reset(phydev, 1); - mdiobus_unregister_device(&phydev->mdio); return err; } +EXPORT_SYMBOL(phy_device_register_locked); + +/** + * phy_device_register - Register the phy device on the MDIO bus + * @phydev: phy_device structure to be added to the MDIO bus + * + * Return: 0 on success, negative error code on failure. + */ +int phy_device_register(struct phy_device *phydev) +{ + int err; + + rtnl_lock(); + err =3D phy_device_register_locked(phydev); + rtnl_unlock(); + + return err; +} EXPORT_SYMBOL(phy_device_register); =20 /** @@ -1152,8 +1273,6 @@ EXPORT_SYMBOL(phy_device_register); void phy_device_remove(struct phy_device *phydev) { unregister_mii_timestamper(phydev->mii_ts); - pse_control_put(phydev->psec); - device_del(&phydev->mdio.dev); =20 /* Assert the reset signal */ @@ -3962,8 +4081,14 @@ static int __init phy_init(void) if (rc) goto err_c45; =20 + rc =3D pse_register_notifier(&phy_pse_notifier); + if (rc) + goto err_genphy; + return 0; =20 +err_genphy: + phy_driver_unregister(&genphy_driver); err_c45: phy_driver_unregister(&genphy_c45_driver); err_ethtool_phy_ops: @@ -3980,6 +4105,7 @@ static int __init phy_init(void) =20 static void __exit phy_exit(void) { + pse_unregister_notifier(&phy_pse_notifier); phy_driver_unregister(&genphy_c45_driver); phy_driver_unregister(&genphy_driver); rtnl_lock(); diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index bd970f753beb..d19fe0f30c5d 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -1932,7 +1932,7 @@ static int sfp_sm_probe_phy(struct sfp *sfp, int addr= , bool is_c45) /* Mark this PHY as being on a SFP module */ phy->is_on_sfp_module =3D true; =20 - err =3D phy_device_register(phy); + err =3D phy_device_register_locked(phy); if (err) { phy_device_free(phy); dev_err(sfp->dev, "phy_device_register failed: %pe\n", diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c index 82125502a8e3..a0667324a029 100644 --- a/drivers/net/pse-pd/pse_core.c +++ b/drivers/net/pse-pd/pse_core.c @@ -2016,3 +2016,17 @@ bool pse_has_c33(struct pse_control *psec) return psec->pcdev->types & ETHTOOL_PSE_C33; } EXPORT_SYMBOL_GPL(pse_has_c33); + +/** + * pse_control_matches_pcdev - Test whether a pse_control targets a contro= ller + * @psec: pse_control obtained from of_pse_control_get() + * @pcdev: PSE controller to compare against + * + * Return: %true if @psec was obtained from @pcdev, %false otherwise. + */ +bool pse_control_matches_pcdev(struct pse_control *psec, + struct pse_controller_dev *pcdev) +{ + return psec->pcdev =3D=3D pcdev; +} +EXPORT_SYMBOL_GPL(pse_control_matches_pcdev); diff --git a/include/linux/phy.h b/include/linux/phy.h index 199a7aaa341b..865b9baddb85 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -2158,6 +2158,8 @@ struct phy_device *fwnode_phy_find_device(struct fwno= de_handle *phy_fwnode); struct fwnode_handle *fwnode_get_phy_node(const struct fwnode_handle *fwno= de); struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c= 45); int phy_device_register(struct phy_device *phy); +/* Caller must hold rtnl_lock(); see phy_device_register() for the public = form. */ +int phy_device_register_locked(struct phy_device *phy); void phy_device_free(struct phy_device *phydev); void phy_device_remove(struct phy_device *phydev); int phy_get_c45_ids(struct phy_device *phydev); diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h index 78fe3a2b1ea8..d4310ca71a3e 100644 --- a/include/linux/pse-pd/pse.h +++ b/include/linux/pse-pd/pse.h @@ -385,6 +385,9 @@ int pse_ethtool_set_prio(struct pse_control *psec, bool pse_has_podl(struct pse_control *psec); bool pse_has_c33(struct pse_control *psec); =20 +bool pse_control_matches_pcdev(struct pse_control *psec, + struct pse_controller_dev *pcdev); + int pse_register_notifier(struct notifier_block *nb); int pse_unregister_notifier(struct notifier_block *nb); =20 @@ -438,6 +441,12 @@ static inline bool pse_has_c33(struct pse_control *pse= c) return false; } =20 +static inline bool pse_control_matches_pcdev(struct pse_control *psec, + struct pse_controller_dev *pcdev) +{ + return false; +} + static inline int pse_register_notifier(struct notifier_block *nb) { return 0; --=20 2.53.0