net: pse-pd: assorted lifetime and error-path fixes
Subsystems (3)
cover net: pse-pd: assorted lifetime and error-path fixes
Fixes for the PSE core and two of its drivers, found while working on the PSE controller teardown. Two patches depend on the one before them:
6 is only safe with the locked check from 5, and 8 relies on 7 to give back a budget that was already reserved.
1 The kfifo is too small: podl-pse-regulator and any controller with
0 or 1 lines cannot register.
2 A module reference leaks when reading the port state fails. 3 A failed IRQ request stays in pcdev->irq, so unregister disables a
line another device may own.
4 The notification worker can read a PHY that was already freed.
5 Setting a power limit checks the PI state without the lock.
6 A power domain shared by two controllers is freed when the one that
created it unbinds.
7 pd692x0 leaks its manager power budget when probe fails.
8 pd692x0 crashes on a manager supply that is not resolved yet.
9 si3474 logs through an I2C client it has already unregistered. 10 A low voltage reading overflows the current for a power limit and
hits a BUG_ON().
Testing: every patch builds on its own with W=1 and is checkpatch clean. 1, 4 and 6 were tested in QEMU (arm64, KASAN, lockdep, kmemleak) with a test-only fake PSE controller: net/main shows the kfifo failure and use-after-free reports, the series is clean. The other patches are build-tested only. Together with the PSE core fixes and the decouple series, the series also runs on a Hasivo S600WP (hs104), including setting power limits.
Pending work:
- "net: pse-pd: fix use-after-free of PI array on controller unregister" [1] applies with this series in either order.
- The PSE core fixes for net-next [2] conflict with 5 and 6; the note in their patch 2 explains how to merge them.
Not fixed here: a shared power domain's supply handle belongs to the PI regulator of the controller that created it, so it goes stale when that controller unbinds. That is older than this series.
[1] https://lore.kernel.org/netdev/[email protected]/
[2] https://lore.kernel.org/netdev/[email protected]/
Carlo Szelinsky (10):
net: pse-pd: size the notification kfifo after defaulting nr_lines
net: pse-pd: fix module refcount leak in pse_control_get_internal()
net: pse-pd: do not keep a failed IRQ in pcdev->irq
net: pse-pd: do not reach a freed PHY from the notification worker
net: pse-pd: check the PI state under the lock when setting a power
limit
net: pse-pd: do not tie a shared power domain to its creator's devres
net: pse-pd: pd692x0: release the manager power budget through devres
net: pse-pd: pd692x0: defer probe while a manager supply is unresolved
net: pse-pd: si3474: fix use-after-free in ancillary client error path
net: pse-pd: reject a power limit whose current does not fit an int
drivers/net/pse-pd/pd692x0.c | 31 ++++-- drivers/net/pse-pd/pse_core.c | 171 +++++++++++++++++++++++----------- drivers/net/pse-pd/si3474.c | 4 +- 3 files changed, 140 insertions(+), 66 deletions(-)
base-commit: aaaaf87ea99b8766c9a8aa0e71aa42e6bc8a5320
Signature · 1 line
2.43.0
PATCH 1/10 net: pse-pd: size the notification kfifo after defaulting nr_lines
pse_controller_register() allocates the notification kfifo before it turns nr_lines == 0 into 1, and kfifo_alloc() fails for a size below 2. So a controller with nr_lines 0 or 1 cannot register. pse_regulator (podl-pse-regulator) never sets nr_lines, so its probe has failed with -EINVAL ever since the kfifo was added.
Apply the default first, and allocate at least two entries.
--- drivers/net/pse-pd/pse_core.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
drivers/net/pse-pd/pse_core.c +7 -4
@@ -1044,16 +1044,19 @@ int pse_controller_register(struct pse_controller_dev *pcdev)mutex_init (&pcdev->lock);INIT_LIST_HEAD (&pcdev->pse_control_head);spin_lock_init (&pcdev->ntf_fifo_lock); - ret =kfifo_alloc (&pcdev->ntf_fifo, pcdev->nr_lines,GFP_KERNEL ); + +if (!pcdev->nr_lines) + pcdev->nr_lines =1 ; + +/* kfifo_alloc() rejects a size that rounds up to less than 2 */ + ret =kfifo_alloc (&pcdev->ntf_fifo,max (pcdev->nr_lines,2U ), +GFP_KERNEL );if (ret) {dev_err (pcdev->dev,"failed to allocate kfifo notifications\n" );return ret; }INIT_WORK (&pcdev->ntf_work, pse_send_ntf_worker); -if (!pcdev->nr_lines) - pcdev->nr_lines =1 ; -if (!pcdev->ops->pi_get_admin_state || !pcdev->ops->pi_get_pw_status) { dev_err(pcdev->dev,
Signature · 1 line
2.43.0
Re: [PATCH net 01/10] net: pse-pd: size the notification kfifo after defaulting nr_lines
Quoted 7 lines · Carlo Szelinsky
pse_controller_register()allocates the notification kfifo before it turnsnr_lines== 0 into 1, andkfifo_alloc()fails for a size below 2. So a controller withnr_lines0 or 1 cannot register.pse_regulator(podl-pse-regulator) never setsnr_lines, so its probe has failed with-EINVALever since the kfifo was added.Apply the default first, and allocate at least two entries.
Thank you!
Quoted 34 lines
Fixes ffef61d6d273 ("net: pse-pd: Add support for budget evaluation strategies")Signed-off-by Carlo Szelinsky <[email protected]>--- drivers/net/pse-pd/pse_core.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)drivers/net/pse-pd/pse_core.c +7
-4@@ -1044,16 +1044,19 @@ int pse_controller_register(struct pse_controller_dev *pcdev)mutex_init (&pcdev->lock);INIT_LIST_HEAD (&pcdev->pse_control_head);spin_lock_init (&pcdev->ntf_fifo_lock); - ret =kfifo_alloc (&pcdev->ntf_fifo, pcdev->nr_lines,GFP_KERNEL ); + +if (!pcdev->nr_lines) + pcdev->nr_lines =1 ; + +/* kfifo_alloc() rejects a size that rounds up to less than 2 */ + ret =kfifo_alloc (&pcdev->ntf_fifo,max (pcdev->nr_lines,2U ), +GFP_KERNEL );if (ret) {dev_err (pcdev->dev,"failed to allocate kfifo notifications\n" );return ret; }INIT_WORK (&pcdev->ntf_work, pse_send_ntf_worker); -if (!pcdev->nr_lines) - pcdev->nr_lines =1 ; -if (!pcdev->ops->pi_get_admin_state || !pcdev->ops->pi_get_pw_status) { dev_err(pcdev->dev,
Signature · 3 lines
Köry Maincent, Bootlin Embedded Linux and kernel engineering https://bootlin.com
PATCH 2/10 net: pse-pd: fix module refcount leak in pse_control_get_internal()
In reply to Carlo Szelinskypse_control_get_internal() takes a module reference and then has two error paths that skip module_put(): a controller without pi_get_admin_state(), and a failing pse_pi_is_hw_enabled(). The second one happens on any I2C or UART error. Each time, one more reference leaks, and the PSE driver can no longer be unloaded.
Send both to put_module, like the regulator_get_exclusive() error path.
--- drivers/net/pse-pd/pse_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/pse-pd/pse_core.c +2 -2
@@ -1427,7 +1427,7 @@ pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index,if (!pcdev->ops->pi_get_admin_state) { ret = -EOPNOTSUPP ; - goto free_psec; + goto put_module; }/* Initialize admin_state_enabled before the regulator_get. This
@@ -1436,7 +1436,7 @@ pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index,*/ ret =pse_pi_is_hw_enabled (pcdev, index);if (ret <0 ) - goto free_psec; + goto put_module; pcdev->pi[index].admin_state_enabled = ret; psec->ps = regulator_get_exclusive(pcdev->dev,
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH net 02/10] net: pse-pd: fix module refcount leak in pse_control_get_internal()
Thank you for your contribution! Sashiko AI review found more severe issue(s) in other patches of this series. This mail is only an FYI, in case you want to address the nit picks below when you…
Thank you for your contribution! Sashiko AI review found more severe issue(s) in other patches of this series. This mail is only an FYI, in case you want to address the nit picks below when you respin.
Low priority / nit picks for your consideration:
- [Low] The commit message names two leaking error paths in pse_control_get_internal() (drivers/net/pse-pd/pse_core.c): a…
--
net: pse-pd: fix module refcount leak in pse_control_get_internal()
This patch changes two error paths in pse_control_get_internal() to jump to put_module instead of free_psec. Without it, the module reference taken by try_module_get() is not dropped. One path is a controller without pi_get_admin_state(). The other is a failing pse_pi_is_hw_enabled().
Quoted 5 lines
pse_control_get_internal()takes a module reference and then has two error paths that skipmodule_put(): a controller withoutpi_get_admin_state(), and a failingpse_pi_is_hw_enabled(). The second one happens on any I2C or UART error. Each time, one more reference leaks, and the PSE driver can no longer be unloaded.
[Severity: Low]
The code change looks correct. Is the first path in this description actually reachable, though?
pse_controller_register() rejects a controller that has no pi_get_admin_state(), and it does so before the controller is added to pse_controller_list:
drivers/net/pse-pd/pse_core.c:pse_controller_register() {
...
if (!pcdev->ops->pi_get_admin_state ||
!pcdev->ops->pi_get_pw_status) {
dev_err(pcdev->dev,
"Mandatory status report callbacks are missing");
return -EINVAL;
}
...
mutex_lock(&pse_list_mutex);
list_add(&pcdev->list, &pse_controller_list);
mutex_unlock(&pse_list_mutex);
...
}The only caller of pse_control_get_internal() is of_pse_control_get(). It takes pcdev from that list:
list_for_each_entry(r, &pse_controller_list, list) {So every pcdev that reaches pse_control_get_internal() has pi_get_admin_state set. That means this check cannot fire:
if (!pcdev->ops->pi_get_admin_state) {
ret = -EOPNOTSUPP;
goto put_module;
}Changing its goto keeps the unwind consistent, but it does not fix a leak that can be hit. The leak that can be hit is the pse_pi_is_hw_enabled() failure path. It returns the driver's pi_get_admin_state() error, for example an I2C or UART failure.
Could the commit message say that the first branch is a defensive check and cannot be reached today? Then anyone judging the fix for stable would see that only the pse_pi_is_hw_enabled() path really leaks.
Signature · 1 line
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261018082314.48950-1-github%40szelinsky.de
Re: [PATCH net 02/10] net: pse-pd: fix module refcount leak in pse_control_get_internal()
Quoted 2 lines · On Mon, 19 Oct 2026 08:51:10 +0000, [email protected] wrote:
Could the commit message say that the first branch is a defensive check and cannot be reached today?
Right, it cannot be reached. As Kory asked, the next version removes that check in a patch before this one, so this patch only fixes the pse_pi_is_hw_enabled() path, and its commit message says only that.
Carlo
Re: [PATCH net 02/10] net: pse-pd: fix module refcount leak in pse_control_get_internal()
In reply to Carlo SzelinskyQuoted 10 lines · Carlo Szelinsky
pse_control_get_internal()takes a module reference and then has two error paths that skipmodule_put(): a controller withoutpi_get_admin_state(), and a failingpse_pi_is_hw_enabled(). The second one happens on any I2C or UART error. Each time, one more reference leaks, and the PSE driver can no longer be unloaded.Send both to
put_module, like theregulator_get_exclusive()error path.Fixes ffef61d6d273 ("net: pse-pd: Add support for budget evaluation strategies")Signed-off-by Carlo Szelinsky <[email protected]>
As spotted by sashiko you can remove the two checks on pi_get_admin_state callback as it is already done in the pse_controller_register() function. Maybe you can do it in a patch before this one even if it is not a fix.
With this change:
Thank you!
Quoted 26 lines
--- drivers/net/pse-pd/pse_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)drivers/net/pse-pd/pse_core.c +2
-2@@ -1427,7 +1427,7 @@ pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index,if (!pcdev->ops->pi_get_admin_state) { ret = -EOPNOTSUPP ; - goto free_psec; + goto put_module; }/* Initialize admin_state_enabled before the regulator_get. This @@ -1436,7 +1436,7 @@ pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index,*/ ret =pse_pi_is_hw_enabled (pcdev, index);if (ret <0 ) - goto free_psec; + goto put_module; pcdev->pi[index].admin_state_enabled = ret; psec->ps = regulator_get_exclusive(pcdev->dev,
Signature · 3 lines
Köry Maincent, Bootlin Embedded Linux and kernel engineering https://bootlin.com
Re: [PATCH net 02/10] net: pse-pd: fix module refcount leak in pse_control_get_internal()
Quoted 4 lines · Kory Maincent
As spotted by sashiko you can remove the two checks on
pi_get_admin_statecallback as it is already done in thepse_controller_register()function.
Maybe you can do it in a patch before this one even if it is not a fix.
Hi Kory,
Will do. In the next version a patch before this one removes both checks, in pse_control_get_internal() and pse_pi_is_enabled(). This patch then only fixes the pse_pi_is_hw_enabled() error path.
pw-bot: cr
Carlo
PATCH 3/10 net: pse-pd: do not keep a failed IRQ in pcdev->irq
In reply to Carlo Szelinskydevm_pse_irq_helper() keeps the IRQ number in pcdev->irq even when the IRQ request failed. The failed probe then runs pse_controller_unregister(), which calls disable_irq() on that line. If another device owns it, that device stops getting interrupts.
Clear pcdev->irq when the request fails. It is now set before the request, because pse_isr() can run as soon as the IRQ is enabled and pse_pw_d_is_sw_pw_control() reads it.
--- drivers/net/pse-pd/pse_core.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
drivers/net/pse-pd/pse_core.c +9 -3
@@ -1348,14 +1348,20 @@ int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq,if (!h->notifs_mask)return -ENOMEM ; +/* pse_isr() can run as soon as the IRQ is enabled, and +* pse_pw_d_is_sw_pw_control() reads pcdev->irq. +*/ + pcdev->irq = irq; ret =devm_request_threaded_irq (dev, irq,NULL , pse_isr,IRQF_ONESHOT | irq_flags, irq_name, h); -if (ret) +if (ret) { + pcdev->irq =0 ;dev_err (pcdev->dev,"Failed to request IRQ %d\n" , irq); +return ret; + } - pcdev->irq = irq; -return ret; +return 0 ; }EXPORT_SYMBOL_GPL (devm_pse_irq_helper);
Signature · 1 line
2.43.0
Re: [PATCH net 03/10] net: pse-pd: do not keep a failed IRQ in pcdev->irq
Quoted 8 lines · Carlo Szelinsky
devm_pse_irq_helper()keeps the IRQ number inpcdev->irqeven when the IRQ request failed. The failed probe then runspse_controller_unregister(), which callsdisable_irq()on that line. If another device owns it, that device stops getting interrupts.Clear
pcdev->irqwhen the request fails. It is now set before the request, becausepse_isr()can run as soon as the IRQ is enabled andpse_pw_d_is_sw_pw_control()reads it.
Thank you!
Signature · 3 lines
Köry Maincent, Bootlin Embedded Linux and kernel engineering https://bootlin.com
PATCH 4/10 net: pse-pd: do not reach a freed PHY from the notification worker
In reply to Carlo SzelinskyThe notification worker holds a reference on the pse_control, but not on the PHY that psec->attached_phydev points to. If the PHY is removed while the worker runs, the pse_control survives and the worker reads the freed PHY. The same happens without a race when two PHYs share one PI:attached_phydev keeps pointing at the first PHY after it is removed.
pse_control_put() unlinks and frees a pse_control under pse_list_mutex, and phy_device_remove() calls it before the PHY is freed. So instead of taking a reference, let the worker look the pse_control up and use attached_phydev under pse_list_mutex. This nests pse_list_mutex inside rtnl; nothing under pse_list_mutex takes rtnl.
For a PI shared by two PHYs there is no single netdev to report to, so clear attached_phydev when a second PHY takes the pse_control, and warn. Events for that PI are then no longer reported, even after one PHY goes away. Failing the second PHY instead would fail its whole MDIO bus.
--- drivers/net/pse-pd/pse_core.c | 42 ++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 11 deletions(-)
drivers/net/pse-pd/pse_core.c +31 -11
@@ -238,24 +238,22 @@ static int of_load_pse_pis(struct pse_controller_dev *pcdev)* @pcdev: a pointer to the PSE * @id: index of the PSE control * -* Return: pse_control pointer or NULL. The device returned has had a -* reference added and the pointer is safe until the user calls -* pse_control_put() to indicate they have finished with it. +* Must be called with pse_list_mutex held. No reference is taken: the +* pointer is only valid until pse_list_mutex is released. +* +* Return: pse_control pointer or NULL. */ static struct pse_control *pse_control_find_by_id (struct pse_controller_dev *pcdev,int id) {struct pse_control *psec; -mutex_lock (&pse_list_mutex); +lockdep_assert_held (&pse_list_mutex); +list_for_each_entry (psec, &pcdev->pse_control_head, list) { -if (psec->id == id) { -kref_get (&psec->refcnt); -mutex_unlock (&pse_list_mutex); +if (psec->id == id)return psec; - } } -mutex_unlock (&pse_list_mutex);return NULL ; }
@@ -268,6 +266,7 @@ pse_control_find_by_id(struct pse_controller_dev *pcdev, int id)static struct net_device *pse_control_get_netdev (struct pse_control *psec) {ASSERT_RTNL (); +lockdep_assert_held (&pse_list_mutex);if (!psec || !psec->attached_phydev)return NULL ;
@@ -1020,13 +1019,21 @@ static void pse_send_ntf_worker(struct work_struct *work)struct net_device *netdev;struct pse_control *psec; - psec =pse_control_find_by_id (pcdev, ntf.id); +/* A pse_control is only on the list while the PHY it was +* handed to holds it: the PHY's pse_control_put() unlinks +* and frees it under pse_list_mutex before the PHY itself +* can be freed. Holding the mutex across the lookup and the +* attached_phydev dereference therefore keeps the PHY alive, +* which a reference on the pse_control alone would not. +*/ rtnl_lock (); +mutex_lock (&pse_list_mutex); + psec =pse_control_find_by_id (pcdev, ntf.id); netdev =pse_control_get_netdev (psec);if (netdev)ethnl_pse_send_ntf (netdev, ntf.notifs); +mutex_unlock (&pse_list_mutex);rtnl_unlock (); -pse_control_put (psec); } }
@@ -1417,6 +1424,19 @@ pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index,list_for_each_entry (psec, &pcdev->pse_control_head, list) {if (psec->id == index) { +/* attached_phydev can only name one PHY. If a second +* one shares the PI, either may be removed first and +* leave it pointing at a freed phy_device, so stop +* reporting events for this PI instead. +*/ +if (psec->attached_phydev && + psec->attached_phydev != phydev) { +dev_warn (pcdev->dev, +"PI %u shared by several PHYs, not reporting its events\n" , + index); + psec->attached_phydev =NULL ; + } +kref_get (&psec->refcnt);return psec; }
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH net 04/10] net: pse-pd: do not reach a freed PHY from the notification worker
Thank you for your contribution! Sashiko AI review found more severe issue(s) in other patches of this series. This mail is only an FYI, in case you want to address the nit picks below when you…
Thank you for your contribution! Sashiko AI review found more severe issue(s) in other patches of this series. This mail is only an FYI, in case you want to address the nit picks below when you respin.
Low priority / nit picks for your consideration:
- [Low] The comment this patch adds to pse_send_ntf_worker() (drivers/net/pse-pd/pse_core.c, lines 1022-1028) says: "A pse_control…
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but phy_device_remove() (drivers/net/phy/phy_device.c:1155) calls…
--
net: pse-pd: do not reach a freed PHY from the notification worker
The notification worker used to take a reference on the pse_control. It now looks up the pse_control under pse_list_mutex and uses attached_phydev under that mutex, so a removed PHY cannot be reached. If a second PHY takes a pse_control for a shared PI, attached_phydev is cleared and a warning is printed.
Quoted 4 lines
drivers/net/pse-pd/pse_core.c +0
-0
[ ... ]
drivers/net/pse-pd/pse_core.c static void pse_send_ntf_worker(struct work_struct *work) · line 1028
Quoted 12 lines
drivers/net/pse-pd/pse_core.c +7
-1@@ -1020,13 +1019,21 @@ static void pse_send_ntf_worker(struct work_struct *work)struct net_device *netdev;struct pse_control *psec; - psec =pse_control_find_by_id (pcdev, ntf.id); +/* A pse_control is only on the list while the PHY it was +* handed to holds it: the PHY's pse_control_put() unlinks +* and frees it under pse_list_mutex before the PHY itself +* can be freed. Holding the mutex across the lookup and the +* attached_phydev dereference therefore keeps the PHY alive, +* which a reference on the pse_control alone would not. +*/
[Severity: Low]
This comment says a pse_control is only on the list while the PHY it was handed to holds it, and that the PHY's pse_control_put() unlinks and frees it. Does that hold when two PHYs share a PI?
In that case pse_control_get_internal() returns the existing psec after kref_get(). The unlink and free only happen on the last put:
drivers/net/pse-pd/pse_core.c:__pse_control_release() {
...
list_del(&psec->list);
kfree(psec);
}So the first PHY's phy_device_remove()->pse_control_put() only drops the refcount. The psec stays on pcdev->pse_control_head after that PHY can be freed.
The worker is still safe today, but only because pse_control_get_internal() clears psec->attached_phydev when a different PHY takes the psec. The comment gives list membership as the reason and does not mention that dependency.
Could the comment say that it relies on attached_phydev being cleared in the shared-PI case? Later patches in the series leave this comment unchanged.
[ ... ]
Quoted 10 lines
drivers/net/pse-pd/pse_core.c +6
-0@@ -1417,6 +1424,19 @@ pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index,list_for_each_entry (psec, &pcdev->pse_control_head, list) {if (psec->id == index) { +/* attached_phydev can only name one PHY. If a second +* one shares the PI, either may be removed first and +* leave it pointing at a freed phy_device, so stop +* reporting events for this PI instead. +*/ + if (psec->attached_phydev &&
drivers/net/pse-pd/pse_core.c pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index, · line 1442
Quoted 10 lines
drivers/net/pse-pd/pse_core.c +7
-0+ psec->attached_phydev != phydev) { +dev_warn (pcdev->dev, +"PI %u shared by several PHYs, not reporting its events\n" , + index); + psec->attached_phydev =NULL ; + } +kref_get (&psec->refcnt);return psec; }
[Severity: High]
This is a pre-existing issue and was not introduced by this patch. This patch relies on the pse_control_put() in phy_device_remove(). Does that call leave phydev->psec dangling?
drivers/net/phy/phy_device.c:phy_device_remove() {
unregister_mii_timestamper(phydev->mii_ts);
pse_control_put(phydev->psec);
device_del(&phydev->mdio.dev);
...
}With the usual one PHY per PI, this put drops the last reference and __pse_control_release() does list_del() and kfree(psec). phydev->psec is never set to NULL.
The phy_device can outlive phy_device_remove(), because phy_attach_direct() holds get_device() on it. The MAC<->PHY device_link is DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS, so the MAC is not unbound. phy_remove() also does not detach the netdev. After the MDIO bus is unregistered or unbound under an attached MAC, netdev->phydev still points at a phy_device whose psec has been freed.
ETHTOOL_MSG_PSE_GET does not need admin permissions, and it reaches:
pse_prepare_data()
pse_get_pse_attributes()
pse_ethtool_get_status(phydev->psec, ...)
net/ethtool/pse-pd.c:pse_get_pse_attributes() {
...
if (!phydev->psec) {
NL_SET_ERR_MSG(extack, "No PSE is attached");
return -EOPNOTSUPP;
}
...
return pse_ethtool_get_status(phydev->psec, extack, &data->status);
}Can this dereference the freed pse_control? The PSE SET handlers in net/ethtool/pse-pd.c appear to follow the same pattern.
There may also be a race during removal. pse_control_put() in phy_device_remove() runs without rtnl, but the ethtool handlers read phydev->psec under rtnl. Could a concurrent PSE_GET or PSE_SET use the psec while it is being freed?
Would clearing phydev->psec after the put, synchronized with the rtnl-protected readers, address both cases? The pse_control_put() call in phy_device_remove() dates back to commit 5e82147de1cbd7 in 2022. No later patch in this series touches phy_device_remove() or the ethtool PSE handlers.
Signature · 1 line
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261018082314.48950-1-github%40szelinsky.de
Re: [PATCH net 04/10] net: pse-pd: do not reach a freed PHY from the notification worker
Quoted 3 lines · On Mon, 19 Oct 2026 08:51:12 +0000, [email protected] wrote:
[Severity: Low]
Could the comment say that it relies onattached_phydevbeing cleared in the shared-PI case?
Yes. I will add that to the comment in the next version.
Quoted 4 lines
[Severity: High]
This is a pre-existing issue and was not introduced by this patch. This patch relies on thepse_control_put()inphy_device_remove(). Does that call leavephydev->psecdangling?
Yes, an older bug, and it is not fixed in net. The PSE notifier series for net-next fixes it: phy_device_remove() clears phydev->psec under pse_phy_lock(), and the ethtool PSE handlers take the same lock, so a request that is already running ends before the handle is dropped.
Carlo
PATCH 5/10 net: pse-pd: check the PI state under the lock when setting a power limit
In reply to Carlo Szelinskypse_ethtool_set_pw_limit() checks whether the PI is powered without pcdev->lock. pse_isr() changes that state under the lock, so if the PD disconnects in between, budget is reserved for a port that is no longer powered.
Do the check in pse_pi_update_pw_budget() under the lock, and report whether the budget was changed, so the rollback after a failed regulator_set_current_limit() runs only then. This also rolls back a budget that started from zero, which the old code skipped.
--- drivers/net/pse-pd/pse_core.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-)
drivers/net/pse-pd/pse_core.c +21 -13
@@ -1829,22 +1829,32 @@ EXPORT_SYMBOL_GPL(pse_ethtool_set_config);* @pcdev: a pointer to the PSE controller device * @id: index of the PSE PI * @pw_req: power requested +* @updated: set to true if the budget was changed * @extack: extack for reporting useful error messages * * Return: Previous power allocated on success and failure value on error */ static int pse_pi_update_pw_budget (struct pse_controller_dev *pcdev,int id, -const unsigned int pw_req, +const unsigned int pw_req,bool *updated,struct netlink_ext_ack *extack) {struct pse_pi *pi = &pcdev->pi[id];int previous_pw_allocated;int pw_diff, ret =0 ; + *updated = false; +/* We don't want pw_allocated_mW value change in the middle of an * power budget update */ mutex_lock (&pcdev->lock); +/* Only a powered PI under software power control holds a budget. +* Check it here, as pse_isr() changes it under the same lock. +*/ +if (!pse_pw_d_is_sw_pw_control (pcdev, pi->pw_d) || + !pi->admin_state_enabled || !pi->isr_pd_detected) + goto out; + previous_pw_allocated = pi->pw_allocated_mW; pw_diff = pw_req - previous_pw_allocated;if (!pw_diff) {
@@ -1862,6 +1872,7 @@ static int pse_pi_update_pw_budget(struct pse_controller_dev *pcdev, int id,regulator_free_power_budget (pi->pw_d->supply, -pw_diff); } pi->pw_allocated_mW = pw_req; + *updated = true; ret = previous_pw_allocated; out:
@@ -1882,6 +1893,7 @@ int pse_ethtool_set_pw_limit(struct pse_control *psec, const unsigned int pw_limit) {int uV, uA, ret, previous_pw_allocated =0 ; +bool updated;s64 tmp_64;if (pw_limit >MAX_PI_PW )
@@ -1908,21 +1920,17 @@ int pse_ethtool_set_pw_limit(struct pse_control *psec,/* Update power budget only in software power control case and * if a Power Device is powered. */ -if (pse_pw_d_is_sw_pw_control (psec->pcdev, - psec->pcdev->pi[psec->id].pw_d) && - psec->pcdev->pi[psec->id].admin_state_enabled && - psec->pcdev->pi[psec->id].isr_pd_detected) { - ret =pse_pi_update_pw_budget (psec->pcdev, psec->id, - pw_limit, extack); -if (ret <0 ) -return ret; - previous_pw_allocated = ret; - } + ret =pse_pi_update_pw_budget (psec->pcdev, psec->id, pw_limit, + &updated, extack); +if (ret <0 ) +return ret; + previous_pw_allocated = ret; ret =regulator_set_current_limit (psec->ps,0 , uA); -if (ret <0 && previous_pw_allocated) { +if (ret <0 && updated) {pse_pi_update_pw_budget (psec->pcdev, psec->id, - previous_pw_allocated, extack); + previous_pw_allocated, &updated, + extack); }return ret;
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH net 05/10] net: pse-pd: check the PI state under the lock when setting a power limit
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider.
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider.
Critical: 0 · High: 0 · Medium: 1 · Low: 1
If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough).
- [Medium] The patch puts the PD-state check and the budget change in one pcdev->lock critical section inside pse_pi_update_pw_budget().
- [Low] The kernel-doc for pse_pi_update_pw_budget() (drivers/net/pse- pd/pse_core.c:1835) still says "Return: Previous power allocated on…
--
net: pse-pd: check the PI state under the lock when setting a power limit
This moves the software power control, admin state and PD detected checks from pse_ethtool_set_pw_limit() into pse_pi_update_pw_budget(), so they now run under pcdev->lock. A new updated output parameter reports whether the budget changed. The rollback after a failed regulator_set_current_limit() now runs only when updated is true.
Quoted 18 lines
drivers/net/pse-pd/pse_core.c +2
-1@@ -1829,22 +1829,32 @@ EXPORT_SYMBOL_GPL(pse_ethtool_set_config);* @pcdev: a pointer to the PSE controller device * @id: index of the PSE PI * @pw_req: power requested +* @updated: set to true if the budget was changed * @extack: extack for reporting useful error messages * * Return: Previous power allocated on success and failure value on error */ static int pse_pi_update_pw_budget (struct pse_controller_dev *pcdev,int id, -const unsigned int pw_req, +const unsigned int pw_req,bool *updated,struct netlink_ext_ack *extack) {
[ ... ]
drivers/net/pse-pd/pse_core.c EXPORT_SYMBOL_GPL(pse_ethtool_set_config); · line 1857
Quoted 8 lines
drivers/net/pse-pd/pse_core.c +7
-0mutex_lock (&pcdev->lock); +/* Only a powered PI under software power control holds a budget. +* Check it here, as pse_isr() changes it under the same lock. +*/ +if (!pse_pw_d_is_sw_pw_control (pcdev, pi->pw_d) || + !pi->admin_state_enabled || !pi->isr_pd_detected) + goto out; +
[Severity: Low]
This isn't a bug, but the kernel-doc for pse_pi_update_pw_budget() still says:
* Return: Previous power allocated on success and failure value on error
This new early exit returns 0 with *updated = false, whatever pi->pw_allocated_mW holds. The existing !pw_diff exit also returns 0.
pw_allocated_mW can be non-zero when this check fails. For example, _pse_pi_disable() returns before pse_pi_deallocate_pw_budget() if ops->pi_disable() fails, but pse_set_config_isr() still clears isr_pd_detected on disconnection.
Could the Return description say that 0 can also mean no change was made? It could also say that callers should check *updated, not the return value, before rolling back.
Quoted 3 lines
drivers/net/pse-pd/pse_core.c +0
-0previous_pw_allocated = pi->pw_allocated_mW; pw_diff = pw_req - previous_pw_allocated;if (!pw_diff) {
[ ... ]
Quoted 11 lines
drivers/net/pse-pd/pse_core.c +0
-7@@ -1908,21 +1920,17 @@ int pse_ethtool_set_pw_limit(struct pse_control *psec,/* Update power budget only in software power control case and * if a Power Device is powered. */ -if (pse_pw_d_is_sw_pw_control (psec->pcdev, - psec->pcdev->pi[psec->id].pw_d) && - psec->pcdev->pi[psec->id].admin_state_enabled && - psec->pcdev->pi[psec->id].isr_pd_detected) { - ret =pse_pi_update_pw_budget (psec->pcdev, psec->id, - pw_limit, extack); -if (ret <0 )
drivers/net/pse-pd/pse_core.c int pse_ethtool_set_pw_limit(struct pse_control *psec, · line 1929
Quoted 10 lines
drivers/net/pse-pd/pse_core.c +5
-3-return ret; - previous_pw_allocated = ret; - } + ret =pse_pi_update_pw_budget (psec->pcdev, psec->id, pw_limit, + &updated, extack); +if (ret <0 ) +return ret; + previous_pw_allocated = ret; ret =regulator_set_current_limit (psec->ps,0 , uA);
[Severity: Medium]
The scenario in the commit message, where budget is reserved for a port that is no longer powered, looks closed now. But can the budget and the hardware limit still drift apart in the other direction?
The state check and the budget change now share one pcdev->lock critical section. The hardware limit is still written in a separate one:
pse_ethtool_set_pw_limit()
pse_pi_update_pw_budget() <- takes and drops pcdev->lock
regulator_set_current_limit()
pse_pi_set_current_limit() <- takes pcdev->lock again
ops->pi_set_pw_limit()The threaded pse_isr() only needs pcdev->lock, so it can run in between:
CPU0 (ethtool set_pw_limit)pse_pi_update_pw_budget()
sees isr_pd_detected == false
*updated = false, nothing reserved
mutex_unlock(&pcdev->lock)
CPU1 (pse_isr, classification event)pse_set_config_isr()
isr_pd_detected = true
_pse_pi_delivery_power_sw_pw_ctrl()
pw_req = min(pi_get_pw_req(), pi_get_pw_limit()) <- old hw limit
pse_pi_allocate_pw_budget(pcdev, id, pw_req, extack)
ops->pi_enable()CPU0regulator_set_current_limit()
pse_pi_set_current_limit()
ops->pi_set_pw_limit() <- new, higher limit
The port would then be powered with a hardware limit above its reserved budget, and nothing later corrects pi->pw_allocated_mW. Could that over-subscribe the power domain?
A disconnect followed by a reconnect between the two sections seems to end the same way. If regulator_set_current_limit() then fails, this rollback writes back the previous_pw_allocated value that was read before the reconnect:
if (ret < 0 && updated) {
pse_pi_update_pw_budget(psec->pcdev, psec->id,
previous_pw_allocated, &updated,
extack);Would a full fix need the state check, the budget update and ops->pi_set_pw_limit() in a single pcdev->lock critical section? pse_pi_set_current_limit() takes pcdev->lock itself, so that looks like it needs a different structure from calling regulator_set_current_limit() after pse_pi_update_pw_budget().
Quoted 9 lines
drivers/net/pse-pd/pse_core.c +3
-2-if (ret <0 && previous_pw_allocated) { +if (ret <0 && updated) {pse_pi_update_pw_budget (psec->pcdev, psec->id, - previous_pw_allocated, extack); + previous_pw_allocated, &updated, + extack); }return ret;
Signature · 1 line
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261018082314.48950-1-github%40szelinsky.de
Kory Maincent
maintainerRe: [PATCH net 05/10] net: pse-pd: check the PI state under the lock when setting a power limit
Quoted 128 lines · On 10/19/26 10:51, [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider.
Critical: 0 · High: 0 · Medium: 1 · Low: 1
If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough).
- [Medium] The patch puts the PD-state check and the budget change in one
pcdev->lockcritical section insidepse_pi_update_pw_budget().
- [Low] The kernel-doc forpse_pi_update_pw_budget()(drivers/net/pse-pd/pse_core.c:1835) still says "Return: Previous power allocated on…--
net: pse-pd: check the PI state under the lock when setting a power limit
This moves the software power control, admin state and PD detected checks from
pse_ethtool_set_pw_limit()intopse_pi_update_pw_budget(), so they now run underpcdev->lock. A new updated output parameter reports whether the budget changed. The rollback after a failedregulator_set_current_limit()now runs only when updated is true.Quoted 18 lines
drivers/net/pse-pd/pse_core.c +2
-1@@ -1829,22 +1829,32 @@ EXPORT_SYMBOL_GPL(pse_ethtool_set_config);* @pcdev: a pointer to the PSE controller device * @id: index of the PSE PI * @pw_req: power requested +* @updated: set to true if the budget was changed * @extack: extack for reporting useful error messages * * Return: Previous power allocated on success and failure value on error */ static int pse_pi_update_pw_budget (struct pse_controller_dev *pcdev,int id, -const unsigned int pw_req, +const unsigned int pw_req,bool *updated,struct netlink_ext_ack *extack) {[ ... ]
drivers/net/pse-pd/pse_core.c EXPORT_SYMBOL_GPL(pse_ethtool_set_config); · line 1857
Quoted 8 lines
mutex_lock(&pcdev->lock);drivers/net/pse-pd/pse_core.c +7
-0+/* Only a powered PI under software power control holds a budget. +* Check it here, as pse_isr() changes it under the same lock. +*/ +if (!pse_pw_d_is_sw_pw_control (pcdev, pi->pw_d) || + !pi->admin_state_enabled || !pi->isr_pd_detected) + goto out; +[Severity: Low]
This isn't a bug, but the kernel-doc forpse_pi_update_pw_budget()still says:* Return: Previous power allocated on success and failure value on error
This new early exit returns 0 with *updated = false, whatever
pi->pw_allocated_mWholds. The existing !pw_diff exit also returns 0.
pw_allocated_mWcan be non-zero when this check fails. For example,_pse_pi_disable()returns beforepse_pi_deallocate_pw_budget()ifops->pi_disable()fails, butpse_set_config_isr()still clearsisr_pd_detectedon disconnection.Could the Return description say that 0 can also mean no change was made? It could also say that callers should check *updated, not the return value, before rolling back.
Quoted 3 lines
previous_pw_allocated = pi->pw_allocated_mW; pw_diff = pw_req - previous_pw_allocated; if (!pw_diff) {[ ... ]
Quoted 11 lines
drivers/net/pse-pd/pse_core.c +0
-7@@ -1908,21 +1920,17 @@ int pse_ethtool_set_pw_limit(struct pse_control *psec,/* Update power budget only in software power control case and * if a Power Device is powered. */ -if (pse_pw_d_is_sw_pw_control (psec->pcdev, - psec->pcdev->pi[psec->id].pw_d) && - psec->pcdev->pi[psec->id].admin_state_enabled && - psec->pcdev->pi[psec->id].isr_pd_detected) { - ret =pse_pi_update_pw_budget (psec->pcdev, psec->id, - pw_limit, extack); -if (ret <0 )drivers/net/pse-pd/pse_core.c int pse_ethtool_set_pw_limit(struct pse_control *psec, · line 1929
Quoted 10 lines
drivers/net/pse-pd/pse_core.c +5
-3-return ret; - previous_pw_allocated = ret; - } + ret =pse_pi_update_pw_budget (psec->pcdev, psec->id, pw_limit, + &updated, extack); +if (ret <0 ) +return ret; + previous_pw_allocated = ret; ret =regulator_set_current_limit (psec->ps,0 , uA);[Severity: Medium]
The scenario in the commit message, where budget is reserved for a port that is no longer powered, looks closed now. But can the budget and the hardware limit still drift apart in the other direction?The state check and the budget change now share one
pcdev->lockcritical section. The hardware limit is still written in a separate one:
pse_ethtool_set_pw_limit()
pse_pi_update_pw_budget()<- takes and dropspcdev->lock
regulator_set_current_limit()pse_pi_set_current_limit() <- takes pcdev->lock again ops->pi_set_pw_limit()The threaded
pse_isr()only needspcdev->lock, so it can run in between:CPU0 (ethtool
set_pw_limit)pse_pi_update_pw_budget()
seesisr_pd_detected== false
*updated = false, nothing reserved
mutex_unlock(&pcdev->lock)CPU1 (
pse_isr, classification event)pse_set_config_isr()
isr_pd_detected= true
_pse_pi_delivery_power_sw_pw_ctrl()
Quoted 8 lines
pw_req = min(pi_get_pw_req(), pi_get_pw_limit()) <- old hw limit pse_pi_allocate_pw_budget(pcdev, id, pw_req, extack) ops->pi_enable()CPU0
regulator_set_current_limit()
pse_pi_set_current_limit()ops->pi_set_pw_limit() <- new, higher limit
@Oleksij, Here is another example of an issue related to the PSE PI being a regulator provider.
@Carlo, Kyle is working on a series that remove the support for a PSE PI being a regulator provider. We will see where it goes. I think you can ignore this sashiko point as for now.
Quoted 28 lines
The port would then be powered with a hardware limit above its reserved budget, and nothing later corrects
pi->pw_allocated_mW. Could that over-subscribe the power domain?A disconnect followed by a reconnect between the two sections seems to end the same way. If
regulator_set_current_limit()then fails, this rollback writes back theprevious_pw_allocatedvalue that was read before the reconnect:if (ret < 0 && updated) { pse_pi_update_pw_budget(psec->pcdev, psec->id, previous_pw_allocated, &updated, extack);Would a full fix need the state check, the budget update and
ops->pi_set_pw_limit()in a singlepcdev->lockcritical section?pse_pi_set_current_limit()takespcdev->lockitself, so that looks like it needs a different structure from callingregulator_set_current_limit()afterpse_pi_update_pw_budget().Quoted 8 lines
drivers/net/pse-pd/pse_core.c +3
-2-if (ret <0 && previous_pw_allocated) { +if (ret <0 && updated) {pse_pi_update_pw_budget (psec->pcdev, psec->id, - previous_pw_allocated, extack); + previous_pw_allocated, &updated, + extack); }
Quoted 1 line
drivers/net/pse-pd/pse_core.c int pse_ethtool_set_pw_limit(struct pse_control *psec, · line 1936
Quoted 1 line
drivers/net/pse-pd/pse_core.c +0
-0return ret;
With the change ask by sashiko on the kdoc you can add my Reviewed-by tag.
Signature · 3 lines
Köry Maincent, Bootlin Embedded Linux and kernel engineering https://bootlin.com
Re: [PATCH net 05/10] net: pse-pd: check the PI state under the lock when setting a power limit
Quoted 1 line · Kory Maincent
I think you can ignore this sashiko point as for now.
[...]
Quoted 1 line
With the change ask by sashiko on the kdoc you can add my Reviewed-by tag.
Thanks, Kory. I will fix the kernel-doc and leave the race for now.
Carlo
Oleksij Rempel
maintainerRe: [PATCH net 05/10] net: pse-pd: check the PI state under the lock when setting a power limit
In reply to Kory MaincentCCing regulator related folks.
Quoted 64 lines · Kory Maincent
Quoted 53 lines · On 10/19/26 10:51, [email protected] wrote:
Quoted 3 lines
previous_pw_allocated = pi->pw_allocated_mW; pw_diff = pw_req - previous_pw_allocated; if (!pw_diff) {[ ... ]
Quoted 10 lines
drivers/net/pse-pd/pse_core.c +0
-6@@ -1908,21 +1920,17 @@ int pse_ethtool_set_pw_limit(struct pse_control *psec,/* Update power budget only in software power control case and * if a Power Device is powered. */ -if (pse_pw_d_is_sw_pw_control (psec->pcdev, - psec->pcdev->pi[psec->id].pw_d) && - psec->pcdev->pi[psec->id].admin_state_enabled && - psec->pcdev->pi[psec->id].isr_pd_detected) { - ret =pse_pi_update_pw_budget (psec->pcdev, psec->id, - pw_limit, extack);drivers/net/pse-pd/pse_core.c int pse_ethtool_set_pw_limit(struct pse_control *psec, · line 1928
Quoted 10 lines
drivers/net/pse-pd/pse_core.c +5
-4-if (ret <0 ) -return ret; - previous_pw_allocated = ret; - } + ret =pse_pi_update_pw_budget (psec->pcdev, psec->id, pw_limit, + &updated, extack); +if (ret <0 ) +return ret; + previous_pw_allocated = ret; ret =regulator_set_current_limit (psec->ps,0 , uA);[Severity: Medium]
The scenario in the commit message, where budget is reserved for a port that is no longer powered, looks closed now. But can the budget and the hardware limit still drift apart in the other direction?The state check and the budget change now share one
pcdev->lockcritical section. The hardware limit is still written in a separate one:
pse_ethtool_set_pw_limit()
pse_pi_update_pw_budget()<- takes and dropspcdev->lock
regulator_set_current_limit()pse_pi_set_current_limit() <- takes pcdev->lock again ops->pi_set_pw_limit()The threaded
pse_isr()only needspcdev->lock, so it can run in between:CPU0 (ethtool
set_pw_limit)pse_pi_update_pw_budget()
seesisr_pd_detected== false
*updated = false, nothing reserved
mutex_unlock(&pcdev->lock)CPU1 (
pse_isr, classification event)pse_set_config_isr()
isr_pd_detected= true
_pse_pi_delivery_power_sw_pw_ctrl()Quoted 8 lines
pw_req = min(pi_get_pw_req(), pi_get_pw_limit()) <- old hw limit pse_pi_allocate_pw_budget(pcdev, id, pw_req, extack) ops->pi_enable()CPU0
regulator_set_current_limit()
pse_pi_set_current_limit()ops->pi_set_pw_limit() <- new, higher limit
Quoted 5 lines
@Oleksij, Here is another example of an issue related to the PSE PI being a regulator provider.
@Carlo, Kyle is working on a series that remove the support for a PSE PI being a regulator provider. We will see where it goes.
Removing the regulator provider support is a bad idea:
- We will end up implementing a poor man's regulator framework inside the network subsystem. Networking folks will have to deal with physical constraints already solved by the power management community, requiring a different mindset and domain experience.
- We lose built-in regulator infrastructure: DT safety constraints (regulator-max-microamp), over-current/under-voltage notifications, and sysfs/debugfs (regulator_summary) visibility.
- The race above is just a lock scope issue across pcdev->lock and the regulator call, not a flaw in the abstraction.
- Writing a custom power state machine in net/ will introduce more bugs than it fixes - especially if we overestimate our ability to upstream complex custom logic under LLM review supervision.
Let's fix the lock serialization instead of dropping the framework.
Signature · 4 lines
Pengutronix e.K. | | Steuerwalder Str. 21 | http://www.pengutronix.de/ | 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
Kory Maincent
maintainerRe: [PATCH net 05/10] net: pse-pd: check the PI state under the lock when setting a power limit
Quoted 73 lines · Oleksij Rempel
CCing regulator related folks.
Quoted 64 lines · Kory Maincent
Quoted 57 lines · On 10/19/26 10:51, [email protected] wrote:
Quoted 3 lines
previous_pw_allocated = pi->pw_allocated_mW; pw_diff = pw_req - previous_pw_allocated; if (!pw_diff) {[ ... ]
Quoted 10 lines
drivers/net/pse-pd/pse_core.c +0
-6@@ -1908,21 +1920,17 @@ int pse_ethtool_set_pw_limit(struct pse_control *psec,/* Update power budget only in software power control case and * if a Power Device is powered. */ -if (pse_pw_d_is_sw_pw_control (psec->pcdev, - psec->pcdev->pi[psec->id].pw_d) && - psec->pcdev->pi[psec->id].admin_state_enabled && - psec->pcdev->pi[psec->id].isr_pd_detected) { - ret =pse_pi_update_pw_budget (psec->pcdev, psec->id, - pw_limit, extack);drivers/net/pse-pd/pse_core.c int pse_ethtool_set_pw_limit(struct pse_control *psec, · line 1928
Quoted 10 lines
drivers/net/pse-pd/pse_core.c +5
-4-if (ret <0 ) -return ret; - previous_pw_allocated = ret; - } + ret =pse_pi_update_pw_budget (psec->pcdev, psec->id, pw_limit, + &updated, extack); +if (ret <0 ) +return ret; + previous_pw_allocated = ret; ret =regulator_set_current_limit (psec->ps,0 , uA);[Severity: Medium]
The scenario in the commit message, where budget is reserved for a port that is no longer powered, looks closed now. But can the budget and the hardware limit still drift apart in the other direction?The state check and the budget change now share one
pcdev->lockcritical section. The hardware limit is still written in a separate one:
pse_ethtool_set_pw_limit()pse_pi_update_pw_budget() <- takes and drops pcdev->lock regulator_set_current_limit() pse_pi_set_current_limit() <- takes pcdev->lock again ops->pi_set_pw_limit()The threaded
pse_isr()only needspcdev->lock, so it can run in between:CPU0 (ethtool
set_pw_limit)pse_pi_update_pw_budget()sees isr_pd_detected == false *updated = false, nothing reserved mutex_unlock(&pcdev->lock)CPU1 (
pse_isr, classification event)pse_set_config_isr()isr_pd_detected = true _pse_pi_delivery_power_sw_pw_ctrl() pw_req = min(pi_get_pw_req(), pi_get_pw_limit()) <- old hw limit pse_pi_allocate_pw_budget(pcdev, id, pw_req, extack) ops->pi_enable()Quoted 4 lines
CPU0
regulator_set_current_limit()pse_pi_set_current_limit() ops->pi_set_pw_limit() <- new, higher limitQuoted 5 lines
@Oleksij, Here is another example of an issue related to the PSE PI being a regulator provider.
@Carlo, Kyle is working on a series that remove the support for a PSE PI being a regulator provider. We will see where it goes.
Quoted 6 lines
Removing the regulator provider support is a bad idea:
- We will end up implementing a poor man's regulator framework inside the network subsystem. Networking folks will have to deal with physical constraints already solved by the power management community, requiring a different mindset and domain experience.
That's what we thought at the beginning of the implementation of PoE but in fact that's not the case. I don't see currently any cases of using already solved regulator constraint in the PoE core. The drawback are:
- locks issues: solved one by using worker but there are still others
appearing like this one
- code complexity due to all the regulator callback wrapper
- converting mW to mA for regulator framework then to mW again for the
PSE driver callbacks. There are even devices that are using current
limit therefore there will be 3 conversions.
The more painful issues are the lock issues.
Quoted 3 lines
- We lose built-in regulator infrastructure: DT safety constraints (regulator-max-microamp), over-current/under-voltage notifications, and sysfs/debugfs (
regulator_summary) visibility.
I disagree here.
regulator-max-microamp: has not interest from the PI side but this is related to its power supply. The PI should still stay as a regulator consumer so the power supply could describe this binding.
over-current/under-voltage notifications: we are already reporting these PSE PIs notification through ethtool. We will loose regulator_notifier_call_chain() call but well there is no consumer to the PI regulator provider so it's mainly useless.
Yes, we will loose sysfs/debugfs visibility, but is someone really using it as all PSE communication goes through ethtool?
Quoted 2 lines
- The race above is just a lock scope issue across
pcdev->lockand the regulator call, not a flaw in the abstraction.
That's not something easy to solve as PSE and regulator provider are nested together.
Either we put all of the lock issues inside workers, it will not be synchronous and will smell hackish but well... Or we try to combine the PSE lock with the regulator nested lock in one way or another.
Do you have a better idea?
Quoted 3 lines
- Writing a custom power state machine in
net/will introduce more bugs than it fixes - especially if we overestimate our ability to upstream complex custom logic under LLM review supervision.
Removing regulator provider will simplify a lot the implementation so I think it will rather remove bugs than introduce new one. I think we should let Kyle works on it with a RFC and we will see where it goes.
Regards,
Signature · 3 lines
Köry Maincent, Bootlin Embedded Linux and kernel engineering https://bootlin.com
Re: [PATCH net 05/10] net: pse-pd: check the PI state under the lock when setting a power limit
Quoted 11 lines · Kory Maincent
Quoted 8 lines · Oleksij Rempel
CCing regulator related folks.
Quoted 5 lines · Kory Maincent
Quoted 3 lines · On 10/19/26 10:51, [email protected] wrote:
Quoted 3 lines
previous_pw_allocated = pi->pw_allocated_mW; pw_diff = pw_req - previous_pw_allocated; if (!pw_diff) {
[...]
Quoted 58 lines
Quoted 10 lines
Quoted 9 lines
Quoted 3 lines
regulator_set_current_limit()pse_pi_set_current_limit() ops->pi_set_pw_limit() <- new, higher limit@Oleksij, Here is another example of an issue related to the PSE PI being a regulator provider.
@Carlo, Kyle is working on a series that remove the support for a PSE PI being a regulator provider. We will see where it goes.
Quoted 6 lines
Removing the regulator provider support is a bad idea:
- We will end up implementing a poor man's regulator framework inside the network subsystem. Networking folks will have to deal with physical constraints already solved by the power management community, requiring a different mindset and domain experience.
That's what we thought at the beginning of the implementation of PoE but in fact that's not the case. I don't see currently any cases of using already solved regulator constraint in the PoE core.
The drawback are:
- locks issues: solved one by using worker but there are still others appearing like this one
- code complexity due to all the regulator callback wrapper
- converting mW to mA for regulator framework then to mW again for the PSE driver callbacks. There are even devices that are using current limit therefore there will be 3 conversions.The more painful issues are the lock issues.
Quoted 3 lines
- We lose built-in regulator infrastructure: DT safety constraints (regulator-max-microamp), over-current/under-voltage notifications, and sysfs/debugfs (
regulator_summary) visibility.I disagree here.
regulator-max-microamp: has not interest from the PI side but this is related to its power supply. The PI should still stay as a regulator consumer so the power supply could describe this binding.over-current/under-voltage notifications: we are already reporting these PSE PIs notification through ethtool. We will loose
regulator_notifier_call_chain()call but well there is no consumer to the PI regulator provider so it's mainly useless.Yes, we will loose sysfs/debugfs visibility, but is someone really using it as all PSE communication goes through ethtool?
Quoted 2 lines
- The race above is just a lock scope issue across
pcdev->lockand the regulator call, not a flaw in the abstraction.That's not something easy to solve as PSE and regulator provider are nested together.
Either we put all of the lock issues inside workers, it will not be synchronous and will smell hackish but well...
Or we try to combine the PSE lock with the regulator nested lock in one way or another.
Do you have a better idea?
Quoted 8 lines
Quoted 3 lines
- Writing a custom power state machine in
net/will introduce more bugs than it fixes - especially if we overestimate our ability to upstream complex custom logic under LLM review supervision.Removing regulator provider will simplify a lot the implementation so I think it will rather remove bugs than introduce new one.
I think we should let Kyle works on it with a RFC and we will see where it goes.
For what it's worth, and for others that might be interested but I missed on cc, the RFC is here:
https://lore.kernel.org/netdev/[email protected]/
Thanks,
Kyle
Kory Maincent
maintainerRe: [PATCH net 05/10] net: pse-pd: check the PI state under the lock when setting a power limit
Quoted 82 lines · Kyle Swenson
Quoted 11 lines · Kory Maincent
Quoted 8 lines · Oleksij Rempel
CCing regulator related folks.
Quoted 5 lines · Kory Maincent
Quoted 3 lines · On 10/19/26 10:51, [email protected] wrote:
Quoted 3 lines
previous_pw_allocated = pi->pw_allocated_mW; pw_diff = pw_req - previous_pw_allocated; if (!pw_diff) {[...]
Quoted 58 lines
Quoted 10 lines
Quoted 9 lines
Quoted 3 lines
regulator_set_current_limit()pse_pi_set_current_limit() ops->pi_set_pw_limit() <- new, higher limit@Oleksij, Here is another example of an issue related to the PSE PI being a regulator provider.
@Carlo, Kyle is working on a series that remove the support for a PSE PI being a regulator provider. We will see where it goes.
Quoted 6 lines
Removing the regulator provider support is a bad idea:
- We will end up implementing a poor man's regulator framework inside the network subsystem. Networking folks will have to deal with physical constraints already solved by the power management community, requiring a different mindset and domain experience.
That's what we thought at the beginning of the implementation of PoE but in fact that's not the case. I don't see currently any cases of using already solved regulator constraint in the PoE core.
The drawback are:
- locks issues: solved one by using worker but there are still others appearing like this one
- code complexity due to all the regulator callback wrapper
- converting mW to mA for regulator framework then to mW again for the PSE driver callbacks. There are even devices that are using current limit therefore there will be 3 conversions.The more painful issues are the lock issues.
Quoted 3 lines
- We lose built-in regulator infrastructure: DT safety constraints (regulator-max-microamp), over-current/under-voltage notifications, and sysfs/debugfs (
regulator_summary) visibility.I disagree here.
regulator-max-microamp: has not interest from the PI side but this is related to its power supply. The PI should still stay as a regulator consumer so the power supply could describe this binding.over-current/under-voltage notifications: we are already reporting these PSE PIs notification through ethtool. We will loose
regulator_notifier_call_chain()call but well there is no consumer to the PI regulator provider so it's mainly useless.Yes, we will loose sysfs/debugfs visibility, but is someone really using it as all PSE communication goes through ethtool?
Quoted 2 lines
- The race above is just a lock scope issue across
pcdev->lockand the regulator call, not a flaw in the abstraction.That's not something easy to solve as PSE and regulator provider are nested together.
Either we put all of the lock issues inside workers, it will not be synchronous and will smell hackish but well...
Or we try to combine the PSE lock with the regulator nested lock in one way or another.
Do you have a better idea?Quoted 8 lines
Quoted 3 lines
- Writing a custom power state machine in
net/will introduce more bugs than it fixes - especially if we overestimate our ability to upstream complex custom logic under LLM review supervision.Removing regulator provider will simplify a lot the implementation so I think it will rather remove bugs than introduce new one.
I think we should let Kyle works on it with a RFC and we will see where it goes.
Quoted 4 lines
For what it's worth, and for others that might be interested but I missed on cc, the RFC is here:
Thank's for that.
I will take a look at it when I have time, but it seems promising to me to solve lots of issue as you mention.
Regards,
Signature · 3 lines
Köry Maincent, Bootlin Embedded Linux and kernel engineering https://bootlin.com
Re: [PATCH net 05/10] net: pse-pd: check the PI state under the lock when setting a power limit
In reply to [email protected]Quoted 4 lines · On Mon, 19 Oct 2026 08:51:13 +0000, [email protected] wrote:
[Severity: Medium]
The scenario in the commit message, where budget is reserved for a port that is no longer powered, looks closed now. But can the budget and the hardware limit still drift apart in the other direction?
Yes, but that was already possible before this patch, which does not make it worse. A real fix needs the budget update and the hardware limit write under one lock, and that needs a different structure. As Kory wrote, PSE PIs may stop being regulator providers, which changes this code, so I leave it for now.
Quoted 3 lines
[Severity: Low]
This isn't a bug, but the kernel-doc forpse_pi_update_pw_budget()still says:
Right. The next version says that 0 can also mean that nothing changed, and that callers check *updated before they roll back.
Carlo
PATCH 6/10 net: pse-pd: do not tie a shared power domain to its creator's devres
In reply to Carlo SzelinskyA power domain is shared by all controllers on the same supply and counted with a kref, but it is devm memory of the controller that created it. When that controller unbinds while another one still uses the domain, devres frees it anyway, and the other controller and the global map keep pointing at freed memory. The same happens when pse_register_pw_ds() fails part way.
Allocate the domain with kzalloc() and free it on its last kref_put(). A domain left over from a failed pse_register_pw_ds() is now leaked instead of freed while still in the map.
Because the domain can now be freed at the put, pse_flush_pw_ds() clears pi[].pw_d under pcdev->lock before the put, and pse_ethtool_set_prio() checks it under that lock. The PI regulator ops and pse_isr() use it under the same lock.
--- drivers/net/pse-pd/pse_core.c | 43 ++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 13 deletions(-)
drivers/net/pse-pd/pse_core.c +30 -13
@@ -886,6 +886,7 @@ static void __pse_pw_d_release(struct kref *kref)regulator_put (pw_d->supply);xa_erase (&pse_pw_d_map, pw_d->id);mutex_unlock (&pse_pw_d_mutex); +kfree (pw_d); }/**
@@ -898,10 +899,18 @@ static void pse_flush_pw_ds(struct pse_controller_dev *pcdev)int i;for (i =0 ; i < pcdev->nr_lines; i++) { -if (!pcdev->pi[i].pw_d) +/* Clear it before the put that may free the domain. The PI +* regulator ops and pse_isr() hold pcdev->lock while they +* use it. +*/ +mutex_lock (&pcdev->lock); + pw_d = pcdev->pi[i].pw_d; + pcdev->pi[i].pw_d =NULL ; +mutex_unlock (&pcdev->lock); +if (!pw_d)continue ; - pw_d =xa_load (&pse_pw_d_map, pcdev->pi[i].pw_d->id); + pw_d =xa_load (&pse_pw_d_map, pw_d->id);if (!pw_d)continue ;
@@ -911,24 +920,30 @@ static void pse_flush_pw_ds(struct pse_controller_dev *pcdev) }/** -* devm_pse_alloc_pw_d - allocate a new PSE power domain for a device -* @dev: device that is registering this PSE power domain +* pse_alloc_pw_d - allocate a new PSE power domain +* +* A power domain is shared by every controller whose PIs sit on the same +* supply, so it must not be tied to the lifetime of the controller that +* happened to create it. It is freed by __pse_pw_d_release() on the last +* kref_put(). * * Return: Pointer to the newly allocated PSE power domain or error pointers */ -static struct pse_power_domain *devm_pse_alloc_pw_d (struct device *dev) +static struct pse_power_domain *pse_alloc_pw_d (void ) {struct pse_power_domain *pw_d;int index, ret; - pw_d =devm_kzalloc (dev,sizeof (*pw_d),GFP_KERNEL ); + pw_d =kzalloc_obj (*pw_d);if (!pw_d)return ERR_PTR (-ENOMEM ); ret =xa_alloc (&pse_pw_d_map, &index, pw_d,XA_LIMIT (1 ,PSE_PW_D_LIMIT ),GFP_KERNEL ); -if (ret) +if (ret) { +kfree (pw_d);return ERR_PTR (ret); + }kref_init (&pw_d->refcnt); pw_d->id = index;
@@ -974,7 +989,7 @@ static int pse_register_pw_ds(struct pse_controller_dev *pcdev)continue ; } - pw_d =devm_pse_alloc_pw_d (pcdev->dev); + pw_d =pse_alloc_pw_d ();if (IS_ERR (pw_d)) { ret =PTR_ERR (pw_d); goto out;
@@ -983,6 +998,7 @@ static int pse_register_pw_ds(struct pse_controller_dev *pcdev) supply =regulator_get (&rdev->dev, rdev->supply_name);if (IS_ERR (supply)) {xa_erase (&pse_pw_d_map, pw_d->id); +kfree (pw_d); ret =PTR_ERR (supply); goto out; }
@@ -1954,15 +1970,16 @@ int pse_ethtool_set_prio(struct pse_control *psec,const struct pse_controller_ops *ops;int ret =0 ; -if (!pcdev->pi[psec->id].pw_d) { -NL_SET_ERR_MSG (extack,"no power domain attached" ); -return -EOPNOTSUPP ; - } -/* We don't want priority change in the middle of an * enable/disable call or a priority mode change */ mutex_lock (&pcdev->lock); +if (!pcdev->pi[psec->id].pw_d) { +NL_SET_ERR_MSG (extack,"no power domain attached" ); + ret = -EOPNOTSUPP ; + goto out; + } +switch (pcdev->pi[psec->id].pw_d->budget_eval_strategy) {case PSE_BUDGET_EVAL_STRAT_STATIC :if (prio >= pcdev->nr_lines) {
Signature · 1 line
2.43.0
[email protected]
AI reviewRe: [PATCH net 06/10] net: pse-pd: do not tie a shared power domain to its creator's devres
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider.
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider.
Critical: 0 · High: 1 · Medium: 2 · Low: 0
If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough).
- [High] The shared power domain still depends on its creator's devres through its supply handle.
- [Medium] A partial failure of pse_register_pw_ds() is never unwound, so the left-over domains stay live and keep being reused.
- [Medium] pse_flush_pw_ds() now clears pcdev->pi[i].pw_d to NULL while the PI regulators are still registered.
--
net: pse-pd: do not tie a shared power domain to its creator's devres
This patch allocates PSE power domains with kzalloc() instead of devm memory and frees them on the last kref_put(). pse_flush_pw_ds() now clears pi[].pw_d under pcdev->lock before the put, and pse_ethtool_set_prio() checks pw_d under that lock.
Quoted 4 lines
drivers/net/pse-pd/pse_core.c +0
-0
[ ... ]
drivers/net/pse-pd/pse_core.c static void pse_flush_pw_ds(struct pse_controller_dev *pcdev) · line 909
Quoted 13 lines
drivers/net/pse-pd/pse_core.c +8
-1@@ -898,10 +899,18 @@ static void pse_flush_pw_ds(struct pse_controller_dev *pcdev)int i;for (i =0 ; i < pcdev->nr_lines; i++) { -if (!pcdev->pi[i].pw_d) +/* Clear it before the put that may free the domain. The PI +* regulator ops and pse_isr() hold pcdev->lock while they +* use it. +*/ +mutex_lock (&pcdev->lock); + pw_d = pcdev->pi[i].pw_d; + pcdev->pi[i].pw_d =NULL ; +mutex_unlock (&pcdev->lock);
[Severity: Medium]
Can the PI regulator ops still run here and see pw_d == NULL?
The PI regulators are devm registered before the pcdevp devres entry. That means they stay registered until after pse_controller_unregister() returns:
pse_controller_unregister()
pse_flush_pw_ds(pcdev);
pse_release_pis(pcdev);During that window a consumer, such as a PHY's pse_control driven through ethtool, can call pse_pi_enable(). It would then skip the software power control branch:
if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d)) {
...
}
ret = ops->pi_enable(pcdev, id);So the port is powered without the PD detection gate and without a budget charge.
The disable side has a similar gap. _pse_pi_disable() calls pse_pi_deallocate_pw_budget(), which returns early:
if (!pi->pw_d || !pi->pw_allocated_mW) return;
As a result, regulator_free_power_budget() is never called for a supply that another controller may still share.
Before this patch, pw_d stayed valid during this window. The commit message says "The PI regulator ops and pse_isr() use it under the same lock". Does that cover only memory safety, and not this new no-domain state?
Separately, there is a pre-existing issue that this patch did not
introduce: pse_release_pis() frees pcdev->pi right after the flush while the regulators are still live. The pse_isr() part does not seem to apply to in-tree drivers. For tps23881, devm_pse_irq_helper() is registered after the controller, so devres frees the IRQ first.
Quoted 2 lines
drivers/net/pse-pd/pse_core.c +1
-0+if (!pw_d)continue ;
[ ... ]
drivers/net/pse-pd/pse_core.c static int pse_register_pw_ds(struct pse_controller_dev *pcdev) · line 998
Quoted 11 lines
drivers/net/pse-pd/pse_core.c +1
-1@@ -974,7 +989,7 @@ static int pse_register_pw_ds(struct pse_controller_dev *pcdev)continue ; } - pw_d =devm_pse_alloc_pw_d (pcdev->dev); + pw_d =pse_alloc_pw_d ();if (IS_ERR (pw_d)) { ret =PTR_ERR (pw_d); goto out;@@ -983,6 +998,7 @@ static int pse_register_pw_ds(struct pse_controller_dev *pcdev) supply =regulator_get (&rdev->dev, rdev->supply_name);
[Severity: High]
Is the shared domain still tied to its creator through this supply handle?
The consumer device here is &rdev->dev, where rdev is a PI regulator of the creating controller. devm_pse_pi_regulator_register() registers that regulator with devm_regulator_register() on the creator's device.
In the regulator core, _regulator_get_common()->link_and_create_debugfs() stores the pointer without taking a reference:
regulator->dev = dev;
The only reference comes from the DL_FLAG_STATELESS device link, and device_del() of the PI rdev purges that link.
With this patch, controller B can keep the domain alive after creator A unbinds. devres then unregisters and frees A's PI regulators:
regulator_unregister()
device_unregister(&rdev->dev)
regulator_dev_release()pw_d->supply stays on the supply's consumer_list with ->dev pointing at freed memory. Reading /sys/kernel/debug/regulator/regulator_summary then reaches regulator_summary_show_subtree():
if (consumer->dev && consumer->dev->class == ®ulator_class)
Would that be a use-after-free read of the freed struct device?
When B finally puts the domain, destroy_regulator() passes the stale pointer to device_link_remove(regulator->dev, &rdev->dev). The address may have been reused by another consumer of the same supply. Could that drop that consumer's device link?
A domain left over from a failed pse_register_pw_ds() has the same stale ->dev, and it stays in pse_pw_d_map where later controllers can pick it up.
The new pse_alloc_pw_d() comment says the domain "must not be tied to the lifetime of the controller that happened to create it". Should the supply handle be acquired with a consumer device that the creator does not own? The rest of the series leaves this code unchanged.
drivers/net/pse-pd/pse_core.c static int pse_register_pw_ds(struct pse_controller_dev *pcdev) · line 1004
Quoted 6 lines
drivers/net/pse-pd/pse_core.c +1
-0if (IS_ERR (supply)) {xa_erase (&pse_pw_d_map, pw_d->id); +kfree (pw_d); ret =PTR_ERR (supply); goto out; }
[Severity: Medium]
What happens to the PIs handled before the failing one?
When pse_alloc_pw_d() or regulator_get() fails for PI i, only the current iteration is cleaned up. Each of PIs 0..i-1 did one of two things:
- took kref_get() on an existing domain, possibly another controller's
- created a new domain in pse_pw_d_map holding a supply handle
pse_controller_register() then returns without a flush:
ret = pse_register_pw_ds(pcdev); if (ret) return ret;
devm_pse_controller_register() only calls devres_free(pcdevp), so pse_controller_unregister()->pse_flush_pw_ds() never runs.
Does this leak the extra references on other controllers' domains, so they can never be released?
The new domains stay in the global map. Their supply consumer's ->dev is the failed controller's PI rdev, and devres unregisters that rdev as probe unwinds. A later pse_register_pw_ds() can find such a domain through regulator_is_equal() and kref_get() it, so its refcount never returns to zero.
The commit message says:
A domain left over from a failed pse_register_pw_ds() is now leaked instead of freed while still in the map.
The leaked domain is still published in the map and still carries a dangling consumer device. Would calling pse_flush_pw_ds(pcdev) on the pse_register_pw_ds() error path in pse_controller_register() be enough? pcdev->lock is already initialized there, and the flush already skips PIs whose pw_d is NULL.
[ ... ]
Signature · 1 line
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261018082314.48950-1-github%40szelinsky.de
Re: [PATCH net 06/10] net: pse-pd: do not tie a shared power domain to its creator's devres
Quoted 2 lines · On Mon, 19 Oct 2026 08:51:14 +0000, [email protected] wrote:
[Severity: High]
Is the shared domain still tied to its creator through this supply handle?
Yes. That is an older bug, and the cover letter lists it as not fixed here. It needs a supply handle whose consumer device the creator does not own. I will look at that as a follow-up.
Quoted 2 lines
[Severity: Medium]
What happens to the PIs handled before the failing one?
Right, this patch alone does not undo them. "net: pse-pd: unwind allocations when controller registration fails" does: it undoes them inside pse_register_pw_ds(), while it holds pse_pw_d_mutex. That patch moves to this series in the next version.
Quoted 2 lines
[Severity: Medium]
Can the PI regulator ops still run here and seepw_d== NULL?
Yes, in the short window before the PI array is freed. That is the same older bug as the freed PI array. I will rework the second patch of [1] and fold it into this series: it stops the PI regulator ops at the start of unregister, before this flush.
[1] https://lore.kernel.org/netdev/[email protected]/
Carlo
PATCH 7/10 net: pse-pd: pd692x0: release the manager power budget through devres
In reply to Carlo Szelinskypd692x0 reserves a power budget on each manager's supply in setup_pi_matrix(), but gives it back only on that function's own errors and in remove(). If probe fails later, or the request fails for one manager after others succeeded, the budget stays reserved on vmain. Each probe retry, e.g. after -EPROBE_DEFER, reserves it again until nothing is left.
Release the budget from a devm action instead, and drop the explicit release calls.
--- drivers/net/pse-pd/pd692x0.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-)
drivers/net/pse-pd/pd692x0.c +15 -7
@@ -1224,8 +1224,9 @@ static void pd692x0_of_put_managers(struct pd692x0_priv *priv, } } -static void pd692x0_managers_free_pw_budget (struct pd692x0_priv *priv) +static void pd692x0_managers_free_pw_budget (void *data) { +struct pd692x0_priv *priv = data;int i;for (i =0 ; i <PD692X0_MAX_MANAGERS ; i++) {
@@ -1279,31 +1280,39 @@ static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev)if (ret) goto err_of_managers; +/* Frees whatever pd692x0_req_managers_pw_budget() manages to +* request, on any later probe failure and on unbind. Added after +* the manager regulators so that it runs before devres unregisters +* them and their supply goes away. +*/ + ret =devm_add_action_or_reset (&priv->client->dev, + pd692x0_managers_free_pw_budget, priv); +if (ret) + goto err_of_managers; + ret =pd692x0_req_managers_pw_budget (priv);if (ret) goto err_of_managers; ret =pd692x0_set_ports_matrix (priv, manager);if (ret) - goto err_managers_req_pw; + goto err_of_managers;/* Do not init the conf if it is already saved */ if (!priv->cfg_saved) { ret =pd692x0_hw_conf_init (priv);if (ret) - goto err_managers_req_pw; + goto err_of_managers; ret =pd692x0_save_user_byte (priv);if (ret) - goto err_managers_req_pw; + goto err_of_managers; }pd692x0_of_put_managers (priv, manager);kfree (manager);return 0 ; -err_managers_req_pw: -pd692x0_managers_free_pw_budget (priv); err_of_managers:pd692x0_of_put_managers (priv, manager); err_free_manager:
@@ -1855,7 +1864,6 @@ static void pd692x0_i2c_remove(struct i2c_client *client) {struct pd692x0_priv *priv =i2c_get_clientdata (client); -pd692x0_managers_free_pw_budget (priv);firmware_upload_unregister (priv->fwl); }
Signature · 1 line
2.43.0
Re: [PATCH net 07/10] net: pse-pd: pd692x0: release the manager power budget through devres
Quoted 9 lines · Carlo Szelinsky
pd692x0 reserves a power budget on each manager's supply in
setup_pi_matrix(), but gives it back only on that function's own errors and inremove(). If probe fails later, or the request fails for one manager after others succeeded, the budget stays reserved on vmain. Each probe retry, e.g. after -EPROBE_DEFER, reserves it again until nothing is left.Release the budget from a devm action instead, and drop the explicit release calls.
Thank you!
Quoted 74 lines
Fixes 1c67f9c54cdc ("net: pse-pd: pd692x0: Fix power budget leak in manager setup error path")Signed-off-by Carlo Szelinsky <[email protected]>--- drivers/net/pse-pd/pd692x0.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-)drivers/net/pse-pd/pd692x0.c +15
-7@@ -1224,8 +1224,9 @@ static void pd692x0_of_put_managers(struct pd692x0_priv *priv, } } -static void pd692x0_managers_free_pw_budget (struct pd692x0_priv *priv) +static void pd692x0_managers_free_pw_budget (void *data) { +struct pd692x0_priv *priv = data;int i;for (i =0 ; i <PD692X0_MAX_MANAGERS ; i++) {@@ -1279,31 +1280,39 @@ static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev)if (ret) goto err_of_managers; +/* Frees whatever pd692x0_req_managers_pw_budget() manages to +* request, on any later probe failure and on unbind. Added after +* the manager regulators so that it runs before devres unregisters +* them and their supply goes away. +*/ + ret =devm_add_action_or_reset (&priv->client->dev, + pd692x0_managers_free_pw_budget, priv); +if (ret) + goto err_of_managers; + ret =pd692x0_req_managers_pw_budget (priv);if (ret) goto err_of_managers; ret =pd692x0_set_ports_matrix (priv, manager);if (ret) - goto err_managers_req_pw; + goto err_of_managers;/* Do not init the conf if it is already saved */ if (!priv->cfg_saved) { ret =pd692x0_hw_conf_init (priv);if (ret) - goto err_managers_req_pw; + goto err_of_managers; ret =pd692x0_save_user_byte (priv);if (ret) - goto err_managers_req_pw; + goto err_of_managers; }pd692x0_of_put_managers (priv, manager);kfree (manager);return 0 ; -err_managers_req_pw: -pd692x0_managers_free_pw_budget (priv); err_of_managers:pd692x0_of_put_managers (priv, manager); err_free_manager:@@ -1855,7 +1864,6 @@ static void pd692x0_i2c_remove(struct i2c_client *client) {struct pd692x0_priv *priv =i2c_get_clientdata (client); -pd692x0_managers_free_pw_budget (priv);firmware_upload_unregister (priv->fwl); }
Signature · 3 lines
Köry Maincent, Bootlin Embedded Linux and kernel engineering https://bootlin.com
PATCH 8/10 net: pse-pd: pd692x0: defer probe while a manager supply is unresolved
In reply to Carlo Szelinskypd692x0_req_managers_pw_budget() uses each manager regulator's supply without a NULL check. The supply is NULL while the vmain regulator is described but not registered yet, because regulator_register() does not wait for its supply. Probe then crashes.
Return -EPROBE_DEFER in that case. On net, each retry also leaks the notification kfifo and the PI array until vmain appears; the fix for that is in the PSE core fixes for net-next.
--- drivers/net/pse-pd/pd692x0.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/net/pse-pd/pd692x0.c +9 -0
@@ -1056,6 +1056,15 @@ pd692x0_req_managers_pw_budget(struct pd692x0_priv *priv)struct regulator *supply = priv->manager_reg[i]->supply;int pw_budget; +/* The regulator core registers the manager regulator even +* when its vmain supply cannot be resolved yet and retries +* later, leaving ->supply NULL until then. +*/ +if (!supply) +return dev_err_probe (&priv->client->dev, -EPROBE_DEFER , +"manager %d: vmain supply not resolved\n" , + i); + pw_budget =regulator_get_unclaimed_power_budget (supply);if (!pw_budget)/* Do nothing if no power budget */
Signature · 1 line
2.43.0
Re: [PATCH net 08/10] net: pse-pd: pd692x0: defer probe while a manager supply is unresolved
Quoted 8 lines · Carlo Szelinsky
pd692x0_req_managers_pw_budget()uses each manager regulator's supply without a NULL check. The supply is NULL while the vmain regulator is described but not registered yet, becauseregulator_register()does not wait for its supply. Probe then crashes.Return -EPROBE_DEFER in that case. On net, each retry also leaks the notification kfifo and the PI array until vmain appears; the fix for that is in the PSE core fixes for net-next.
Thank you!
Quoted 26 lines
Fixes 359754013e6a ("net: pse-pd: pd692x0: Add support for PSE PI priority feature")Signed-off-by Carlo Szelinsky <[email protected]>--- drivers/net/pse-pd/pd692x0.c | 9 +++++++++ 1 file changed, 9 insertions(+)drivers/net/pse-pd/pd692x0.c +9
-0@@ -1056,6 +1056,15 @@ pd692x0_req_managers_pw_budget(struct pd692x0_priv *priv)struct regulator *supply = priv->manager_reg[i]->supply;int pw_budget; +/* The regulator core registers the manager regulator even +* when its vmain supply cannot be resolved yet and retries +* later, leaving ->supply NULL until then. +*/ +if (!supply) +return dev_err_probe (&priv->client->dev, -EPROBE_DEFER , +"manager %d: vmain supply not resolved\n" , + i); + pw_budget =regulator_get_unclaimed_power_budget (supply);if (!pw_budget)/* Do nothing if no power budget */
Signature · 3 lines
Köry Maincent, Bootlin Embedded Linux and kernel engineering https://bootlin.com
PATCH 9/10 net: pse-pd: si3474: fix use-after-free in ancillary client error path
In reply to Carlo SzelinskyIf devm_add_action_or_reset() fails, it has already unregistered the ancillary I2C client, and the error message then reads that client's device. It can only fail with -ENOMEM, which is already reported, so drop the message.
--- drivers/net/pse-pd/si3474.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/net/pse-pd/si3474.c +1 -3
@@ -516,10 +516,8 @@ static int si3474_i2c_probe(struct i2c_client *client)return PTR_ERR (priv->client[1 ]); ret =devm_add_action_or_reset (dev, si3474_ancillary_i2c_remove, priv->client[1 ]); -if (ret <0 ) { -dev_err (&priv->client[1 ]->dev,"Cannot register remove callback\n" ); +if (ret)return ret; - } ret =i2c_smbus_read_byte_data (priv->client[1 ],VENDOR_IC_ID_REG );if (ret <0 ) {
Signature · 1 line
2.43.0
Re: [PATCH net 09/10] net: pse-pd: si3474: fix use-after-free in ancillary client error path
Quoted 4 lines · Carlo Szelinsky
If
devm_add_action_or_reset()fails, it has already unregistered the ancillary I2C client, and the error message then reads that client's device. It can only fail with-ENOMEM, which is already reported, so drop the message.
That's not really a fix but well:
Thank you!
Quoted 22 lines
Fixes a2317231df4b ("net: pse-pd: Add Si3474 PSE controller driver")Signed-off-by Carlo Szelinsky <[email protected]>--- drivers/net/pse-pd/si3474.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)drivers/net/pse-pd/si3474.c +1
-3@@ -516,10 +516,8 @@ static int si3474_i2c_probe(struct i2c_client *client)return PTR_ERR (priv->client[1 ]); ret =devm_add_action_or_reset (dev, si3474_ancillary_i2c_remove, priv->client[1 ]); -if (ret <0 ) { -dev_err (&priv->client[1 ]->dev,"Cannot register remove callback\n" ); +if (ret)return ret; - } ret =i2c_smbus_read_byte_data (priv->client[1 ],VENDOR_IC_ID_REG );if (ret <0 ) {
Signature · 3 lines
Köry Maincent, Bootlin Embedded Linux and kernel engineering https://bootlin.com
Re: [PATCH net 09/10] net: pse-pd: si3474: fix use-after-free in ancillary client error path
Quoted 2 lines · Kory Maincent
That's not really a fix but well:
Reviewed-by Kory Maincent <[email protected]>
Thanks, Kory. You are right, so I will drop it from the net series and send it to net-next with your Reviewed-by, together with 10/10.
Carlo
PATCH 10/10 net: pse-pd: reject a power limit whose current does not fit an int
In reply to Carlo Szelinskypse_ethtool_set_pw_limit() turns the power limit into a current as mW * 10^9 / uV and stores it in an int. For the largest allowed limit, 99900 mW, that overflows below about 46.5 mV. Only 0 V is rejected, and si3474 reports the measured voltage of each port. A negative result then hits the BUG_ON() in regulator_check_current_limit().
pse_pi_get_current_limit() does the same conversion and can return a wrapped value that looks like an error code.
Do the conversion in one helper that returns -ERANGE when the result does not fit an int.
--- drivers/net/pse-pd/pse_core.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-)
drivers/net/pse-pd/pse_core.c +15 -10
@@ -741,12 +741,19 @@ static int pse_pi_get_voltage(struct regulator_dev *rdev)return ret; } +/* uA = mW * 1000000000 / uV, which does not fit an int for a low uV */ +static int pse_mw_to_ua (unsigned int mW,int uV) +{ +u64 uA =DIV_ROUND_CLOSEST_ULL ((u64 )mW *1000000000ull , uV); + +return uA >INT_MAX ? -ERANGE : uA; +} +static int pse_pi_get_current_limit (struct regulator_dev *rdev) {struct pse_controller_dev *pcdev =rdev_get_drvdata (rdev);const struct pse_controller_ops *ops;int id, uV, mW, ret; -s64 tmp_64; ops = pcdev->ops; id =rdev_get_id (rdev);
@@ -769,10 +776,7 @@ static int pse_pi_get_current_limit(struct regulator_dev *rdev) goto out; uV = ret; - tmp_64 = mW; - tmp_64 *=1000000000ull ; -/* uA = mW * 1000000000 / uV */ - ret =DIV_ROUND_CLOSEST_ULL (tmp_64, uV); + ret =pse_mw_to_ua (mW, uV); out:mutex_unlock (&pcdev->lock);
@@ -1910,7 +1914,6 @@ int pse_ethtool_set_pw_limit(struct pse_control *psec, {int uV, uA, ret, previous_pw_allocated =0 ;bool updated; -s64 tmp_64;if (pw_limit >MAX_PI_PW )return -ERANGE ;
@@ -1928,10 +1931,12 @@ int pse_ethtool_set_pw_limit(struct pse_control *psec, } uV = ret; - tmp_64 = pw_limit; - tmp_64 *=1000000000ull ; -/* uA = mW * 1000000000 / uV */ - uA =DIV_ROUND_CLOSEST_ULL (tmp_64, uV); + uA =pse_mw_to_ua (pw_limit, uV); +if (uA <0 ) { +NL_SET_ERR_MSG (extack, +"PSE voltage read is too low to set the limit" ); +return uA; + }/* Update power budget only in software power control case and * if a Power Device is powered.
Signature · 1 line
2.43.0
Kory Maincent
maintainerRe: [PATCH net 10/10] net: pse-pd: reject a power limit whose current does not fit an int
Quoted 11 lines · Carlo Szelinsky
pse_ethtool_set_pw_limit()turns the power limit into a current as mW * 10^9 / uV and stores it in an int. For the largest allowed limit, 99900 mW, that overflows below about 46.5 mV. Only 0 V is rejected, and si3474 reports the measured voltage of each port. A negative result then hits theBUG_ON()inregulator_check_current_limit().
pse_pi_get_current_limit()does the same conversion and can return a wrapped value that looks like an error code.Do the conversion in one helper that returns
-ERANGEwhen the result does not fit an int.
Same as patch 9. This is not a fix.
Can you send both to net-next instead.
You can add my R-by tag to it.
Quoted 68 lines
Fixes 4a83abcef5f4 ("net: pse-pd: Add new power limit get and set c33 features")Signed-off-by Carlo Szelinsky <[email protected]>--- drivers/net/pse-pd/pse_core.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-)drivers/net/pse-pd/pse_core.c +15
-10@@ -741,12 +741,19 @@ static int pse_pi_get_voltage(struct regulator_dev *rdev)return ret; } +/* uA = mW * 1000000000 / uV, which does not fit an int for a low uV */ +static int pse_mw_to_ua (unsigned int mW,int uV) +{ +u64 uA =DIV_ROUND_CLOSEST_ULL ((u64 )mW *1000000000ull , uV); + +return uA >INT_MAX ? -ERANGE : uA; +} +static int pse_pi_get_current_limit (struct regulator_dev *rdev) {struct pse_controller_dev *pcdev =rdev_get_drvdata (rdev);const struct pse_controller_ops *ops;int id, uV, mW, ret; -s64 tmp_64; ops = pcdev->ops; id =rdev_get_id (rdev);@@ -769,10 +776,7 @@ static int pse_pi_get_current_limit(struct regulator_dev *rdev) goto out; uV = ret; - tmp_64 = mW; - tmp_64 *=1000000000ull ; -/* uA = mW * 1000000000 / uV */ - ret =DIV_ROUND_CLOSEST_ULL (tmp_64, uV); + ret =pse_mw_to_ua (mW, uV); out:mutex_unlock (&pcdev->lock);@@ -1910,7 +1914,6 @@ int pse_ethtool_set_pw_limit(struct pse_control *psec, {int uV, uA, ret, previous_pw_allocated =0 ;bool updated; -s64 tmp_64;if (pw_limit >MAX_PI_PW )return -ERANGE ;@@ -1928,10 +1931,12 @@ int pse_ethtool_set_pw_limit(struct pse_control *psec, } uV = ret; - tmp_64 = pw_limit; - tmp_64 *=1000000000ull ; -/* uA = mW * 1000000000 / uV */ - uA =DIV_ROUND_CLOSEST_ULL (tmp_64, uV); + uA =pse_mw_to_ua (pw_limit, uV); +if (uA <0 ) { +NL_SET_ERR_MSG (extack, +"PSE voltage read is too low to set the limit" ); +return uA; + }/* Update power budget only in software power control case and * if a Power Device is powered.
Signature · 3 lines
Köry Maincent, Bootlin Embedded Linux and kernel engineering https://bootlin.com
Re: [PATCH net 10/10] net: pse-pd: reject a power limit whose current does not fit an int
Quoted 3 lines · Kory Maincent
Same as patch 9. This is not a fix.
Can you send both to net-next instead.
You can add my R-by tag to it.
Thanks, Kory. Will do: 9/10 and 10/10 go to net-next, with your Reviewed-by.
Carlo
Hi Carlo,
I sent a fix to pse_core.c for net today as well, for the budget of PIs with software power control:
https://lore.kernel.org/netdev/[email protected]/
It changes _pse_pi_disable(), pse_disable_pi_pol(), pse_pi_disable() and pse_set_config_isr(), none of which your series touches, so I don't expect the two to get in each other's way. If they do, I'm happy to rebase mine on top of yours.
Separately: I have a driver for the Realtek RTL8239, two of which a MokerLink POE-10G07110GSM switch drives directly with no PSE MCU, and it uses devm_pse_poll_helper() and the PSE LED triggers from your "poll path and LED trigger support" series. Is a v6 of that planned once these fixes are in? I can test it on that switch, and would post the RTL8239 driver on top of it.
Ross
Quoted 3 lines · Ross Cawston
It changes
_pse_pi_disable(),pse_disable_pi_pol(),pse_pi_disable()andpse_set_config_isr(), none of which your series touches, so I don't expect the two to get in each other's way.
Hi Ross,
Right for this version: I checked, and your patch applies on top of it. The next version will also change pse_pi_disable(), because it takes in my older fix for the PI regulator ops during unregister. If the two then overlap, I will rebase on yours, whichever is merged first.
Quoted 1 line
Is a v6 of that planned once these fixes are in?
Yes. I will finish the poll and LED pieces as soon as these fixes are done. Thanks for offering to test on the RTL8239 switch, I will Cc you.
Carlo