diff options
author | Michal Kazior <michal.kazior@tieto.com> | 2013-09-24 04:18:36 -0400 |
---|---|---|
committer | Kalle Valo <kvalo@qca.qualcomm.com> | 2013-09-26 10:22:54 -0400 |
commit | 6e712d427cb0542afdd5220edb6e4f4f8a5b952d (patch) | |
tree | ab9d056def4b85a1f1214a7feb13ebd277d0af55 /drivers/net/wireless/ath/ath10k/htt_rx.c | |
parent | 4d316c79a5dcc13dea8110f0fcf295a17b4b625b (diff) |
ath10k: replenish HTT RX buffers in a tasklet
This starves FW RX ring buffer in case of
excessive RX. This prevents from CPU being
overwhelmed by RX indications/completions by
naturally forbiddin FW to submit more RX.
This fixes RX starvation on slow machines when
under heavy RX traffic.
kvalo: remove extra newline
Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
Diffstat (limited to 'drivers/net/wireless/ath/ath10k/htt_rx.c')
-rw-r--r-- | drivers/net/wireless/ath/ath10k/htt_rx.c | 35 |
1 files changed, 32 insertions, 3 deletions
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index c4637f18734f..90d4f74c28d7 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c | |||
@@ -182,10 +182,27 @@ static int ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num) | |||
182 | 182 | ||
183 | static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt) | 183 | static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt) |
184 | { | 184 | { |
185 | int ret, num_to_fill; | 185 | int ret, num_deficit, num_to_fill; |
186 | 186 | ||
187 | /* Refilling the whole RX ring buffer proves to be a bad idea. The | ||
188 | * reason is RX may take up significant amount of CPU cycles and starve | ||
189 | * other tasks, e.g. TX on an ethernet device while acting as a bridge | ||
190 | * with ath10k wlan interface. This ended up with very poor performance | ||
191 | * once CPU the host system was overwhelmed with RX on ath10k. | ||
192 | * | ||
193 | * By limiting the number of refills the replenishing occurs | ||
194 | * progressively. This in turns makes use of the fact tasklets are | ||
195 | * processed in FIFO order. This means actual RX processing can starve | ||
196 | * out refilling. If there's not enough buffers on RX ring FW will not | ||
197 | * report RX until it is refilled with enough buffers. This | ||
198 | * automatically balances load wrt to CPU power. | ||
199 | * | ||
200 | * This probably comes at a cost of lower maximum throughput but | ||
201 | * improves the avarage and stability. */ | ||
187 | spin_lock_bh(&htt->rx_ring.lock); | 202 | spin_lock_bh(&htt->rx_ring.lock); |
188 | num_to_fill = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt; | 203 | num_deficit = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt; |
204 | num_to_fill = min(ATH10K_HTT_MAX_NUM_REFILL, num_deficit); | ||
205 | num_deficit -= num_to_fill; | ||
189 | ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill); | 206 | ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill); |
190 | if (ret == -ENOMEM) { | 207 | if (ret == -ENOMEM) { |
191 | /* | 208 | /* |
@@ -196,6 +213,8 @@ static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt) | |||
196 | */ | 213 | */ |
197 | mod_timer(&htt->rx_ring.refill_retry_timer, jiffies + | 214 | mod_timer(&htt->rx_ring.refill_retry_timer, jiffies + |
198 | msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS)); | 215 | msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS)); |
216 | } else if (num_deficit > 0) { | ||
217 | tasklet_schedule(&htt->rx_replenish_task); | ||
199 | } | 218 | } |
200 | spin_unlock_bh(&htt->rx_ring.lock); | 219 | spin_unlock_bh(&htt->rx_ring.lock); |
201 | } | 220 | } |
@@ -217,6 +236,7 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt) | |||
217 | int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld; | 236 | int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld; |
218 | 237 | ||
219 | del_timer_sync(&htt->rx_ring.refill_retry_timer); | 238 | del_timer_sync(&htt->rx_ring.refill_retry_timer); |
239 | tasklet_kill(&htt->rx_replenish_task); | ||
220 | 240 | ||
221 | while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) { | 241 | while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) { |
222 | struct sk_buff *skb = | 242 | struct sk_buff *skb = |
@@ -446,6 +466,12 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, | |||
446 | return msdu_chaining; | 466 | return msdu_chaining; |
447 | } | 467 | } |
448 | 468 | ||
469 | static void ath10k_htt_rx_replenish_task(unsigned long ptr) | ||
470 | { | ||
471 | struct ath10k_htt *htt = (struct ath10k_htt *)ptr; | ||
472 | ath10k_htt_rx_msdu_buff_replenish(htt); | ||
473 | } | ||
474 | |||
449 | int ath10k_htt_rx_attach(struct ath10k_htt *htt) | 475 | int ath10k_htt_rx_attach(struct ath10k_htt *htt) |
450 | { | 476 | { |
451 | dma_addr_t paddr; | 477 | dma_addr_t paddr; |
@@ -506,6 +532,9 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt) | |||
506 | if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level)) | 532 | if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level)) |
507 | goto err_fill_ring; | 533 | goto err_fill_ring; |
508 | 534 | ||
535 | tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task, | ||
536 | (unsigned long)htt); | ||
537 | |||
509 | ath10k_dbg(ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n", | 538 | ath10k_dbg(ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n", |
510 | htt->rx_ring.size, htt->rx_ring.fill_level); | 539 | htt->rx_ring.size, htt->rx_ring.fill_level); |
511 | return 0; | 540 | return 0; |
@@ -956,7 +985,7 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, | |||
956 | } | 985 | } |
957 | } | 986 | } |
958 | 987 | ||
959 | ath10k_htt_rx_msdu_buff_replenish(htt); | 988 | tasklet_schedule(&htt->rx_replenish_task); |
960 | } | 989 | } |
961 | 990 | ||
962 | static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, | 991 | static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, |