]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
wifi: ath11k: skip status ring entry processing
authorVenkateswara Naralasetty <quic_vnaralas@quicinc.com>
Fri, 3 May 2024 13:24:16 +0000 (16:24 +0300)
committerKalle Valo <quic_kvalo@quicinc.com>
Tue, 7 May 2024 10:00:01 +0000 (13:00 +0300)
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 happen that hardware 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.

To fix the issue, when HP (Head Pointer) + 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 (Tail
Pointer) points to the start of the ring.  Using ath11k_hal_srng_src_peek() may
result in processing invalid buffer for the very first interrupt. Since, HW
starts writing buffer from TP.

To avoid this issue call ath11k_hal_srng_src_next_peek() instead of
calling ath11k_hal_srng_src_peek().

Tested-on: IPQ5018 hw1.0 AHB WLAN.HK.2.6.0.1-00861-QCAHKSWPL_SILICONZ-1

Signed-off-by: Venkateswara Naralasetty <quic_vnaralas@quicinc.com>
Co-developed-by: Tamizh Chelvam Raja <quic_tamizhr@quicinc.com>
Signed-off-by: Tamizh Chelvam Raja <quic_tamizhr@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://msgid.link/20240429073624.736147-1-quic_tamizhr@quicinc.com
drivers/net/wireless/ath/ath11k/dp_rx.c
drivers/net/wireless/ath/ath11k/hal.c
drivers/net/wireless/ath/ath11k/hal.h

index afd481f5858f031b6cba2d417dfa6c38775ce2e9..8a860a17d7e48e65658633c16eecd175d9e4e5d0 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/ieee80211.h>
@@ -2990,11 +2990,52 @@ ath11k_dp_rx_mon_update_status_buf_state(struct ath11k_mon_data *pmon,
        }
 }
 
+static enum dp_mon_status_buf_state
+ath11k_dp_rx_mon_buf_done(struct ath11k_base *ab, struct hal_srng *srng,
+                         struct dp_rxdma_ring *rx_ring)
+{
+       struct ath11k_skb_rxcb *rxcb;
+       struct hal_tlv_hdr *tlv;
+       struct sk_buff *skb;
+       void *status_desc;
+       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)
 {
        struct ath11k *ar;
        const struct ath11k_hw_hal_params *hal_params;
+       enum dp_mon_status_buf_state reap_status;
        struct ath11k_pdev_dp *dp;
        struct dp_rxdma_ring *rx_ring;
        struct ath11k_mon_data *pmon;
@@ -3057,15 +3098,38 @@ static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id,
                                ath11k_warn(ab, "mon status DONE not set %lx, buf_id %d\n",
                                            FIELD_GET(HAL_TLV_HDR_TAG,
                                                      tlv->tl), buf_id);
-                               /* If done status is missing, hold onto status
-                                * ring until status is done for this status
-                                * ring buffer.
-                                * Keep HP in mon_status_ring unchanged,
-                                * and break from here.
-                                * Check status for same buffer for next time
+                               /* RxDMA status done bit might not be set even
+                                * though tp is moved by HW.
                                 */
-                               pmon->buf_state = DP_MON_STATUS_NO_DMA;
-                               break;
+
+                               /* 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 = ath11k_dp_rx_mon_buf_done(ab, srng,
+                                                                       rx_ring);
+                               if (reap_status == DP_MON_STATUS_NO_DMA)
+                                       continue;
+
+                               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);
index f3d04568c221b6355b0da2f23bb850c9a01732bf..f02599bd1c36b15b03d2eb488e861e881c2ef49f 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 #include <linux/dma-mapping.h>
 #include "hal_tx.h"
@@ -796,6 +796,20 @@ u32 *ath11k_hal_srng_src_get_next_reaped(struct ath11k_base *ab,
        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);
index e453c137385e5944d7ce88ef77a6d639aa6dbfc7..dc8bbe0730170edfc7542fafa24185de03c22816 100644 (file)
@@ -947,6 +947,8 @@ u32 *ath11k_hal_srng_dst_peek(struct ath11k_base *ab, struct hal_srng *srng);
 int ath11k_hal_srng_dst_num_free(struct ath11k_base *ab, struct hal_srng *srng,
                                 bool sync_hw_ptr);
 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,