From f8b59ca3be7b17703df55ef9bbe3f6398d5c094c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 23 Jun 2026 13:17:01 +0200 Subject: [PATCH] wifi: mt76: don't pin undrainable PS stations in the tx scheduler A power-save station whose buffered frames cannot be delivered (e.g. it stopped responding) consumes no airtime, so its airtime DRR deficit never goes negative. Returning its txq via ieee80211_return_txq() then keeps it pinned at the head of the airtime scheduler's active_txqs list. Since mt7996 uses a single ieee80211_hw for all bands, that list is shared device-wide, and the schedule_round guard in ieee80211_next_txq() ends the scheduling pass at the stuck station, starving every other station on all bands until it is removed by inactivity timeout. Only return the txq while it makes progress or the shared queue is stopped. Otherwise leave it unscheduled and rely on the existing tx completion handler to re-arm it once a buffered frame completes, matching the non-AQL throttle path. Fixes: 9a46d8d21d2a ("wifi: mt76: add PS buffering support for HW-managed TIM drivers") Signed-off-by: Felix Fietkau --- tx.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tx.c b/tx.c index dc60a966..4507fd15 100644 --- a/tx.c +++ b/tx.c @@ -591,10 +591,22 @@ mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid) if (!mt76_txq_stopped(q)) n_frames = mt76_txq_send_burst(phy, q, mtxq, wcid); - ieee80211_return_txq(phy->hw, txq, false); - if (unlikely(n_frames < 0)) return n_frames; + + /* + * Keep the station in the airtime scheduler only while it + * makes progress or the shared queue is stopped. A PS + * station that cannot be drained consumes no airtime, so + * its DRR deficit never goes negative and returning it here + * would pin it at the head of the device-wide active_txqs + * list, starving all other stations on every band. Leave it + * unscheduled instead and let mt76_tx_complete_skb() re-arm + * it once a buffered frame completes. + */ + if (n_frames > 0 || mt76_txq_stopped(q)) + ieee80211_return_txq(phy->hw, txq, false); + ret += n_frames; continue; }