Skip to content

Commit

Permalink
mac80211: ath11k_nss: add additional fixes for rx path
Browse files Browse the repository at this point in the history
Add additional fixes for rx path that should ideally reduce
memory usage.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
  • Loading branch information
Ansuel committed May 19, 2022
1 parent de3f672 commit 85b1f66
Show file tree
Hide file tree
Showing 2 changed files with 219 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
From 0e79fe8c6937e080816230892d5382af5a5a86c6 Mon Sep 17 00:00:00 2001
From: Venkateswara Naralasetty <quic_vnaralas@quicinc.com>
Date: Mon, 29 Nov 2021 11:07:53 +0530
Subject: [PATCH] ath11k: skip status ring entry processing

If STATUS_BUFFER_DONE is not set for a monitor status ring entry,
we don't process the status ring until STATUS_BUFFER_DONE set
for that status ring entry.

During LMAC reset it may happnen that HW will not write
STATUS_BUFFER_DONE tlv in status buffer, in that case we end up
waiting for STATUS_BUFFER_DONE leading to backpressure on monitor
status ring.

As per MAC team's suggestion, when HP + 1 entry is peeked and if DMA
is not done and if HP + 2 entry's DMA done is set,
replenish HP + 1 entry and start processing in next interrupt.
If HP + 2 entry's DMA done is not set,
poll onto HP + 1 entry DMA done to be set.

Also, During monitor attach HP points to the end of the ring and TP
points to the start of the ring. Using ath11k_hal_srng_src_peek()
may result in processing invali buffer for the very first interrupt.
Since, HW starts writing buffer from TP.

Hence, replaced ath11k_hal_srng_src_peek() with
ath11k_hal_srng_src_next_peek().

Signed-off-by: Venkateswara Naralasetty <quic_vnaralas@quicinc.com>
---
drivers/net/wireless/ath/ath11k/dp_rx.c | 92 ++++++++++++++++++++++++++++-----
drivers/net/wireless/ath/ath11k/hal.c | 30 +++++++++++
drivers/net/wireless/ath/ath11k/hal.h | 4 ++
3 files changed, 113 insertions(+), 13 deletions(-)

--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -3837,6 +3837,46 @@ ath11k_dp_rx_mon_update_status_buf_state
}
}

+enum dp_mon_status_buf_state
+dp_rx_mon_handle_status_buf_done(struct ath11k_base *ab, struct hal_srng *srng,
+ struct dp_rxdma_ring *rx_ring)
+{
+ void *status_desc;
+ struct sk_buff *skb;
+ struct ath11k_skb_rxcb *rxcb;
+ struct hal_tlv_hdr *tlv;
+ dma_addr_t paddr;
+ u32 cookie;
+ int buf_id;
+ u8 rbm;
+
+ status_desc = ath11k_hal_srng_src_next_peek(ab, srng);
+ if (!status_desc)
+ return DP_MON_STATUS_NO_DMA;
+
+ ath11k_hal_rx_buf_addr_info_get(status_desc, &paddr, &cookie, &rbm);
+
+ buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, cookie);
+
+ spin_lock_bh(&rx_ring->idr_lock);
+ skb = idr_find(&rx_ring->bufs_idr, buf_id);
+ spin_unlock_bh(&rx_ring->idr_lock);
+
+ if (!skb)
+ return DP_MON_STATUS_NO_DMA;
+
+ rxcb = ATH11K_SKB_RXCB(skb);
+ dma_sync_single_for_cpu(ab->dev, rxcb->paddr,
+ skb->len + skb_tailroom(skb),
+ DMA_FROM_DEVICE);
+
+ tlv = (struct hal_tlv_hdr *)skb->data;
+ if (FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl) != HAL_RX_STATUS_BUFFER_DONE)
+ return DP_MON_STATUS_NO_DMA;
+
+ return DP_MON_STATUS_REPLINISH;
+}
+
static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id,
int *budget, struct sk_buff_head *skb_list)
{
@@ -3849,6 +3889,7 @@ static int ath11k_dp_rx_reap_mon_status_
struct sk_buff *skb;
struct ath11k_skb_rxcb *rxcb;
struct hal_tlv_hdr *tlv;
+ enum dp_mon_status_buf_state reap_status;
u32 cookie;
int buf_id, srng_id;
dma_addr_t paddr;
@@ -3868,8 +3909,7 @@ static int ath11k_dp_rx_reap_mon_status_
ath11k_hal_srng_access_begin(ab, srng);
while (*budget) {
*budget -= 1;
- rx_mon_status_desc =
- ath11k_hal_srng_src_peek(ab, srng);
+ rx_mon_status_desc = ath11k_hal_srng_src_peek(ab, srng);
if (!rx_mon_status_desc) {
pmon->buf_state = DP_MON_STATUS_REPLINISH;
break;
@@ -3906,12 +3946,43 @@ static int ath11k_dp_rx_reap_mon_status_
tlv = (struct hal_tlv_hdr *)skb->data;
if (FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl) !=
HAL_RX_STATUS_BUFFER_DONE) {
- ath11k_warn(ab, "mon status DONE not set %lx\n",
- FIELD_GET(HAL_TLV_HDR_TAG,
- tlv->tl));
+
+ /* RxDMA status done bit might not be set even
+ * though tp is moved by HW.
+ */
- dev_kfree_skb_any(skb);
- pmon->buf_state = DP_MON_STATUS_NO_DMA;
- goto move_next;
+
+ /* If done status is missing:
+ * 1. As per MAC team's suggestion,
+ * when HP + 1 entry is peeked and if DMA
+ * is not done and if HP + 2 entry's DMA done
+ * is set. skip HP + 1 entry and
+ * start processing in next interrupt.
+ * 2. If HP + 2 entry's DMA done is not set,
+ * poll onto HP + 1 entry DMA done to be set.
+ * Check status for same buffer for next time
+ * dp_rx_mon_status_srng_process
+ */
+
+ reap_status = dp_rx_mon_handle_status_buf_done(ab, srng,
+ rx_ring);
+ if (reap_status == DP_MON_STATUS_NO_DMA) {
+ continue;
+ } else if (reap_status == DP_MON_STATUS_REPLINISH) {
+ ath11k_warn(ab, "mon status DONE not set %lx\n",
+ FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl));
+
+ spin_lock_bh(&rx_ring->idr_lock);
+ idr_remove(&rx_ring->bufs_idr, buf_id);
+ spin_unlock_bh(&rx_ring->idr_lock);
+
+ dma_unmap_single(ab->dev, rxcb->paddr,
+ skb->len + skb_tailroom(skb),
+ DMA_FROM_DEVICE);
+
+ dev_kfree_skb_any(skb);
+ pmon->buf_state = DP_MON_STATUS_REPLINISH;
+ goto move_next;
+ }
}

spin_lock_bh(&rx_ring->idr_lock);
--- a/drivers/net/wireless/ath/ath11k/hal.c
+++ b/drivers/net/wireless/ath/ath11k/hal.c
@@ -828,6 +828,20 @@ u32 *ath11k_hal_srng_src_get_next_reaped
return desc;
}

+u32 *ath11k_hal_srng_src_next_peek(struct ath11k_base *ab, struct hal_srng *srng)
+{
+ u32 next_hp;
+
+ lockdep_assert_held(&srng->lock);
+
+ next_hp = (srng->u.src_ring.hp + srng->entry_size) % srng->ring_size;
+
+ if (next_hp != srng->u.src_ring.cached_tp)
+ return srng->ring_base_vaddr + next_hp;
+
+ return NULL;
+}
+
u32 *ath11k_hal_srng_src_peek(struct ath11k_base *ab, struct hal_srng *srng)
{
lockdep_assert_held(&srng->lock);
--- a/drivers/net/wireless/ath/ath11k/hal.h
+++ b/drivers/net/wireless/ath/ath11k/hal.h
@@ -951,6 +951,8 @@ int ath11k_hal_srng_dst_num_valid(struct
void ath11k_hal_srng_dst_invalidate_entry(struct ath11k_base *ab,
struct hal_srng *srng, int entries);
u32 *ath11k_hal_srng_src_peek(struct ath11k_base *ab, struct hal_srng *srng);
+u32 *ath11k_hal_srng_src_next_peek(struct ath11k_base *ab,
+ struct hal_srng *srng);
u32 *ath11k_hal_srng_src_get_next_reaped(struct ath11k_base *ab,
struct hal_srng *srng);
u32 *ath11k_hal_srng_src_reap_next(struct ath11k_base *ab,
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
From a12157095a8a59eeaeb7c9efe70495288140159c Mon Sep 17 00:00:00 2001
From: Hari Chandrakanthan <quic_haric@quicinc.com>
Date: Mon, 10 Jan 2022 12:38:10 +0530
Subject: [PATCH] ath11k: provide access of the dma buffer back to dma device

In ath11k_dbring_bufs_replenish, after accessing paddr which is a member of
ath11k_dbring_element, provide the access of the buffer back to
dma device by using dma_sync_single_for_device.

Also the gfp flag to used inside ath11k_dbring_fill_bufs is changed
from GFP_KERNEL to GFP_ATOMIC as the buffers are allocated inside spin lock.

Signed-off-by: Hari Chandrakanthan <quic_haric@quicinc.com>
---
drivers/net/wireless/ath/ath11k/dbring.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath11k/dbring.c b/drivers/net/wireless/ath/ath11k/dbring.c
index 635b51e..5e8028a 100644
--- a/drivers/net/wireless/ath/ath11k/dbring.c
+++ b/drivers/net/wireless/ath/ath11k/dbring.c
@@ -90,6 +90,8 @@ int ath11k_dbring_bufs_replenish(struct ath11k *ar,

buff->paddr = paddr;

+ dma_sync_single_for_device(ab->dev, paddr, ring->buf_sz, DMA_FROM_DEVICE);
+
cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, ar->pdev_idx) |
FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id);

--
2.7.4

0 comments on commit 85b1f66

Please sign in to comment.