From nobody Fri Jun 19 19:39:58 2026 Received: from mail-4320.protonmail.ch (mail-4320.protonmail.ch [185.70.43.20]) (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 631F02DE6F8; Thu, 23 Apr 2026 07:23:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.43.20 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776928989; cv=none; b=L6Wsg0yXy3pVMrxRAoLF9h9CxMIsixDvr5RbYUjJPIaJS1WzS8WcAYpbDlqILW1sANskQQoS9ZiHHFgRnLOCXB6Gb7Kkgo5nV/eQXS7niCOLmnlNQh4hASkiVSWu8U/BMOi0s/4r8tDtDGU7KYo529Ik1wIAyK/lPbyTxVe4GJU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776928989; c=relaxed/simple; bh=rU5SFRDMxVUocEWkRBaTwnmlMCuQOyRV9Xszlm2djQY=; h=Date:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=JKiM/5AsxT/NLJr3xYYu9FPsHp9knPTkT22Cubbk4Q70i0Oc7RD9EAUPRPBpxqMpBYto2LHmW8pRKqSeBPlXZUOJXS70MON119En/TziWzVbrd6pyyjYNnT8VT81h5MSCHnYB8PgSoQKbtJOnjGVe0ktZ9YRFZOf0mirlRjqrV8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=leavitt.info; spf=pass smtp.mailfrom=leavitt.info; dkim=pass (2048-bit key) header.d=leavitt.info header.i=@leavitt.info header.b=AMRS5U9j; arc=none smtp.client-ip=185.70.43.20 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=leavitt.info Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=leavitt.info Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=leavitt.info header.i=@leavitt.info header.b="AMRS5U9j" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=leavitt.info; s=protonmail2; t=1776928975; x=1777188175; bh=YfI4kXOLAwVZfQy4tYll6QMphd3BqRNpbQf/vGRAJ8s=; h=Date:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=AMRS5U9jSZCsIvAeAH9/lse6bYeUlkF3b+ovW0WuuGMRizrxjD5lY4gQ5w1icUn3y jKjumGf8JSlpeqmHFE3YpOaf58HggBNEmMRbSfpOo+Nqf88ZXQmMB7+VuMkKMECqlI BQbvrQ8C17Tx5vr70ssKpUBL9hglhBAtSKPGLj24uXMW9lUOl4nbLsEiNZZItdGYJd 4/489ZwMfUhjSWSh6t8JhdKA94uFncWaIM2zplaEMk8LE6SPbTsZ34XxEVvovLjB1W ZAfD1hEi9Ot4sGZtEY0LefCYshxGTXOLktcdCV609xASaxGTaRytngS6isy63P15nk bzPX23dJJY3pA== Date: Thu, 23 Apr 2026 07:22:48 +0000 From: Corey Leavitt Cc: Andrew Lunn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Corey Leavitt Subject: [PATCH RFC net-next 1/4] net: pse-pd: scope pse_control regulator handle to kref lifetime Message-ID: <20260423-pse-notifier-decouple-v1-1-7d4856f686f6@leavitt.info> In-Reply-To: <20260423-pse-notifier-decouple-v1-0-7d4856f686f6@leavitt.info> References: <20260423-pse-notifier-decouple-v1-0-7d4856f686f6@leavitt.info> Feedback-ID: 22087991:user:proton X-Pm-Message-ID: a3ccc6cd4734ba283db894f3fd3911ccfe1c3737 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 Content-Type: text/plain; charset="utf-8" __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 --- 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 Fri Jun 19 19:39:58 2026 Received: from mail-07.mail-europe.com (mail-07.mail-europe.com [188.165.51.139]) (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 73FC82F7AD2; Thu, 23 Apr 2026 07:23:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=188.165.51.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776928998; cv=none; b=ugTykFb6lv63nonoLujuMPFDP8QXxshO65il7UrgMyo31DgGQT5SNh1+eQywvQSKkrRivu2LJ647tDb5e2ckmPN82a79X6z1qitO9DpHyTS74OFIidgI9SJAPHtiV93hL9Rw0+ghIdTGB+VRtxmDuJLt16DHaqRKvQ2dvYrn2Yk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776928998; c=relaxed/simple; bh=hChy+6rDRUf2GpLoSaLhsfjEw8fjgI5kUD3SzhAOXmw=; h=Date:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=tkB1PqBvgC3NQWfMtby5lW42BB+Sf+M9TfMRXWWqVnU6wkqGw+VfQc5Uzfv0bpF/TPbKnSTXJmYob1gQFhiU9As+jWuUK5RVdS1HdRLu63kHMAkWsSNw/HlXWLdKg9oFHBZE64TXbhloEYOUWLvsDYDCBkcAtb41Y2pmd3rMsKE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=leavitt.info; spf=pass smtp.mailfrom=leavitt.info; dkim=pass (2048-bit key) header.d=leavitt.info header.i=@leavitt.info header.b=iQgi4L+e; arc=none smtp.client-ip=188.165.51.139 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=leavitt.info Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=leavitt.info Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=leavitt.info header.i=@leavitt.info header.b="iQgi4L+e" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=leavitt.info; s=protonmail2; t=1776928983; x=1777188183; bh=Bp6phfYHAPKngKvtNuRG23M7W4n7ya6XWisq2+okU0w=; h=Date:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=iQgi4L+eJwtQfYXvaJy1fIaaR/2j+5gbVBIfkdSm049XWuCnV8EVws1ycETNqeRTV g4sOBM4Al5QZZX3vPck4hFuHBEvKo9OSr6vQuuxq3bEBdM6Tls1YkRgaKtNYeinrVl ECbOxfF0YL9MjSj6lGx0oRybTnSGlmaDyH9ef2M7R1fGIKWBnqOXbeuZNyCt4b+goe 3vJOrlg7DD76EWQnCTmZCS4PXwr/VjD5MwsG6FgmD1r5IaI662dIE3xCREMebzIFg+ ZV/q9+5grzwsQzOcAkOpg5vBcKe7EXuSEWzT56U9VnVC8NPMswehqP6tEEX5aUZU1Z X82oSxBQh0ekA== Date: Thu, 23 Apr 2026 07:22:57 +0000 From: Corey Leavitt Cc: Andrew Lunn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Corey Leavitt Subject: [PATCH RFC net-next 2/4] net: pse-pd: add notifier chain for controller lifecycle events Message-ID: <20260423-pse-notifier-decouple-v1-2-7d4856f686f6@leavitt.info> In-Reply-To: <20260423-pse-notifier-decouple-v1-0-7d4856f686f6@leavitt.info> References: <20260423-pse-notifier-decouple-v1-0-7d4856f686f6@leavitt.info> Feedback-ID: 22087991:user:proton X-Pm-Message-ID: e51c526c85b485921bc072aefe51139be90a55dc 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 Content-Type: text/plain; charset="utf-8" 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 --- 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 Fri Jun 19 19:39:58 2026 Received: from mail-4318.protonmail.ch (mail-4318.protonmail.ch [185.70.43.18]) (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 3CCB52DEA74; Thu, 23 Apr 2026 07:23:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.43.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776928993; cv=none; b=TPsHjxRE/HUFcEiN3/vzNjzNtcW5LDQ2NcIs8spYRobRVBgIygHxr73y0ewYzqfS68XSqPqHHkRbj0mbVIXvmLxbSsOfBbhjv1B+cK/IUCQ66YTr66MjaSHQ2SmQ1AU1bH7VsNpIjoHRDx3Ldj8y+JzID1HgRVyAcd3gTHcAyfo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776928993; c=relaxed/simple; bh=Arx4hOYjLkGsnfY6tXgcbFX1wKOxubgXKQeBDqdW4X4=; h=Date:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=lDq4G7Am8mWvWRpCnIJF3UIlIvgqBn0jLIZIgVgUaqSeIYiDuHnDZO89tBKzRvwmmr8mBlMqrOz+ozE2R6UN5u4wWcICz+0O7JcOD8yYUSOPLHyTjQ/CIm4AIK9UvNqkXkJLO+zGJmyRgvL3owMUxpTflTA9kZku6csCzIoXJVg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=leavitt.info; spf=pass smtp.mailfrom=leavitt.info; dkim=pass (2048-bit key) header.d=leavitt.info header.i=@leavitt.info header.b=Kz3+aLTi; arc=none smtp.client-ip=185.70.43.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=leavitt.info Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=leavitt.info Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=leavitt.info header.i=@leavitt.info header.b="Kz3+aLTi" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=leavitt.info; s=protonmail2; t=1776928990; x=1777188190; bh=R3M4S5i9yX5QgyGHLDpIQ0EoTADd9Orpohb+BkQJq0g=; h=Date:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=Kz3+aLTioi8WF2EV1r/QiLGs+5J4XbE94aZMAwyuX7wbgyNwhrg+K8rdZXrxZ5lt/ h+tlX7x61iTGpG6ZykrBvr1MZAM1NzYUY4tdnSE4QLk66Dytz+AHFcOpyiWS2VF2vi 3G0K0rfUJ6RTXOpjVeSJPE3Jr0d4dncX6dBWY2Doue6eC9qox+ufjsBX/wVWdX+nYk dub1wDs+2ZMLVyF+Pw5Sjc4k5hjQNFK/Gj2ITNtodMDvR/dmKXIULApoGk/gAuMLoK 2AIOM+zoVJMFVfQ77zv/eBHjMnFgVRMxl3TCGIeBzw1DL8ThKiKb1KG8bZUWmcOkej HhAkEk+iHkzGQ== Date: Thu, 23 Apr 2026 07:23:05 +0000 From: Corey Leavitt Cc: Andrew Lunn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Corey Leavitt Subject: [PATCH RFC net-next 3/4] net: pse-pd: fire lifecycle events on controller register/unregister Message-ID: <20260423-pse-notifier-decouple-v1-3-7d4856f686f6@leavitt.info> In-Reply-To: <20260423-pse-notifier-decouple-v1-0-7d4856f686f6@leavitt.info> References: <20260423-pse-notifier-decouple-v1-0-7d4856f686f6@leavitt.info> Feedback-ID: 22087991:user:proton X-Pm-Message-ID: 9786f841e078a3542c664a83c2acd05174e23d04 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 Content-Type: text/plain; charset="utf-8" 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 --- 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 Fri Jun 19 19:39:58 2026 Received: from mail-06.mail-europe.com (mail-06.mail-europe.com [85.9.210.45]) (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 9E64C2ECD32; Thu, 23 Apr 2026 07:23:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=85.9.210.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776929017; cv=none; b=FO4JtCu62tzeIJv4MQwlXRGYKfbOc2Okfvn5RrjVjCilb44mE3cvDLLTVllKVHCNaXYgudTVnzPp+irRRg8q5yuyG3C0iDaG3lkVzBzU9qxvrWmFwXo4ly4Z/+hOf1+FjdIKZxS4vtzVbpqgJO06KbpQlhlnK8q6uuxhJmE1PRk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776929017; c=relaxed/simple; bh=2q2Ht1T4JW4DHF8OgvK6JxL3ji+OL2Cil+OTtz7ITI8=; h=Date:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=I0rxD2LRXmgbz6/POJXaAG++/a09YDldGLpbl2bSox89FPCSJyG+/s6GjZ2Fv9f3pF1j+PHIqeQzSIOHmG7se7iK5VGwa77pF4v3B+EkxMXDj5VS+HSIxKLZrcvE1yWPQ0aXQbi3uO0N/a8YP3RKAB0Igslv0cemZQv/ieKqG6U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=leavitt.info; spf=pass smtp.mailfrom=leavitt.info; dkim=pass (2048-bit key) header.d=leavitt.info header.i=@leavitt.info header.b=h+7lDWvA; arc=none smtp.client-ip=85.9.210.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=leavitt.info Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=leavitt.info Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=leavitt.info header.i=@leavitt.info header.b="h+7lDWvA" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=leavitt.info; s=protonmail2; t=1776928999; x=1777188199; bh=8BPE33mdf0YTUZNZ1J3DbxAruLByJV6Jr4w045J0KKU=; h=Date:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=h+7lDWvAeiSc5dCybMxjcq66yd9mfYDPnOX4Q5RIuGRXbdJjupxfpoPhZUKfxAq8O DvSOP5e5+0RkSlPs5tylEgpuwxn94iKZM6qaRTWxNNOjMh51xpA90f/LjtZa2lSfoN sw1aYt4/CTZO0PlQ3NK3DHqbJgRIf82pZkK0eWUVGv+hnXfDJ5zmQlPEyK+miXNtDG GxAgCEFXCVqPX6y7ZMbI7GJj5Tn/tLoeaV/A9nIZXOiG5CQtWTovXW0RzVnRRdUhYN R7dsTcxHnelVP1KZBVC1ifiJsipwKTNoPHSvkRDtaIjPlcdqk0LmxjI7F5jgd2R6/T nRkpNNaduZv3g== Date: Thu, 23 Apr 2026 07:23:13 +0000 From: Corey Leavitt Cc: Andrew Lunn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Corey Leavitt Subject: [PATCH RFC net-next 4/4] net: phy: own phydev->psec via PSE notifier and remove fwnode_mdio hook Message-ID: <20260423-pse-notifier-decouple-v1-4-7d4856f686f6@leavitt.info> In-Reply-To: <20260423-pse-notifier-decouple-v1-0-7d4856f686f6@leavitt.info> References: <20260423-pse-notifier-decouple-v1-0-7d4856f686f6@leavitt.info> Feedback-ID: 22087991:user:proton X-Pm-Message-ID: 5f66f2e8f185b92ad7784f3fcda0573da8580689 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 Content-Type: text/plain; charset="utf-8" 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 --- 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