[PATCH net v6 1/4] net: wwan: t7xx: fix runtime PM usage count underflow on -EACCES

From: Tim JH Chen

Date: Thu Oct 01 2026 - 21:48:00 EST


t7xx_dpmaif_tx_hw_push_thread(), t7xx_dpmaif_tx_done() and
t7xx_dpmaif_bat_release_work() treat -EACCES from
pm_runtime_resume_and_get() as success and proceed to access the
hardware. That is intentional, but pm_runtime_resume_and_get() has
already dropped the usage count it took before returning -EACCES, so the
unconditional pm_runtime_put_autosuspend() at the end of each context
drops a reference that was never held and drives the usage count
negative.

Only balance the reference when it was actually taken.

Fixes: 46e8f49ed7b3 ("net: wwan: t7xx: Introduce power management")
Signed-off-by: Tim JH Chen <tim770802@xxxxxxxxx>
---
drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c | 3 ++-
drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c | 10 ++++++++--
2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
index 5af90ca6e063..0e1174ee611d 100644
--- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
+++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
@@ -1082,7 +1082,8 @@ static void t7xx_dpmaif_bat_release_work(struct work_struct *work)
}

t7xx_pci_enable_sleep(dpmaif_ctrl->t7xx_dev);
- pm_runtime_put_autosuspend(dpmaif_ctrl->dev);
+ if (ret != -EACCES)
+ pm_runtime_put_autosuspend(dpmaif_ctrl->dev);
}

int t7xx_dpmaif_bat_rel_wq_alloc(struct dpmaif_ctrl *dpmaif_ctrl)
diff --git a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c
index 236d632cf591..bd6116a8c541 100644
--- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c
+++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c
@@ -160,12 +160,16 @@ static void t7xx_dpmaif_tx_done(struct work_struct *work)
struct dpmaif_tx_queue *txq = container_of(work, struct dpmaif_tx_queue, dpmaif_tx_work);
struct dpmaif_ctrl *dpmaif_ctrl = txq->dpmaif_ctrl;
struct dpmaif_hw_info *hw_info;
+ bool pm_ref;
int ret;

ret = pm_runtime_resume_and_get(dpmaif_ctrl->dev);
if (ret < 0 && ret != -EACCES)
return;

+ /* -EACCES means no reference was taken; only balance a real one. */
+ pm_ref = !ret;
+
/* The device may be in low power state. Disable sleep if needed */
t7xx_pci_disable_sleep(dpmaif_ctrl->t7xx_dev);
if (t7xx_pci_sleep_disable_complete(dpmaif_ctrl->t7xx_dev)) {
@@ -185,7 +189,8 @@ static void t7xx_dpmaif_tx_done(struct work_struct *work)
}

t7xx_pci_enable_sleep(dpmaif_ctrl->t7xx_dev);
- pm_runtime_put_autosuspend(dpmaif_ctrl->dev);
+ if (pm_ref)
+ pm_runtime_put_autosuspend(dpmaif_ctrl->dev);
}

static void t7xx_setup_msg_drb(struct dpmaif_ctrl *dpmaif_ctrl, unsigned int q_num,
@@ -467,7 +472,8 @@ static int t7xx_dpmaif_tx_hw_push_thread(void *arg)
t7xx_pci_disable_sleep(dpmaif_ctrl->t7xx_dev);
t7xx_do_tx_hw_push(dpmaif_ctrl);
t7xx_pci_enable_sleep(dpmaif_ctrl->t7xx_dev);
- pm_runtime_put_autosuspend(dpmaif_ctrl->dev);
+ if (ret != -EACCES)
+ pm_runtime_put_autosuspend(dpmaif_ctrl->dev);
}

return 0;
--
2.43.0