From: Kory Maincent <kory.maincent@bootlin.com>
To: Ross Cawston <ross@r-sc.ca>, netdev@vger.kernel.org
Cc: o.rempel@pengutronix.de, andrew+netdev@lunn.ch,
davem@davemloft.net, edumazet@kernel.org, kuba@kernel.org,
pabeni@redhat.com, linux-kernel@vger.kernel.org
Subject: Re: [PATCH net] net: pse-pd: don't enable a PI again while disabling it
Date: Mon, 19 Oct 2026 11:11:24 +0200 [thread overview]
Message-ID: <748d24c8-2576-4958-95e7-01a1d40d5891@bootlin.com> (raw)
In-Reply-To: <20261018182005.1193-1-ross@r-sc.ca>
On 10/18/26 20:20, Ross Cawston wrote:
> With software power control, _pse_pi_disable() frees the power budget
> of the PI and then retries the power delivery of the PIs waiting for
> power in its power domain: those that are admin enabled, have a PD
> detected, and are not enabled in hardware. The PI being disabled is one
> of them, as its admin state and PD detection are only cleared after
> _pse_pi_disable() returns, so it is enabled again right away:
>
> - an admin disable of a port with a PD keeps the port powered, and the
> next admin enable allocates its budget a second time
> - a disconnection enables the now empty port again, with a new budget
> allocation that is never freed: when the port reports the
> disconnection again, its PD detection is already cleared
> - a PI disabled to free budget for a PI of higher priority is enabled
> again with the budget it just freed, so priorities never take effect
>
> Clear the admin state and the PD detection before disabling the PI,
> and don't retry the power domain in pse_disable_pi_pol(), whose freed
> budget is meant for the PI being allocated. That PI stays waiting for
> power and is retried when budget is freed later.
>
> The problem was found, and this fix written, with an AI coding
> assistant, while testing the budget on a switch with two Realtek RTL8239
> PSE controllers, whose driver (not in mainline yet) uses the static
> budget strategy like the TPS23881. Before this change, "ethtool
> --set-pse <port> c33-pse-admin-control disable" left a port with a PD
> powered, and the budget allocated in the power domain grew with every
> disable and every unplug. With it, the port stays off, and the allocated
> budget is back to 0 after a disable and after an unplug. Tested on that
> switch with OpenWrt's 6.18 kernel; the change for mainline is the same
> apart from context and is build-tested.
>
> Fixes: ffef61d6d273 ("net: pse-pd: Add support for budget evaluation strategies")
> Signed-off-by: Ross Cawston <ross@r-sc.ca>
> ---
> drivers/net/pse-pd/pse_core.c | 50 ++++++++++++++++++++++++++++-------
> 1 file changed, 41 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
> index a5e6d7b26b9..b46e3a13a31 100644
> --- a/drivers/net/pse-pd/pse_core.c
> +++ b/drivers/net/pse-pd/pse_core.c
> @@ -448,14 +448,14 @@ static void pse_pi_deallocate_pw_budget(struct pse_pi *pi)
> }
>
> /**
> - * _pse_pi_disable - Call disable operation. Assumes the PSE lock has been
> - * acquired.
> + * __pse_pi_disable - Call disable operation and free the power budget of the
> + * PI. Assumes the PSE lock has been acquired.
> * @pcdev: a pointer to the PSE
> * @id: index of the PSE control
> *
> * Return: 0 on success and failure value on error
> */
> -static int _pse_pi_disable(struct pse_controller_dev *pcdev, int id)
> +static int __pse_pi_disable(struct pse_controller_dev *pcdev, int id)
> {
> const struct pse_controller_ops *ops = pcdev->ops;
> int ret;
> @@ -469,6 +469,29 @@ static int _pse_pi_disable(struct pse_controller_dev *pcdev, int id)
>
> pse_pi_deallocate_pw_budget(&pcdev->pi[id]);
>
> + return 0;
> +}
> +
> +/**
> + * _pse_pi_disable - Disable a PI, then retry the power delivery of the PIs
> + * waiting for power in its power domain. Assumes the PSE
> + * lock has been acquired.
> + * @pcdev: a pointer to the PSE
> + * @id: index of the PSE control
> + *
> + * The caller must have cleared the admin state or the PD detection of the
> + * PI first, or the retry enables the PI again.
> + *
> + * Return: 0 on success and failure value on error
> + */
> +static int _pse_pi_disable(struct pse_controller_dev *pcdev, int id)
> +{
> + int ret;
> +
> + ret = __pse_pi_disable(pcdev, id);
> + if (ret)
> + return ret;
> +
> if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d))
> pse_pw_d_retry_power_delivery(pcdev, pcdev->pi[id].pw_d);
Thanks for your fix.
I think, I would prefer to have these two lines moved out of
_pse_pi_disable() calls than to have another disable function.
Indeed this is not strictly related to the disable call.
This will also simplify your change below.
> @@ -490,7 +513,10 @@ static int pse_disable_pi_pol(struct pse_controller_dev *pcdev, int id)
>
> dev_dbg(pcdev->dev, "Disabling PI %d to free power budget\n", id);
>
> - ret = _pse_pi_disable(pcdev, id);
> + /* The power freed is for the PI being allocated, and this PI waits
> + * for power again: no retry of the power domain here.
> + */
> + ret = __pse_pi_disable(pcdev, id);
> if (ret)
> notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR;
>
> @@ -703,14 +729,18 @@ static int pse_pi_disable(struct regulator_dev *rdev)
> {
> struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
> struct pse_pi *pi;
> + bool enabled;
> int id, ret;
>
> id = rdev_get_id(rdev);
> pi = &pcdev->pi[id];
> mutex_lock(&pcdev->lock);
> + /* Disabled before _pse_pi_disable() retries the power domain */
> + enabled = pi->admin_state_enabled;
> + pi->admin_state_enabled = 0;
> ret = _pse_pi_disable(pcdev, id);
> - if (!ret)
> - pi->admin_state_enabled = 0;
> + if (ret)
> + pi->admin_state_enabled = enabled;
>
> mutex_unlock(&pcdev->lock);
> return 0;
> @@ -1229,10 +1259,12 @@ static int pse_set_config_isr(struct pse_controller_dev *pcdev, int id,
> ret = 0;
> }
> } else if (notifs & ETHTOOL_C33_PSE_EVENT_DISCONNECTION) {
> - if (pcdev->pi[id].admin_state_enabled &&
> - pcdev->pi[id].isr_pd_detected)
> - ret = _pse_pi_disable(pcdev, id);
> + bool pd_detected = pcdev->pi[id].isr_pd_detected;
> +
> + /* Gone before _pse_pi_disable() retries the power domain */
> pcdev->pi[id].isr_pd_detected = false;
> + if (pcdev->pi[id].admin_state_enabled && pd_detected)
> + ret = _pse_pi_disable(pcdev, id);
> }
>
> return ret;
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
next prev parent reply other threads:[~2026-10-19 9:11 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-18 18:20 [PATCH net] net: pse-pd: don't enable a PI again while disabling it Ross Cawston
2026-10-19 9:11 ` Kory Maincent [this message]
2026-10-19 18:42 ` netdev-bot+sashiko
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=748d24c8-2576-4958-95e7-01a1d40d5891@bootlin.com \
--to=kory.maincent@bootlin.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=kuba@kernel.org \
--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