[PATCH net 05/10] net: pse-pd: check the PI state under the lock when setting a power limit

From: Carlo Szelinsky

Date: Sun Oct 18 2026 - 04:27:59 EST


pse_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.

Fixes: ffef61d6d273 ("net: pse-pd: Add support for budget evaluation strategies")
Signed-off-by: Carlo Szelinsky <github@xxxxxxxxxxxx>
---
drivers/net/pse-pd/pse_core.c | 34 +++++++++++++++++++++-------------
1 file changed, 21 insertions(+), 13 deletions(-)

diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index 336f00d82b5f..d5f863834179 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -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;
--
2.43.0