diff --git a/dp/inc/cdp_txrx_mon_struct.h b/dp/inc/cdp_txrx_mon_struct.h index 72d340c9e3..48f69086a7 100644 --- a/dp/inc/cdp_txrx_mon_struct.h +++ b/dp/inc/cdp_txrx_mon_struct.h @@ -219,6 +219,8 @@ enum { * @dup_mon_linkdesc_cnt: duplicate link descriptor indications from HW * @dup_mon_buf_cnt: duplicate buffer indications from HW * @tlv_tag_status_err: status not correct in the tlv tag + * @status_buf_done_war: Number of status ring buffers for which DMA not done + * WAR is applied. * @mon_rx_bufs_replenished_dest: Rx buffers replenish count * @mon_rx_bufs_reaped_dest: Rx buffer reap count * @ppdu_id_mismatch: counter to track ppdu id mismatch in @@ -248,6 +250,7 @@ struct cdp_pdev_mon_stats { uint32_t ppdu_id_hist_idx; uint32_t mon_rx_dest_stuck; uint32_t tlv_tag_status_err; + uint32_t status_buf_done_war; uint32_t mon_rx_bufs_replenished_dest; uint32_t mon_rx_bufs_reaped_dest; uint32_t ppdu_id_mismatch; diff --git a/dp/wifi3.0/dp_rx_mon.h b/dp/wifi3.0/dp_rx_mon.h index ef5574515b..ff19b224ac 100644 --- a/dp/wifi3.0/dp_rx_mon.h +++ b/dp/wifi3.0/dp_rx_mon.h @@ -46,6 +46,23 @@ #define DP_RX_MON_RAW_L2_HDR_PAD_BYTE (0) #define DP_RX_MON_NONRAW_L2_HDR_PAD_BYTE (2) +/** + * enum dp_mon_reap_status - monitor status ring ppdu status + * + * @DP_MON_STATUS_NO_DMA - DMA not done for status ring entry + * @DP_MON_STATUS_MATCH - status and dest ppdu id mathes + * @DP_MON_STATUS_LAG - status ppdu id is lagging + * @DP_MON_STATUS_LEAD - status ppdu id is leading + * @DP_MON_STATUS_REPLENISH - status ring entry is NULL + */ +enum dp_mon_reap_status { + DP_MON_STATUS_NO_DMA, + DP_MON_STATUS_MATCH, + DP_MON_STATUS_LAG, + DP_MON_STATUS_LEAD, + DP_MON_STATUS_REPLENISH +}; + /* * dp_rx_mon_status_process() - Process monitor status ring and * TLV in status ring. @@ -100,6 +117,19 @@ QDF_STATUS dp_rx_pdev_mon_buf_buffers_alloc(struct dp_pdev *pdev, uint32_t mac_id, bool delayed_replenish); +/** + * dp_rx_mon_handle_status_buf_done () - Handle DMA not done case for + * monitor status ring + * + * @pdev: DP pdev handle + * @mon_status_srng: Monitor status SRNG + * + * Return: enum dp_mon_reap_status + */ +enum dp_mon_reap_status +dp_rx_mon_handle_status_buf_done(struct dp_pdev *pdev, + void *mon_status_srng); + #ifdef QCA_SUPPORT_FULL_MON /** diff --git a/dp/wifi3.0/dp_rx_mon_status.c b/dp/wifi3.0/dp_rx_mon_status.c index d1feea3728..34e184e21c 100644 --- a/dp/wifi3.0/dp_rx_mon_status.c +++ b/dp/wifi3.0/dp_rx_mon_status.c @@ -49,6 +49,74 @@ dp_rx_populate_cfr_non_assoc_sta(struct dp_pdev *pdev, struct hal_rx_ppdu_info *ppdu_info, struct cdp_rx_indication_ppdu *cdp_rx_ppdu); +/** + * dp_rx_mon_handle_status_buf_done () - Handle status buf DMA not done + * + * @pdev: DP pdev handle + * @mon_status_srng: Monitor status SRNG + * + * As per MAC team's suggestion, If HP + 2 entry's DMA done is set, + * skip HP + 1 entry and start processing in next interrupt. + * If HP + 2 entry's DMA done is not set, poll onto HP + 1 entry + * for it's DMA done TLV to be set. + * + * Return: enum dp_mon_reap_status + */ +enum dp_mon_reap_status +dp_rx_mon_handle_status_buf_done(struct dp_pdev *pdev, + void *mon_status_srng) +{ + struct dp_soc *soc = pdev->soc; + hal_soc_handle_t hal_soc; + void *ring_entry; + uint32_t rx_buf_cookie; + qdf_nbuf_t status_nbuf; + struct dp_rx_desc *rx_desc; + void *rx_tlv; + QDF_STATUS buf_status; + + hal_soc = soc->hal_soc; + + ring_entry = hal_srng_src_peek_n_get_next_next(hal_soc, + mon_status_srng); + if (!ring_entry) { + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, + FL("Monitor status ring entry is NULL " + "for SRNG: %pK"), + mon_status_srng); + return DP_MON_STATUS_NO_DMA; + } + rx_buf_cookie = HAL_RX_BUF_COOKIE_GET(ring_entry); + rx_desc = dp_rx_cookie_2_va_mon_status(soc, rx_buf_cookie); + + qdf_assert(rx_desc); + + status_nbuf = rx_desc->nbuf; + + qdf_nbuf_sync_for_cpu(soc->osdev, status_nbuf, + QDF_DMA_FROM_DEVICE); + + rx_tlv = qdf_nbuf_data(status_nbuf); + buf_status = hal_get_rx_status_done(rx_tlv); + + /* If status buffer DMA is not done, + * 1. As per MAC team's suggestion, If HP + 2 entry's DMA done is set, + * replenish HP + 1 entry and start processing in next interrupt. + * 2. If HP + 2 entry's DMA done is not set + * hold on to mon destination ring. + */ + if (buf_status != QDF_STATUS_SUCCESS) { + dp_err("Monitor status ring: DMA is not done " + "for nbuf: %pK", status_nbuf); + pdev->rx_mon_stats.tlv_tag_status_err++; + return DP_MON_STATUS_NO_DMA; + } + + pdev->rx_mon_stats.status_buf_done_war++; + + return DP_MON_STATUS_REPLENISH; +} + #ifndef QCA_SUPPORT_FULL_MON /** * dp_rx_mon_process () - Core brain processing for monitor mode @@ -1826,6 +1894,7 @@ dp_rx_mon_status_srng_process(struct dp_soc *soc, struct dp_intr *int_ctx, void *mon_status_srng; void *rxdma_mon_status_ring_entry; QDF_STATUS status; + enum dp_mon_reap_status reap_status; uint32_t work_done = 0; if (!pdev) { @@ -1898,23 +1967,30 @@ dp_rx_mon_status_srng_process(struct dp_soc *soc, struct dp_intr *int_ctx, &tp, &hp); dp_info_rl("tlv tag status error hp:%u, tp:%u", hp, tp); - pdev->rx_mon_stats.tlv_tag_status_err++; /* RxDMA status done bit might not be set even * though tp is moved by HW. - * So Hold on to current entry on - * monitor status ring */ - /* 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 - * dp_rx_mon_status_srng_process + /* 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 */ - break; + reap_status = dp_rx_mon_handle_status_buf_done(pdev, + mon_status_srng); + if (reap_status == DP_MON_STATUS_NO_DMA) + continue; + else if (reap_status == DP_MON_STATUS_REPLENISH) { + qdf_nbuf_free(status_nbuf); + goto buf_replenish; + } } qdf_nbuf_set_pktlen(status_nbuf, RX_MON_STATUS_BUF_SIZE); @@ -1947,6 +2023,7 @@ dp_rx_mon_status_srng_process(struct dp_soc *soc, struct dp_intr *int_ctx, rx_desc = &desc_list->rx_desc; } +buf_replenish: status_nbuf = dp_rx_nbuf_prepare(soc, pdev); /* diff --git a/dp/wifi3.0/dp_stats.c b/dp/wifi3.0/dp_stats.c index 18472349fa..98796d49d7 100644 --- a/dp/wifi3.0/dp_stats.c +++ b/dp/wifi3.0/dp_stats.c @@ -6350,6 +6350,8 @@ dp_print_pdev_rx_mon_stats(struct dp_pdev *pdev) rx_mon_stats->dest_mpdu_done); DP_PRINT_STATS("tlv_tag_status_err_cnt = %u", rx_mon_stats->tlv_tag_status_err); + DP_PRINT_STATS("mon status DMA not done WAR count= %u", + rx_mon_stats->status_buf_done_war); DP_PRINT_STATS("dest_mpdu_drop_cnt = %d", rx_mon_stats->dest_mpdu_drop); DP_PRINT_STATS("dup_mon_linkdesc_cnt = %d", diff --git a/hal/wifi3.0/hal_api.h b/hal/wifi3.0/hal_api.h index f01c8f8343..0f1167b941 100644 --- a/hal/wifi3.0/hal_api.h +++ b/hal/wifi3.0/hal_api.h @@ -1549,6 +1549,41 @@ void *hal_srng_src_peek_n_get_next(hal_soc_handle_t hal_soc_hdl, return NULL; } +/** + * hal_srng_src_peek_n_get_next_next - Get next to next, i.e HP + 2 entry + * from a ring without moving head pointer. + * + * @hal_soc: Opaque HAL SOC handle + * @hal_ring_hdl: Source ring pointer + * + * Return: Opaque pointer for next to next ring entry; NULL on failire + */ +static inline +void *hal_srng_src_peek_n_get_next_next(hal_soc_handle_t hal_soc_hdl, + hal_ring_handle_t hal_ring_hdl) +{ + struct hal_srng *srng = (struct hal_srng *)hal_ring_hdl; + uint32_t *desc; + + /* TODO: Using % is expensive, but we have to do this since + * size of some SRNG rings is not power of 2 (due to descriptor + * sizes). Need to create separate API for rings used + * per-packet, with sizes power of 2 (TCL2SW, REO2SW, + * SW2RXDMA and CE rings) + */ + if ((((srng->u.src_ring.hp + (srng->entry_size)) % + srng->ring_size) != srng->u.src_ring.cached_tp) && + (((srng->u.src_ring.hp + (srng->entry_size * 2)) % + srng->ring_size) != srng->u.src_ring.cached_tp)) { + desc = &(srng->ring_base_vaddr[(srng->u.src_ring.hp + + (srng->entry_size * 2)) % + srng->ring_size]); + return (void *)desc; + } + + return NULL; +} + /** * hal_srng_src_get_cur_hp_n_move_next () - API returns current hp * and move hp to next in src ring