ieee80211_txq_schedule_start() sets local->schedule_round[ac] to 0 when the access category is over its airtime limit, so that ieee80211_next_txq() serves nothing until a round passes the check again. That round is then numbered 1. ieee80211_next_txq() ends a round when it reaches a txq whose schedule_round equals the current number, so a txq served in a round numbered 1 is skipped in the next round numbered 1 as well. Where the airtime queue limit binds, closed and open rounds alternate, so half of the rounds that could refill the queue return nothing. Track whether a round is open in its own flag and leave the round number alone. Checking the limit in ieee80211_next_txq() instead would walk the active list once per txq served rather than once per round. Measured on an IPQ8074 AP with ath11k advertising AQL through the series "wifi: ath11k: airtime queue limits, fairness and a driver TXQ scheduler", one 1x1 VHT80 station, BE aql_txq_limit 500/1000 us, 20 s runs, three interleaved runs per arm, the skipped selections counted where ieee80211_next_txq() ends the round. With a TCP download forwarded from a wired host, 4227, 4195 and 4268 selections are skipped per run without this change and 15, 24 and 41 with it, at 99 to 100 Mbit/s either way: the network stack's arrivals start rounds often enough to cover the loss. With the download generated on the access point, where TCP small queues pace the arrivals, the skipped rounds are the ones that would have refilled the queue: 35.6, 44.4 and 42.8 Mbit/s without this change and 94.7, 94.6 and 95.0 with it. Fixes: 8e4bac067105 ("wifi: mac80211: add a per-PHY AQL limit to improve fairness") Assisted-by: Claude:claude-opus-5 Signed-off-by: Julius Bairaktaris --- net/mac80211/ieee80211_i.h | 1 + net/mac80211/tx.c | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 53b0b08d3459..65311340db40 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1468,6 +1468,7 @@ struct ieee80211_local { spinlock_t active_txq_lock[IEEE80211_NUM_ACS]; struct list_head active_txqs[IEEE80211_NUM_ACS]; u16 schedule_round[IEEE80211_NUM_ACS]; + bool schedule_open[IEEE80211_NUM_ACS]; /* serializes ieee80211_handle_wake_tx_queue */ spinlock_t handle_wake_tx_queue_lock; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index c33092960df2..c665ced9c05c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4122,7 +4122,7 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac) spin_lock_bh(&local->active_txq_lock[ac]); - if (!local->schedule_round[ac]) + if (!local->schedule_open[ac]) goto out; begin: @@ -4347,12 +4347,12 @@ void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac) spin_lock_bh(&local->active_txq_lock[ac]); - if (ieee80211_txq_schedule_airtime_check(local, ac)) { + local->schedule_open[ac] = + ieee80211_txq_schedule_airtime_check(local, ac); + if (local->schedule_open[ac]) { local->schedule_round[ac]++; if (!local->schedule_round[ac]) local->schedule_round[ac]++; - } else { - local->schedule_round[ac] = 0; } spin_unlock_bh(&local->active_txq_lock[ac]); -- 2.53.0