mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: netdev-bot+sashiko@kernel.org
To: github@szelinsky.de
Cc: o.rempel@pengutronix.de, kory.maincent@bootlin.com,
	andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@kernel.org,
	kuba@kernel.org, pabeni@redhat.com, horms@kernel.org,
	kyle.swenson@est.tech, ross@r-sc.ca,
	netdev-bot+sashiko@kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH net v2 06/14] net: pse-pd: check the PI state under the lock when setting a power limit
Date: Fri, 23 Oct 2026 08:13:34 +0000	[thread overview]
Message-ID: <179274321449.3134.2737207729630977183@kernel.org> (raw)
In-Reply-To: <20261020171249.853085-7-github@szelinsky.de>

Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider.

Critical: 0 · High: 1 (1) · Medium: 1 (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).

- [High] The new check in pse_pi_update_pw_budget() (drivers/net/pse-
  pd/pse_core.c:1849-1851) only tests `pse_pw_d_is_sw_pw_control() &&…
- [Medium] pse_ethtool_set_pw_limit() still races pse_isr() after the
  change.
- [Low] The new eligibility check in pse_pi_update_pw_budget() also
  applies to the rollback call, so the rollback can be skipped while the…

Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but there is an
  AB-BA deadlock between pcdev->lock and the supply regulator's ww_mutex.
- [Medium] This problem wasn't introduced by this patch, but the rollback
  call to pse_pi_update_pw_budget() in pse_ethtool_set_pw_limit() (lines…

--

net: pse-pd: check the PI state under the lock when setting a power limit

This patch moves the "PI is powered" check from pse_ethtool_set_pw_limit()
into pse_pi_update_pw_budget(), where it runs under pcdev->lock. The helper
now reports through a new updated argument whether the budget changed. The
rollback after a failed regulator_set_current_limit() uses that flag
instead of a non-zero previous allocation.

> diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
> index af9eb29792706..36f7dba8190a2 100644
> --- a/drivers/net/pse-pd/pse_core.c
> +++ b/drivers/net/pse-pd/pse_core.c
> @@ -1821,22 +1821,35 @@ EXPORT_SYMBOL_GPL(pse_ethtool_set_config);

[ ... ]

>  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 PI under software power control that is enabled and has a
> +	 * PD detected gets a budget. Check this 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: High]
Do admin_state_enabled and isr_pd_detected really mean the PI is powered?

Both flags can stay set while the port is off in hardware.
pse_set_config_isr() hides -ERANGE from the static budget allocation:

	pcdev->pi[id].isr_pd_detected = true;
	if (pcdev->pi[id].admin_state_enabled) {
		ret = _pse_pi_delivery_power_sw_pw_ctrl(pcdev, id,
							&extack);
		if (ret == -ERANGE)
			ret = 0;
	}

pse_pi_enable() also sets admin_state_enabled on -ERANGE. In both cases
the PI is left pending, with pw_allocated_mW == 0 and pi_enable() never
called.

pse_disable_pi_pol() is another case. It turns off a powered PI through
_pse_pi_disable() to shed budget, but it leaves both flags set.

The core already treats this as a separate state in
pse_pi_is_admin_enable_pending():

	if (!pcdev->pi[id].admin_state_enabled ||
	    !pcdev->pi[id].isr_pd_detected)
		return false;

	ret = pse_pi_is_hw_enabled(pcdev, id);
	/* PSE PI is already enabled at hardware level */
	if (ret == 1)
		return false;

A pending PI passes this check. regulator_request_power_budget() then
reserves pw_limit, and pi->pw_allocated_mW is set to pw_limit, while no
power is delivered.

pse_pw_d_retry_power_delivery() can power the port later. It is called
from _pse_pi_disable() of another PI and from pse_ethtool_set_prio(). The
path is _pse_pi_delivery_power_sw_pw_ctrl() ->
pse_pi_allocate_pw_budget_static_prio():

	while (regulator_request_power_budget(pi->pw_d->supply, pw_req) == -ERANGE) {
	...
	pi->pw_allocated_mW = pw_req;

This requests the full pw_req again and overwrites pw_allocated_mW. The
earlier ethtool reservation is never freed. pse_pi_deallocate_pw_budget()
only frees the overwritten value.

Does the pw_limit reserved through ethtool then stay charged to the supply
for good?

The commit message says the patch stops budget being reserved "for a port
that is no longer powered". That still seems possible, for example right
after pse_disable_pi_pol() turns a port off. The same flag test existed
before the patch, outside the lock.

Would checking pse_pi_is_hw_enabled() == 1 here, under the lock, close
this?

The comment kept in pse_ethtool_set_pw_limit() ("if a Power Device is
powered") also doesn't match what this check tests.

[Severity: Low]
The rollback call in pse_ethtool_set_pw_limit() also goes through this
check. Can the rollback be skipped while the increased reservation is still
held?

pse_ethtool_set_pw_limit()
  pse_pi_update_pw_budget()      raises P -> L, updated = true, unlock

pse_isr()                        DISCONNECTION
  pse_set_config_isr()
    _pse_pi_disable()
      ops->pi_disable() fails (e.g. an I2C error), returns before
      pse_pi_deallocate_pw_budget()
    pcdev->pi[id].isr_pd_detected = false;

pse_ethtool_set_pw_limit()
  regulator_set_current_limit()  fails
  pse_pi_update_pw_budget(previous_pw_allocated)
    !pi->isr_pd_detected -> goto out

pw_allocated_mW then stays at L. The pre-patch rollback would have restored
it to P. Failing this check doesn't always mean the PI's reservation has
been released.

> +
>  	previous_pw_allocated = pi->pw_allocated_mW;
>  	pw_diff = pw_req - previous_pw_allocated;
>  	if (!pw_diff) {
> @@ -1854,6 +1867,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);
>  	}

[Severity: High]
This is a pre-existing issue and was not introduced by this patch. Is
there an AB-BA deadlock between pcdev->lock and the supply regulator's
lock?

Path A, ethtool set_config under rtnl:

pse_ethtool_c33_set_config()
  regulator_enable(psec->ps)
    regulator_lock_dependent()
      regulator_lock_recursive()    locks PI rdev, then its supply rdev
    _regulator_enable()->_regulator_do_enable()
      pse_pi_enable()
        mutex_lock(&pcdev->lock)

regulator_disable() -> pse_pi_disable() takes the locks in the same order.

Path B, the threaded IRQ:

pse_isr()
  mutex_lock(&pcdev->lock)
  pse_set_config_isr()
    _pse_pi_delivery_power_sw_pw_ctrl()
      pse_pi_allocate_pw_budget_static_prio()
        regulator_request_power_budget(pi->pw_d->supply)
          regulator_lock(supply rdev)

On DISCONNECTION, _pse_pi_disable() -> regulator_free_power_budget() takes
the same supply lock.

pw_d->supply comes from regulator_get(&rdev->dev, rdev->supply_name). It
is the same supply rdev that path A already holds.

If A holds the supply rdev and waits for pcdev->lock, while B holds
pcdev->lock and waits for the supply rdev, both tasks hang for good. A
also holds rtnl_lock.

pse_pi_update_pw_budget(), including its new rollback use, keeps the same
pcdev->lock -> supply order.

>  	pi->pw_allocated_mW = pw_req;
> +	*updated = true;
>  	ret = previous_pw_allocated;
>  
>  out:

[ ... ]

> @@ -1900,21 +1915,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);
>  	}

[Severity: Medium]
pcdev->lock is still dropped between three steps: the budget update,
regulator_set_current_limit() (which takes the lock again in
pse_pi_set_current_limit()), and the rollback.

Can updated and previous_pw_allocated be stale by the time the rollback
runs? The rollback also applies an absolute value to whatever PD session
exists at that point.

Two interleavings with pse_isr() look possible.

(a) After the budget update, pse_isr() handles DISCONNECTION, which frees
the budget, and then CLASSIFICATION. _pse_pi_delivery_power_sw_pw_ctrl()
budgets the new PD as min(pi_get_pw_req, pi_get_pw_limit), using the old
hardware limit.

If regulator_set_current_limit() then fails, the rollback forces the new
session's allocation back to the old session's value.

The patch adds a rollback-from-zero case (previous_pw_allocated == 0 and
updated == true, e.g. for a pending PI). In that case the rollback can
free the new PD's whole budget while the PD is powered, so the supply is
oversubscribed.

(b) A PD is classified between the budget update and the hardware limit
write. It is budgeted from the old limit, and then the new, higher limit
is programmed. The hardware then allows more power than is reserved.

These windows existed before the patch. The rollback driven by updated is
new.

The commit message describes the pse_isr() race. Would it make sense to do
the budget update and the hardware limit write in one pcdev->lock section,
so that race is fully closed?

[Severity: Medium]
This isn't a bug introduced by this patch, but the return value of the
rollback pse_pi_update_pw_budget() call is ignored. What happens if the
rollback's re-request fails?

When the limit is lowered from P to L, the first call frees P - L mW and
drops pcdev->lock. A concurrent pse_isr() on another PI in the same domain
can take that freed budget.

regulator_set_current_limit() can then fail.
pse_pi_set_current_limit() returns -ERANGE for max_uA > MAX_PI_CURRENT or a
null voltage, and ops->pi_set_pw_limit() can also fail. The rollback's
regulator_request_power_budget(P - L) then returns -ERANGE.

pw_allocated_mW stays at L, but the hardware keeps the old limit P.

The rollback also overwrites extack with "not enough power budget
available". That hides the real regulator_set_current_limit() error.

>  
>  	return ret;

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020171249.853085-1-github%40szelinsky.de

  parent reply	other threads:[~2026-10-23  8:13 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-20 17:12 [PATCH net v2 00/14] net: pse-pd: assorted lifetime and error-path fixes Carlo Szelinsky
2026-10-20 17:12 ` [PATCH net v2 01/14] net: pse-pd: size the notification kfifo after defaulting nr_lines Carlo Szelinsky
2026-10-21  5:17   ` Oleksij Rempel
2026-10-20 17:12 ` [PATCH net v2 02/14] net: pse-pd: remove dead pi_get_admin_state() checks Carlo Szelinsky
2026-10-21  5:19   ` Oleksij Rempel
2026-10-20 17:12 ` [PATCH net v2 03/14] net: pse-pd: fix module refcount leak in pse_control_get_internal() Carlo Szelinsky
2026-10-21  5:24   ` Oleksij Rempel
2026-10-20 17:12 ` [PATCH net v2 04/14] net: pse-pd: do not keep a failed IRQ in pcdev->irq Carlo Szelinsky
2026-10-21  5:26   ` Oleksij Rempel
2026-10-20 17:12 ` [PATCH net v2 05/14] net: pse-pd: do not reach a freed PHY from the notification worker Carlo Szelinsky
2026-10-21  9:17   ` Oleksij Rempel
2026-10-20 17:12 ` [PATCH net v2 06/14] net: pse-pd: check the PI state under the lock when setting a power limit Carlo Szelinsky
2026-10-22  4:46   ` Oleksij Rempel
2026-10-23  8:13   ` netdev-bot+sashiko [this message]
2026-10-20 17:12 ` [PATCH net v2 07/14] net: pse-pd: do not tie a shared power domain to its creator's devres Carlo Szelinsky
2026-10-22  5:13   ` Oleksij Rempel
2026-10-23  8:13   ` netdev-bot+sashiko
2026-10-20 17:12 ` [PATCH net v2 08/14] net: pse-pd: stop async event sources before freeing PI data in unregister Carlo Szelinsky
2026-10-22  5:20   ` Oleksij Rempel
2026-10-23  8:13   ` netdev-bot+sashiko
2026-10-20 17:12 ` [PATCH net v2 09/14] net: pse-pd: unregister from the controller list before freeing PI data Carlo Szelinsky
2026-10-22  5:28   ` Oleksij Rempel
2026-10-20 17:12 ` [PATCH net v2 10/14] net: pse-pd: keep the PI data until the PI regulators are gone Carlo Szelinsky
2026-10-22  7:34   ` Oleksij Rempel
2026-10-23  8:13   ` netdev-bot+sashiko
2026-10-20 17:12 ` [PATCH net v2 11/14] net: pse-pd: unwind allocations when controller registration fails Carlo Szelinsky
2026-10-22  7:47   ` Oleksij Rempel
2026-10-22  8:21   ` Oleksij Rempel
2026-10-20 17:12 ` [PATCH net v2 12/14] net: pse-pd: use each PI's own supply handle for the power budget Carlo Szelinsky
2026-10-22  8:36   ` Oleksij Rempel
2026-10-20 17:12 ` [PATCH net v2 13/14] net: pse-pd: pd692x0: release the manager power budget through devres Carlo Szelinsky
2026-10-20 17:12 ` [PATCH net v2 14/14] net: pse-pd: pd692x0: defer probe while a manager supply is unresolved Carlo Szelinsky

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=179274321449.3134.2737207729630977183@kernel.org \
    --to=netdev-bot+sashiko@kernel.org \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@kernel.org \
    --cc=github@szelinsky.de \
    --cc=horms@kernel.org \
    --cc=kory.maincent@bootlin.com \
    --cc=kuba@kernel.org \
    --cc=kyle.swenson@est.tech \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=o.rempel@pengutronix.de \
    --cc=pabeni@redhat.com \
    --cc=ross@r-sc.ca \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®