[PATCH net v3 08/14] net: pse-pd: stop async event sources before freeing PI data in unregister
From: Carlo Szelinsky
Date: Tue Nov 03 2026 - 17:03:25 EST
pse_controller_unregister() flushes the power domains and frees the PI
array before it disables the IRQ, while pse_isr() uses both. No in-tree
driver hits this: tps23881, the only user of devm_pse_irq_helper(),
requests its IRQ after it registers the controller, so devres frees
the IRQ before unregister runs. A driver that keeps its IRQ across
unregister would read freed memory.
Disable the IRQ and drain the notification worker first, and only then
flush the power domains and release the PI array.
The worker can also be queued again after the drain. With the static
budget strategy, powering a PI can switch off a PI of lower priority
through pse_disable_pi_pol(), which queues the worker. A PI is powered
from pse_isr(), from a priority change, from the retry after a disable,
and when a consumer enables it. All of these go through pse_pi_enable()
or _pse_pi_delivery_power_sw_pw_ctrl().
So pse_controller_unregister() now first sets pcdev->unregistering
under pcdev->lock. After that, both functions return -ENODEV.
Disabling a PI still works.
The next patches in this series build on this order.
Found by code inspection and AI review.
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 | 27 +++++++++++++++++++++++++--
include/linux/pse-pd/pse.h | 3 +++
2 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index 113a34b09386..36f8e4e7d191 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -612,6 +612,13 @@ static int _pse_pi_delivery_power_sw_pw_ctrl(struct pse_controller_dev *pcdev,
struct pse_pi *pi = &pcdev->pi[id];
int ret, pw_req;
+ /* pse_isr(), a priority change and the retry after a disable all
+ * power a PI through here, so stop them here once unregistering
+ * has started.
+ */
+ if (pcdev->unregistering)
+ return -ENODEV;
+
if (!ops->pi_get_pw_req) {
/* No power allocation management */
ret = ops->pi_enable(pcdev, id);
@@ -668,6 +675,11 @@ static int pse_pi_enable(struct regulator_dev *rdev)
id = rdev_get_id(rdev);
mutex_lock(&pcdev->lock);
+ if (pcdev->unregistering) {
+ mutex_unlock(&pcdev->lock);
+ return -ENODEV;
+ }
+
if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d)) {
/* Manage enabled status by software.
* Real enable process will happen if a port is connected.
@@ -1160,11 +1172,22 @@ EXPORT_SYMBOL_GPL(pse_controller_register);
*/
void pse_controller_unregister(struct pse_controller_dev *pcdev)
{
- pse_flush_pw_ds(pcdev);
- pse_release_pis(pcdev);
+ /* From now on the PSE core must not power a PI, so nothing queues
+ * a new notification once the worker is drained below.
+ */
+ mutex_lock(&pcdev->lock);
+ pcdev->unregistering = true;
+ mutex_unlock(&pcdev->lock);
+
+ /* Stop the IRQ and the notification worker first: pse_isr() uses
+ * pcdev->pi and the power domains that pse_flush_pw_ds() drops,
+ * and the worker reads the kfifo that is freed below.
+ */
if (pcdev->irq)
disable_irq(pcdev->irq);
cancel_work_sync(&pcdev->ntf_work);
+ pse_flush_pw_ds(pcdev);
+ pse_release_pis(pcdev);
kfifo_free(&pcdev->ntf_fifo);
mutex_lock(&pse_list_mutex);
list_del(&pcdev->list);
diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h
index 4e5696cfade7..a917ac32dc0c 100644
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -298,6 +298,8 @@ struct pse_ntf {
* @ntf_work: workqueue for PSE notification management
* @ntf_fifo: PSE notifications FIFO
* @ntf_fifo_lock: protect @ntf_fifo writer
+ * @unregistering: set under @lock when unregistering starts; the PSE
+ * core switches no PI on after that
*/
struct pse_controller_dev {
const struct pse_controller_ops *ops;
@@ -317,6 +319,7 @@ struct pse_controller_dev {
struct work_struct ntf_work;
DECLARE_KFIFO_PTR(ntf_fifo, struct pse_ntf);
spinlock_t ntf_fifo_lock; /* Protect @ntf_fifo writer */
+ bool unregistering;
};
/**
--
2.43.0