Skip to content

Commit

Permalink
wifi: ath11k: skip status ring entry processing
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Venkateswara Naralasetty authored and intel-lab-lkp committed Mar 28, 2023
1 parent bea0465 commit c6e8f81
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 13 deletions.
91 changes: 78 additions & 13 deletions drivers/net/wireless/ath/ath11k/dp_rx.c
Expand Up @@ -2960,6 +2960,46 @@ ath11k_dp_rx_mon_update_status_buf_state(struct ath11k_mon_data *pmon,
}
}

enum dp_mon_status_buf_state
ath11k_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)
{
Expand All @@ -2973,6 +3013,7 @@ static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id,
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;
Expand All @@ -2992,8 +3033,7 @@ static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id,
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;
Expand Down Expand Up @@ -3024,18 +3064,43 @@ static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id,
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, 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_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, buf_id %d\n",
FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl),
buf_id);

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);
Expand Down
14 changes: 14 additions & 0 deletions drivers/net/wireless/ath/ath11k/hal.c
Expand Up @@ -783,6 +783,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);
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/wireless/ath/ath11k/hal.h
Expand Up @@ -946,6 +946,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,
Expand Down

0 comments on commit c6e8f81

Please sign in to comment.