[PATCH ath-next] wifi: ath12k: fix IRQ disable imbalance in AHB ext interrupt handler
P Praneesh
praneesh.p at oss.qualcomm.com
Thu Oct 15 01:44:45 PDT 2026
On AHB platforms such as IPQ5332, IRQ group 3 maps multiple hardware
rings to a single NAPI instance. Each ring is assigned its own IRQ
line, but all IRQs in the group share one napi struct and one poll
function.
When two IRQs from the same group fire simultaneously on different
CPUs, both handlers call ath12k_ahb_ext_grp_disable(), which invokes
disable_irq_nosync() for every IRQ in the group. This increments the
interrupt controller's disable reference count for each IRQ twice. The
single NAPI poll that runs calls ath12k_ahb_ext_grp_enable() once,
decrementing each count from 2 to 1. Because the count never reaches 0,
all IRQs in the group remain permanently masked by the interrupt
controller.
With no further interrupts, TX completions stop being reaped from the
WBM release ring. The pending TX descriptor count grows until the WBM
idle link descriptor pool is exhausted. Once full, TX ring allocation
fails and no new packets including management frames can be enqueued.
This causes stations to disassociate with reason 34 (DISASSOC_LOW_ACK)
and ath12k_mac_flush() to time out:
"failed to flush transmit queue, data pkts req N pending N"
where N equals ATH12K_NUM_POOL_TX_DESC(ab).
Fix this by using napi_schedule_prep() to atomically test-and-set
NAPI_STATE_SCHED before disabling IRQs. Only the first CPU to win the
atomic operation disables the group and calls __napi_schedule(). Any
concurrent handler that loses the race skips the disable entirely,
keeping the reference count balanced. Guard the enable_irq() call in
the poll routine with the return value of napi_complete_done() to
prevent an unbalanced enable if the NAPI instance was re-scheduled
before the poll completed.
Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.7-00587-QCAHKSWPL_SILICONZ-1
Fixes: 6cee30f0da75 ("wifi: ath12k: add AHB driver support for IPQ5332")
Signed-off-by: P Praneesh <praneesh.p at oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/ahb.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c
index 2c55aae06dd0..e5bbfca2b2ac 100644
--- a/drivers/net/wireless/ath/ath12k/ahb.c
+++ b/drivers/net/wireless/ath/ath12k/ahb.c
@@ -602,8 +602,8 @@ static int ath12k_ahb_ext_grp_napi_poll(struct napi_struct *napi, int budget)
work_done = ath12k_dp_service_srng(dp, irq_grp, budget);
if (work_done < budget) {
- napi_complete_done(napi, work_done);
- ath12k_ahb_ext_grp_enable(irq_grp);
+ if (likely(napi_complete_done(napi, work_done)))
+ ath12k_ahb_ext_grp_enable(irq_grp);
}
if (work_done > budget)
@@ -619,9 +619,10 @@ static irqreturn_t ath12k_ahb_ext_interrupt_handler(int irq, void *arg)
/* last interrupt received for this group */
irq_grp->timestamp = jiffies;
- ath12k_ahb_ext_grp_disable(irq_grp);
-
- napi_schedule(&irq_grp->napi);
+ if (napi_schedule_prep(&irq_grp->napi)) {
+ ath12k_ahb_ext_grp_disable(irq_grp);
+ __napi_schedule(&irq_grp->napi);
+ }
return IRQ_HANDLED;
}
base-commit: cd026e68fdbf88b9cc522b76c7a9d92c9a41f709
--
2.43.0
More information about the ath12k
mailing list