ath11k_dp_tx_completion_handler() reaps every completion the release ring holds before it returns, and ath11k_dp_service_srng() charges none of that work against the NAPI budget the poll was given, unlike every receive handler below it. With the scheduling round that now ends the handler, one poll that finds the ring full runs one round for all of it. Reap at most the budget's worth of completions per poll and return the count, so the poll ends and the round runs once per budget of completions rather than once per ring. The completions left in the FIFO are reaped by the next poll, which NAPI schedules at once when the budget was spent. Measured on an IPQ8074 AP with a TCP download forwarded from a wired host to one 1x1 VHT80 station, BE aql_txq_limit 500/1000 us, 20 s runs, three interleaved runs per arm, download rate and the RTT the sender's TCP sees on that flow, mean and maximum: Mbit/s RTT mean ms RTT max ms with this patch 100.8, 101.5, 100.6 27, 26, 26 32, 31, 32 without 74.7, 101.1, 100.6 120, 27, 26 1873, 31, 30 Without it a poll that finds the ring full reaps all of it before the round runs, and one run in three stalls for close to two seconds. At the default limit both arms read 101 Mbit/s at 47 to 55 ms. Two soaks on the series, a 91 s TCP soak at 1033 Mbit/s aggregate over five streams, about 86000 completions per second, and a 60 s soak of 200-byte UDP datagrams at 60906 packets per second, produced no status_fifo-is-full warning and no TCL ring-full failure. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.9.0.1-02146-QCAHKSWPL_SILICONZ-1 Assisted-by: Claude:claude-opus-5 Signed-off-by: Julius Bairaktaris --- drivers/net/wireless/ath/ath11k/dp.c | 10 ++++++++-- drivers/net/wireless/ath/ath11k/dp_tx.c | 11 +++++++++-- drivers/net/wireless/ath/ath11k/dp_tx.h | 3 ++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index be65d73b904b..ddb434c513e0 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -783,8 +783,14 @@ int ath11k_dp_service_srng(struct ath11k_base *ab, for (i = 0; i < ab->hw_params.hal_params->num_tx_rings; i++) { if (BIT(ab->hw_params.hal_params->tcl2wbm_rbm_map[i].wbm_ring_num) & - ab->hw_params.ring_mask->tx[grp_id]) - ath11k_dp_tx_completion_handler(ab, i); + ab->hw_params.ring_mask->tx[grp_id]) { + work_done = + ath11k_dp_tx_completion_handler(ab, i, budget); + budget -= work_done; + tot_work_done += work_done; + if (budget <= 0) + goto done; + } } if (ab->hw_params.ring_mask->rx_err[grp_id]) { diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index 70d326fe6a1f..3997bd336942 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -685,7 +685,8 @@ static inline void ath11k_dp_tx_status_parse(struct ath11k_base *ab, ts->rate_stats = 0; } -void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id) +int ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id, + int budget) { struct ath11k *ar; struct ath11k_dp *dp = &ab->dp; @@ -695,6 +696,7 @@ void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id) struct hal_tx_status ts = {}; struct dp_tx_ring *tx_ring = &dp->tx_ring[ring_id]; unsigned long push = 0; + int done = 0; u32 *desc; u32 msdu_id; u8 mac_id, i; @@ -723,10 +725,13 @@ void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id) spin_unlock_bh(&status_ring->lock); - while (ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_tail) != tx_ring->tx_status_head) { + while (done < budget && + ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_tail) != + tx_ring->tx_status_head) { struct hal_wbm_release_ring *tx_status; u32 desc_id; + done++; tx_ring->tx_status_tail = ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_tail); tx_status = &tx_ring->tx_status[tx_ring->tx_status_tail]; @@ -774,6 +779,8 @@ void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id) for_each_set_bit(i, &push, ab->num_radios * IEEE80211_NUM_ACS) ath11k_mac_tx_push_pending(ab->pdevs[i / IEEE80211_NUM_ACS].ar, i % IEEE80211_NUM_ACS); + + return done; } int ath11k_dp_tx_send_reo_cmd(struct ath11k_base *ab, struct dp_rx_tid *rx_tid, diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.h b/drivers/net/wireless/ath/ath11k/dp_tx.h index 9303b5ba6e01..b5296cda73e3 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.h +++ b/drivers/net/wireless/ath/ath11k/dp_tx.h @@ -21,7 +21,8 @@ void ath11k_dp_tx_update_txcompl(struct ath11k *ar, struct hal_tx_status *ts); int ath11k_dp_tx_htt_h2t_ver_req_msg(struct ath11k_base *ab); int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, struct ath11k_sta *arsta, struct sk_buff *skb); -void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id); +int ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id, + int budget); int ath11k_dp_tx_send_reo_cmd(struct ath11k_base *ab, struct dp_rx_tid *rx_tid, enum hal_reo_cmd_type type, struct ath11k_hal_reo_cmd *cmd, -- 2.53.0