aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorRajkumar Manoharan <rmanohar@qti.qualcomm.com>2016-03-22 07:52:11 -0400
committerKalle Valo <kvalo@qca.qualcomm.com>2016-04-04 10:03:07 -0400
commit59465fe46ef1c2caf2c1beca828c4f29d28b98ca (patch)
treeaee8a30ba4ed82d513d4aafcca1408dca765b820 /drivers/net
parentf9575793d44ce68b574d9d8ffb9813eb05c3fd2b (diff)
ath10k: speedup htt rx descriptor processing for tx completion
To optimize CPU usage htt rx descriptors will be reused instead of refilling it for htt rx copy engine (CE5). To support that all htt rx indications should be processed at same context. FIFO queue is used to maintain tx completion status for each msdu. This helps to retain the order of tx completion. Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/ath/ath10k/htt.h18
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_rx.c58
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_tx.c14
-rw-r--r--drivers/net/wireless/ath/ath10k/txrx.c12
4 files changed, 65 insertions, 37 deletions
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index d196bcc50e50..76c4bae0b434 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -22,6 +22,7 @@
22#include <linux/interrupt.h> 22#include <linux/interrupt.h>
23#include <linux/dmapool.h> 23#include <linux/dmapool.h>
24#include <linux/hashtable.h> 24#include <linux/hashtable.h>
25#include <linux/kfifo.h>
25#include <net/mac80211.h> 26#include <net/mac80211.h>
26 27
27#include "htc.h" 28#include "htc.h"
@@ -1526,10 +1527,15 @@ struct htt_resp {
1526/*** host side structures follow ***/ 1527/*** host side structures follow ***/
1527 1528
1528struct htt_tx_done { 1529struct htt_tx_done {
1529 u32 msdu_id; 1530 u16 msdu_id;
1530 bool discard; 1531 u16 status;
1531 bool no_ack; 1532};
1532 bool success; 1533
1534enum htt_tx_compl_state {
1535 HTT_TX_COMPL_STATE_NONE,
1536 HTT_TX_COMPL_STATE_ACK,
1537 HTT_TX_COMPL_STATE_NOACK,
1538 HTT_TX_COMPL_STATE_DISCARD,
1533}; 1539};
1534 1540
1535struct htt_peer_map_event { 1541struct htt_peer_map_event {
@@ -1650,6 +1656,9 @@ struct ath10k_htt {
1650 struct idr pending_tx; 1656 struct idr pending_tx;
1651 wait_queue_head_t empty_tx_wq; 1657 wait_queue_head_t empty_tx_wq;
1652 1658
1659 /* FIFO for storing tx done status {ack, no-ack, discard} and msdu id */
1660 DECLARE_KFIFO_PTR(txdone_fifo, struct htt_tx_done);
1661
1653 /* set if host-fw communication goes haywire 1662 /* set if host-fw communication goes haywire
1654 * used to avoid further failures */ 1663 * used to avoid further failures */
1655 bool rx_confused; 1664 bool rx_confused;
@@ -1658,7 +1667,6 @@ struct ath10k_htt {
1658 /* This is used to group tx/rx completions separately and process them 1667 /* This is used to group tx/rx completions separately and process them
1659 * in batches to reduce cache stalls */ 1668 * in batches to reduce cache stalls */
1660 struct tasklet_struct txrx_compl_task; 1669 struct tasklet_struct txrx_compl_task;
1661 struct sk_buff_head tx_compl_q;
1662 struct sk_buff_head rx_compl_q; 1670 struct sk_buff_head rx_compl_q;
1663 struct sk_buff_head rx_in_ord_compl_q; 1671 struct sk_buff_head rx_in_ord_compl_q;
1664 struct sk_buff_head tx_fetch_ind_q; 1672 struct sk_buff_head tx_fetch_ind_q;
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 06975bf49351..ea73a233ecd7 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -226,7 +226,6 @@ void ath10k_htt_rx_free(struct ath10k_htt *htt)
226 tasklet_kill(&htt->rx_replenish_task); 226 tasklet_kill(&htt->rx_replenish_task);
227 tasklet_kill(&htt->txrx_compl_task); 227 tasklet_kill(&htt->txrx_compl_task);
228 228
229 skb_queue_purge(&htt->tx_compl_q);
230 skb_queue_purge(&htt->rx_compl_q); 229 skb_queue_purge(&htt->rx_compl_q);
231 skb_queue_purge(&htt->rx_in_ord_compl_q); 230 skb_queue_purge(&htt->rx_in_ord_compl_q);
232 skb_queue_purge(&htt->tx_fetch_ind_q); 231 skb_queue_purge(&htt->tx_fetch_ind_q);
@@ -567,7 +566,6 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
567 tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task, 566 tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task,
568 (unsigned long)htt); 567 (unsigned long)htt);
569 568
570 skb_queue_head_init(&htt->tx_compl_q);
571 skb_queue_head_init(&htt->rx_compl_q); 569 skb_queue_head_init(&htt->rx_compl_q);
572 skb_queue_head_init(&htt->rx_in_ord_compl_q); 570 skb_queue_head_init(&htt->rx_in_ord_compl_q);
573 skb_queue_head_init(&htt->tx_fetch_ind_q); 571 skb_queue_head_init(&htt->tx_fetch_ind_q);
@@ -1678,7 +1676,7 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
1678 } 1676 }
1679} 1677}
1680 1678
1681static void ath10k_htt_rx_frm_tx_compl(struct ath10k *ar, 1679static void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar,
1682 struct sk_buff *skb) 1680 struct sk_buff *skb)
1683{ 1681{
1684 struct ath10k_htt *htt = &ar->htt; 1682 struct ath10k_htt *htt = &ar->htt;
@@ -1690,19 +1688,19 @@ static void ath10k_htt_rx_frm_tx_compl(struct ath10k *ar,
1690 1688
1691 switch (status) { 1689 switch (status) {
1692 case HTT_DATA_TX_STATUS_NO_ACK: 1690 case HTT_DATA_TX_STATUS_NO_ACK:
1693 tx_done.no_ack = true; 1691 tx_done.status = HTT_TX_COMPL_STATE_NOACK;
1694 break; 1692 break;
1695 case HTT_DATA_TX_STATUS_OK: 1693 case HTT_DATA_TX_STATUS_OK:
1696 tx_done.success = true; 1694 tx_done.status = HTT_TX_COMPL_STATE_ACK;
1697 break; 1695 break;
1698 case HTT_DATA_TX_STATUS_DISCARD: 1696 case HTT_DATA_TX_STATUS_DISCARD:
1699 case HTT_DATA_TX_STATUS_POSTPONE: 1697 case HTT_DATA_TX_STATUS_POSTPONE:
1700 case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL: 1698 case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL:
1701 tx_done.discard = true; 1699 tx_done.status = HTT_TX_COMPL_STATE_DISCARD;
1702 break; 1700 break;
1703 default: 1701 default:
1704 ath10k_warn(ar, "unhandled tx completion status %d\n", status); 1702 ath10k_warn(ar, "unhandled tx completion status %d\n", status);
1705 tx_done.discard = true; 1703 tx_done.status = HTT_TX_COMPL_STATE_DISCARD;
1706 break; 1704 break;
1707 } 1705 }
1708 1706
@@ -1712,7 +1710,20 @@ static void ath10k_htt_rx_frm_tx_compl(struct ath10k *ar,
1712 for (i = 0; i < resp->data_tx_completion.num_msdus; i++) { 1710 for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
1713 msdu_id = resp->data_tx_completion.msdus[i]; 1711 msdu_id = resp->data_tx_completion.msdus[i];
1714 tx_done.msdu_id = __le16_to_cpu(msdu_id); 1712 tx_done.msdu_id = __le16_to_cpu(msdu_id);
1715 ath10k_txrx_tx_unref(htt, &tx_done); 1713
1714 /* kfifo_put: In practice firmware shouldn't fire off per-CE
1715 * interrupt and main interrupt (MSI/-X range case) for the same
1716 * HTC service so it should be safe to use kfifo_put w/o lock.
1717 *
1718 * From kfifo_put() documentation:
1719 * Note that with only one concurrent reader and one concurrent
1720 * writer, you don't need extra locking to use these macro.
1721 */
1722 if (!kfifo_put(&htt->txdone_fifo, tx_done)) {
1723 ath10k_warn(ar, "txdone fifo overrun, msdu_id %d status %d\n",
1724 tx_done.msdu_id, tx_done.status);
1725 ath10k_txrx_tx_unref(htt, &tx_done);
1726 }
1716 } 1727 }
1717} 1728}
1718 1729
@@ -2339,18 +2350,17 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
2339 struct htt_tx_done tx_done = {}; 2350 struct htt_tx_done tx_done = {};
2340 int status = __le32_to_cpu(resp->mgmt_tx_completion.status); 2351 int status = __le32_to_cpu(resp->mgmt_tx_completion.status);
2341 2352
2342 tx_done.msdu_id = 2353 tx_done.msdu_id = __le32_to_cpu(resp->mgmt_tx_completion.desc_id);
2343 __le32_to_cpu(resp->mgmt_tx_completion.desc_id);
2344 2354
2345 switch (status) { 2355 switch (status) {
2346 case HTT_MGMT_TX_STATUS_OK: 2356 case HTT_MGMT_TX_STATUS_OK:
2347 tx_done.success = true; 2357 tx_done.status = HTT_TX_COMPL_STATE_ACK;
2348 break; 2358 break;
2349 case HTT_MGMT_TX_STATUS_RETRY: 2359 case HTT_MGMT_TX_STATUS_RETRY:
2350 tx_done.no_ack = true; 2360 tx_done.status = HTT_TX_COMPL_STATE_NOACK;
2351 break; 2361 break;
2352 case HTT_MGMT_TX_STATUS_DROP: 2362 case HTT_MGMT_TX_STATUS_DROP:
2353 tx_done.discard = true; 2363 tx_done.status = HTT_TX_COMPL_STATE_DISCARD;
2354 break; 2364 break;
2355 } 2365 }
2356 2366
@@ -2364,9 +2374,9 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
2364 break; 2374 break;
2365 } 2375 }
2366 case HTT_T2H_MSG_TYPE_TX_COMPL_IND: 2376 case HTT_T2H_MSG_TYPE_TX_COMPL_IND:
2367 skb_queue_tail(&htt->tx_compl_q, skb); 2377 ath10k_htt_rx_tx_compl_ind(htt->ar, skb);
2368 tasklet_schedule(&htt->txrx_compl_task); 2378 tasklet_schedule(&htt->txrx_compl_task);
2369 return; 2379 break;
2370 case HTT_T2H_MSG_TYPE_SEC_IND: { 2380 case HTT_T2H_MSG_TYPE_SEC_IND: {
2371 struct ath10k *ar = htt->ar; 2381 struct ath10k *ar = htt->ar;
2372 struct htt_security_indication *ev = &resp->security_indication; 2382 struct htt_security_indication *ev = &resp->security_indication;
@@ -2475,7 +2485,7 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr)
2475{ 2485{
2476 struct ath10k_htt *htt = (struct ath10k_htt *)ptr; 2486 struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
2477 struct ath10k *ar = htt->ar; 2487 struct ath10k *ar = htt->ar;
2478 struct sk_buff_head tx_q; 2488 struct htt_tx_done tx_done = {};
2479 struct sk_buff_head rx_q; 2489 struct sk_buff_head rx_q;
2480 struct sk_buff_head rx_ind_q; 2490 struct sk_buff_head rx_ind_q;
2481 struct sk_buff_head tx_ind_q; 2491 struct sk_buff_head tx_ind_q;
@@ -2483,15 +2493,10 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr)
2483 struct sk_buff *skb; 2493 struct sk_buff *skb;
2484 unsigned long flags; 2494 unsigned long flags;
2485 2495
2486 __skb_queue_head_init(&tx_q);
2487 __skb_queue_head_init(&rx_q); 2496 __skb_queue_head_init(&rx_q);
2488 __skb_queue_head_init(&rx_ind_q); 2497 __skb_queue_head_init(&rx_ind_q);
2489 __skb_queue_head_init(&tx_ind_q); 2498 __skb_queue_head_init(&tx_ind_q);
2490 2499
2491 spin_lock_irqsave(&htt->tx_compl_q.lock, flags);
2492 skb_queue_splice_init(&htt->tx_compl_q, &tx_q);
2493 spin_unlock_irqrestore(&htt->tx_compl_q.lock, flags);
2494
2495 spin_lock_irqsave(&htt->rx_compl_q.lock, flags); 2500 spin_lock_irqsave(&htt->rx_compl_q.lock, flags);
2496 skb_queue_splice_init(&htt->rx_compl_q, &rx_q); 2501 skb_queue_splice_init(&htt->rx_compl_q, &rx_q);
2497 spin_unlock_irqrestore(&htt->rx_compl_q.lock, flags); 2502 spin_unlock_irqrestore(&htt->rx_compl_q.lock, flags);
@@ -2504,10 +2509,13 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr)
2504 skb_queue_splice_init(&htt->tx_fetch_ind_q, &tx_ind_q); 2509 skb_queue_splice_init(&htt->tx_fetch_ind_q, &tx_ind_q);
2505 spin_unlock_irqrestore(&htt->tx_fetch_ind_q.lock, flags); 2510 spin_unlock_irqrestore(&htt->tx_fetch_ind_q.lock, flags);
2506 2511
2507 while ((skb = __skb_dequeue(&tx_q))) { 2512 /* kfifo_get: called only within txrx_tasklet so it's neatly serialized.
2508 ath10k_htt_rx_frm_tx_compl(htt->ar, skb); 2513 * From kfifo_get() documentation:
2509 dev_kfree_skb_any(skb); 2514 * Note that with only one concurrent reader and one concurrent writer,
2510 } 2515 * you don't need extra locking to use these macro.
2516 */
2517 while (kfifo_get(&htt->txdone_fifo, &tx_done))
2518 ath10k_txrx_tx_unref(htt, &tx_done);
2511 2519
2512 while ((skb = __skb_dequeue(&tx_ind_q))) { 2520 while ((skb = __skb_dequeue(&tx_ind_q))) {
2513 ath10k_htt_rx_tx_fetch_ind(ar, skb); 2521 ath10k_htt_rx_tx_fetch_ind(ar, skb);
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index b2ae122381ca..9baa2e677f8a 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -339,8 +339,18 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
339 goto free_frag_desc; 339 goto free_frag_desc;
340 } 340 }
341 341
342 size = roundup_pow_of_two(htt->max_num_pending_tx);
343 ret = kfifo_alloc(&htt->txdone_fifo, size, GFP_KERNEL);
344 if (ret) {
345 ath10k_err(ar, "failed to alloc txdone fifo: %d\n", ret);
346 goto free_txq;
347 }
348
342 return 0; 349 return 0;
343 350
351free_txq:
352 ath10k_htt_tx_free_txq(htt);
353
344free_frag_desc: 354free_frag_desc:
345 ath10k_htt_tx_free_cont_frag_desc(htt); 355 ath10k_htt_tx_free_cont_frag_desc(htt);
346 356
@@ -364,8 +374,8 @@ static int ath10k_htt_tx_clean_up_pending(int msdu_id, void *skb, void *ctx)
364 374
365 ath10k_dbg(ar, ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n", msdu_id); 375 ath10k_dbg(ar, ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n", msdu_id);
366 376
367 tx_done.discard = 1;
368 tx_done.msdu_id = msdu_id; 377 tx_done.msdu_id = msdu_id;
378 tx_done.status = HTT_TX_COMPL_STATE_DISCARD;
369 379
370 ath10k_txrx_tx_unref(htt, &tx_done); 380 ath10k_txrx_tx_unref(htt, &tx_done);
371 381
@@ -388,6 +398,8 @@ void ath10k_htt_tx_free(struct ath10k_htt *htt)
388 398
389 ath10k_htt_tx_free_txq(htt); 399 ath10k_htt_tx_free_txq(htt);
390 ath10k_htt_tx_free_cont_frag_desc(htt); 400 ath10k_htt_tx_free_cont_frag_desc(htt);
401 WARN_ON(!kfifo_is_empty(&htt->txdone_fifo));
402 kfifo_free(&htt->txdone_fifo);
391} 403}
392 404
393void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) 405void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 48e26cdfe9a5..9369411a9ac0 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -61,9 +61,8 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
61 struct sk_buff *msdu; 61 struct sk_buff *msdu;
62 62
63 ath10k_dbg(ar, ATH10K_DBG_HTT, 63 ath10k_dbg(ar, ATH10K_DBG_HTT,
64 "htt tx completion msdu_id %u discard %d no_ack %d success %d\n", 64 "htt tx completion msdu_id %u status %d\n",
65 tx_done->msdu_id, !!tx_done->discard, 65 tx_done->msdu_id, tx_done->status);
66 !!tx_done->no_ack, !!tx_done->success);
67 66
68 if (tx_done->msdu_id >= htt->max_num_pending_tx) { 67 if (tx_done->msdu_id >= htt->max_num_pending_tx) {
69 ath10k_warn(ar, "warning: msdu_id %d too big, ignoring\n", 68 ath10k_warn(ar, "warning: msdu_id %d too big, ignoring\n",
@@ -101,7 +100,7 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
101 memset(&info->status, 0, sizeof(info->status)); 100 memset(&info->status, 0, sizeof(info->status));
102 trace_ath10k_txrx_tx_unref(ar, tx_done->msdu_id); 101 trace_ath10k_txrx_tx_unref(ar, tx_done->msdu_id);
103 102
104 if (tx_done->discard) { 103 if (tx_done->status == HTT_TX_COMPL_STATE_DISCARD) {
105 ieee80211_free_txskb(htt->ar->hw, msdu); 104 ieee80211_free_txskb(htt->ar->hw, msdu);
106 return 0; 105 return 0;
107 } 106 }
@@ -109,10 +108,11 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
109 if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) 108 if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
110 info->flags |= IEEE80211_TX_STAT_ACK; 109 info->flags |= IEEE80211_TX_STAT_ACK;
111 110
112 if (tx_done->no_ack) 111 if (tx_done->status == HTT_TX_COMPL_STATE_NOACK)
113 info->flags &= ~IEEE80211_TX_STAT_ACK; 112 info->flags &= ~IEEE80211_TX_STAT_ACK;
114 113
115 if (tx_done->success && (info->flags & IEEE80211_TX_CTL_NO_ACK)) 114 if ((tx_done->status == HTT_TX_COMPL_STATE_ACK) &&
115 (info->flags & IEEE80211_TX_CTL_NO_ACK))
116 info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; 116 info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
117 117
118 ieee80211_tx_status(htt->ar->hw, msdu); 118 ieee80211_tx_status(htt->ar->hw, msdu);