瀏覽代碼

qcacmn: Add WAR to skip status ring entry

STATUS_BUFFER_DONE tlv written in first word for a status
buffer indicates that DMA is done for that status ring entry.

In existing implementation, for a status ring entry if
STATUS_BUFFER_DONE tlv is not written by HW, we poll on to status ring
entry until DMA is done by HW.

During lmac reset it may happnen that HW will not write STATUS_BUFFER_DONE
tlv in status buffer, in that case we end up polling infinitely 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.

CRs-Fixed: 2740988
Change-Id: Ieef667f0bb4a47e74fc320c93243c637409f47f0
Amir 5 年之前
父節點
當前提交
376724d4f9
共有 5 個文件被更改,包括 158 次插入11 次删除
  1. 3 0
      dp/inc/cdp_txrx_mon_struct.h
  2. 30 0
      dp/wifi3.0/dp_rx_mon.h
  3. 88 11
      dp/wifi3.0/dp_rx_mon_status.c
  4. 2 0
      dp/wifi3.0/dp_stats.c
  5. 35 0
      hal/wifi3.0/hal_api.h

+ 3 - 0
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;

+ 30 - 0
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
 
 /**

+ 88 - 11
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);
 
 		/*

+ 2 - 0
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",

+ 35 - 0
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