Browse Source

qca-wifi: Fix monitor mode ppdu id mismatch issue for qcn9000

If status ring and destination ring ppdu id mismatches,
a.	If If status ring ppdu > destination ring ppdu,
  drop destiantion ring ppdu untill it matches status ring.
a.	If If status ring ppdu < destination ring ppdu,
  drop status ring ppdu untill it matches status ring

CRs-Fixed: 2677655
Change-Id: I80638245a09c3ed5846c0e71c8ad94e1c22c9014
Amir 5 years ago
parent
commit
902435af2a
1 changed files with 326 additions and 82 deletions
  1. 326 82
      dp/wifi3.0/dp_full_mon.c

+ 326 - 82
dp/wifi3.0/dp_full_mon.c

@@ -34,6 +34,184 @@ dp_rx_mon_status_process(struct dp_soc *soc,
 			 uint32_t mac_id,
 			 uint32_t quota);
 
+/*
+ * dp_rx_mon_status_buf_validate () - Validate first monitor status buffer addr
+ * against status buf addr given in monitor destination ring
+ *
+ * @pdev: DP pdev handle
+ * @mac_id: lmac id
+ *
+ * Return: QDF_STATUS
+ */
+static inline QDF_STATUS
+dp_rx_mon_status_buf_validate(struct dp_pdev *pdev, uint32_t mac_id)
+{
+	struct dp_soc *soc = pdev->soc;
+	hal_soc_handle_t hal_soc;
+	void *mon_status_srng;
+	void *ring_entry;
+	uint32_t rx_buf_cookie;
+	qdf_nbuf_t status_nbuf;
+	struct dp_rx_desc *rx_desc;
+	uint64_t buf_paddr;
+	struct rx_desc_pool *rx_desc_pool;
+	uint32_t tlv_tag;
+	void *rx_tlv;
+	struct hal_rx_ppdu_info *ppdu_info;
+	QDF_STATUS status = QDF_STATUS_E_FAILURE;
+	QDF_STATUS buf_status;
+
+	mon_status_srng = soc->rxdma_mon_status_ring[mac_id].hal_srng;
+
+	qdf_assert(mon_status_srng);
+	if (!mon_status_srng || !hal_srng_initialized(mon_status_srng)) {
+		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
+			  "%s %d : HAL Monitor Status Ring Init Failed -- %pK",
+			  __func__, __LINE__, mon_status_srng);
+		return status;
+	}
+
+	hal_soc = soc->hal_soc;
+
+	qdf_assert(hal_soc);
+
+	if (qdf_unlikely(hal_srng_access_start(hal_soc, mon_status_srng))) {
+		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
+			  "%s %d : HAL SRNG access Failed -- %pK",
+			  __func__, __LINE__, mon_status_srng);
+		return status;
+	}
+
+	ring_entry = hal_srng_src_peek_n_get_next(hal_soc, mon_status_srng);
+	if (!ring_entry) {
+		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
+			"%s %d : HAL SRNG entry is NULL srng:-- %pK",
+			__func__, __LINE__, mon_status_srng);
+		goto done;
+	}
+
+	ppdu_info = &pdev->ppdu_info;
+
+	rx_desc_pool = &soc->rx_desc_status[mac_id];
+
+	buf_paddr = (HAL_RX_BUFFER_ADDR_31_0_GET(ring_entry) |
+		     ((uint64_t)(HAL_RX_BUFFER_ADDR_39_32_GET(ring_entry)) << 32));
+
+	if (!buf_paddr) {
+		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
+			  "%s %d : buf addr is NULL -- %pK",
+			  __func__, __LINE__, mon_status_srng);
+		goto done;
+	}
+
+	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,
+	 * hold on to mon destination ring.
+	 */
+	if (buf_status != QDF_STATUS_SUCCESS) {
+		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
+			  FL("Monitor status ring: DMA is not done "
+			     "for nbuf: %pK buf_addr: %llx"),
+			     status_nbuf, buf_paddr);
+		pdev->rx_mon_stats.tlv_tag_status_err++;
+		pdev->hold_mon_dest_ring = true;
+		goto done;
+	}
+
+	qdf_nbuf_unmap_nbytes_single(soc->osdev, status_nbuf,
+			QDF_DMA_FROM_DEVICE,
+			rx_desc_pool->buf_size);
+
+	rx_tlv = hal_rx_status_get_next_tlv(rx_tlv);
+
+	tlv_tag = HAL_RX_GET_USER_TLV32_TYPE(rx_tlv);
+
+	if (tlv_tag == WIFIRX_PPDU_START_E) {
+		rx_tlv = (uint8_t *)rx_tlv + HAL_RX_TLV32_HDR_SIZE;
+		ppdu_info->com_info.ppdu_id = HAL_RX_GET(rx_tlv,
+							 RX_PPDU_START_0,
+							 PHY_PPDU_ID);
+		pdev->status_buf_addr = buf_paddr;
+	}
+
+	/* If Monitor destination ring is on hold and ppdu id matches,
+	 * deliver PPDU data which was on hold.
+	 */
+	if (pdev->hold_mon_dest_ring &&
+	    (pdev->mon_desc->ppdu_id == pdev->ppdu_info.com_info.ppdu_id)) {
+		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
+			  FL("Monitor destination was on Hold "
+			     "PPDU id matched"));
+
+		pdev->hold_mon_dest_ring = false;
+		goto done;
+	}
+
+	if ((pdev->mon_desc->status_buf.paddr != buf_paddr) ||
+	     (pdev->mon_desc->ppdu_id != pdev->ppdu_info.com_info.ppdu_id)) {
+		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
+			  FL("Monitor: PPDU id or status buf_addr mismatch "
+			     "status_ppdu_id: %d dest_ppdu_id: %d "
+			     "status_addr: %llx status_buf_cookie: %d "
+			     "dest_addr: %llx tlv_tag: %d"
+			     " status_nbuf: %pK"),
+			      pdev->ppdu_info.com_info.ppdu_id,
+			      pdev->mon_desc->ppdu_id, pdev->status_buf_addr,
+			      rx_buf_cookie,
+			      pdev->mon_desc->status_buf.paddr, tlv_tag,
+			      status_nbuf);
+	}
+
+	/* Monitor Status ring is reaped in two cases:
+	 * a. If first status buffer's buf_addr_info matches
+	 * with latched status buffer addr info in monitor
+	 * destination ring.
+	 * b. If monitor status ring is lagging behind
+	 * monitor destination ring. Hold on to monitor
+	 * destination ring in this case until status ring
+	 * and destination ring ppdu id matches.
+	 */
+	if ((pdev->mon_desc->status_buf.paddr == buf_paddr) ||
+	    (pdev->mon_desc->ppdu_id > pdev->ppdu_info.com_info.ppdu_id)) {
+		if (pdev->mon_desc->ppdu_id >
+		    pdev->ppdu_info.com_info.ppdu_id) {
+
+			QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
+				  FL("Monitor status ring is lagging behind "
+				     "monitor destination ring "
+				     "status_ppdu_id: %d dest_ppdu_id: %d "
+				     "status_nbuf: %pK tlv_tag: %d "
+				     "status_addr: %llx dest_addr: %llx "),
+				     ppdu_info->com_info.ppdu_id,
+				     pdev->mon_desc->ppdu_id,
+				     status_nbuf, tlv_tag,
+				     pdev->status_buf_addr,
+				     pdev->mon_desc->status_buf.paddr);
+
+			pdev->rx_mon_stats.ppdu_id_mismatch++;
+			pdev->rx_mon_stats.status_ppdu_drop++;
+			pdev->hold_mon_dest_ring = true;
+		}
+		status = QDF_STATUS_SUCCESS;
+	}
+done:
+	hal_srng_access_end(hal_soc, mon_status_srng);
+	return status;
+}
+
 /*
  * dp_rx_mon_prepare_mon_mpdu () - API to prepare dp_mon_mpdu object
  *
@@ -52,7 +230,7 @@ dp_rx_mon_prepare_mon_mpdu(struct dp_pdev *pdev,
 	mon_mpdu = qdf_mem_malloc(sizeof(struct dp_mon_mpdu));
 
 	if (!mon_mpdu) {
-		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
 			  FL("Monitor MPDU object allocation failed -- %pK"),
 			     pdev);
 		qdf_assert_always(0);
@@ -69,6 +247,40 @@ dp_rx_mon_prepare_mon_mpdu(struct dp_pdev *pdev,
 	return mon_mpdu;
 }
 
+static inline void
+dp_rx_mon_drop_ppdu(struct dp_pdev *pdev, uint32_t mac_id)
+{
+	struct dp_mon_mpdu *mpdu = NULL;
+	struct dp_mon_mpdu *temp_mpdu = NULL;
+	qdf_nbuf_t mon_skb, skb_next;
+
+	if (!TAILQ_EMPTY(&pdev->mon_mpdu_q)) {
+		TAILQ_FOREACH_SAFE(mpdu,
+				   &pdev->mon_mpdu_q,
+				   mpdu_list_elem,
+				   temp_mpdu) {
+				   TAILQ_REMOVE(&pdev->mon_mpdu_q,
+						mpdu, mpdu_list_elem);
+
+			mon_skb = mpdu->head;
+			while (mon_skb) {
+				skb_next = qdf_nbuf_next(mon_skb);
+
+				QDF_TRACE(QDF_MODULE_ID_DP,
+					  QDF_TRACE_LEVEL_DEBUG,
+					  "[%s][%d] mon_skb=%pK len %u"
+					  " __func__, __LINE__",
+					  mon_skb, mon_skb->len);
+
+				qdf_nbuf_free(mon_skb);
+				mon_skb = skb_next;
+			}
+
+			qdf_mem_free(mpdu);
+		}
+	}
+}
+
 /*
  * dp_rx_monitor_deliver_ppdu () - API to deliver all MPDU for a MPDU
  * to upper layer stack
@@ -128,21 +340,39 @@ dp_rx_mon_reap_status_ring(struct dp_soc *soc,
 	uint32_t work_done = 0;
 
 	status_buf_count = desc_info->status_buf_count;
+	desc_info->drop_ppdu = false;
 
-status_reap:
-	work_done += dp_rx_mon_status_process(soc, mac_id, status_buf_count);
+	if (dp_rx_mon_status_buf_validate(pdev, mac_id) == QDF_STATUS_SUCCESS)
+		work_done = dp_rx_mon_status_process(soc,
+						     mac_id,
+						     status_buf_count);
 
 	if (desc_info->ppdu_id != pdev->ppdu_info.com_info.ppdu_id) {
-		pdev->rx_mon_stats.ppdu_id_mismatch++;
-
-		qdf_err("count: %d quota: %d work_done: %d status_ppdu_id: %d"
-			 "dest_ppdu_id: %d ", status_buf_count, quota,
-					      work_done,
-					      pdev->ppdu_info.com_info.ppdu_id,
-					      desc_info->ppdu_id);
+		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
+			  FL("Monitor: PPDU id mismatch "
+			     "status_ppdu_id: %d dest_ppdu_id: %d "
+			     "status_addr: %llx dest_addr: %llx "
+			     "count: %d quota: %d work_done: %d "),
+			      pdev->ppdu_info.com_info.ppdu_id,
+			      pdev->mon_desc->ppdu_id, pdev->status_buf_addr,
+			      pdev->mon_desc->status_buf.paddr,
+			       status_buf_count, quota, work_done);
+	}
 
-		if (desc_info->ppdu_id > pdev->ppdu_info.com_info.ppdu_id)
-			goto status_reap;
+	if (desc_info->ppdu_id < pdev->ppdu_info.com_info.ppdu_id) {
+		pdev->rx_mon_stats.ppdu_id_mismatch++;
+		desc_info->drop_ppdu = true;
+		pdev->rx_mon_stats.dest_ppdu_drop++;
+
+		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
+			  FL("Monitor destination ring is lagging behind "
+			     "monitor status ring "
+			     "status_ppdu_id: %d dest_ppdu_id: %d "
+			     "status_addr: %llx dest_addr: %llx "),
+			     pdev->ppdu_info.com_info.ppdu_id,
+			     pdev->mon_desc->ppdu_id,
+			     pdev->status_buf_addr,
+			     pdev->mon_desc->status_buf.paddr);
 	}
 
 	return work_done;
@@ -347,6 +577,63 @@ next_msdu:
 	return rx_buf_reaped;
 }
 
+/*
+ * dp_rx_mon_deliver_prev_ppdu () - Deliver previous PPDU
+ *
+ * @pdev: DP pdev handle
+ * @mac_id: lmac id
+ * @quota: quota
+ *
+ * Return: remaining qouta
+ */
+static inline uint32_t
+dp_rx_mon_deliver_prev_ppdu(struct dp_pdev *pdev,
+			    uint32_t mac_id,
+			    uint32_t quota)
+{
+
+	struct dp_soc *soc = pdev->soc;
+	struct hal_rx_mon_desc_info *desc_info = pdev->mon_desc;
+	uint32_t work_done = 0;
+	bool hold_mon_dest_ring = false;
+
+	while (pdev->hold_mon_dest_ring) {
+		if (dp_rx_mon_status_buf_validate(pdev, mac_id) == QDF_STATUS_SUCCESS) {
+			work_done = dp_rx_mon_status_process(soc, mac_id, 1);
+		}
+		quota -= work_done;
+
+		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
+			  FL("Hold on Monitor destination ring "
+			      "work_done: %d quota: %d status_ppdu_id: %d "
+			      "dest_ppdu_id: %d s_addr: %llx d_addr: %llx "),
+			      work_done, quota,
+			      pdev->ppdu_info.com_info.ppdu_id,
+			      desc_info->ppdu_id, pdev->status_buf_addr,
+			      desc_info->status_buf.paddr);
+
+		hold_mon_dest_ring = true;
+
+		if (!quota)
+			return quota;
+	}
+	if (hold_mon_dest_ring) {
+		if (quota >= desc_info->status_buf_count) {
+			qdf_err("DEBUG:");
+			work_done = dp_rx_mon_status_process(soc, mac_id,
+							     desc_info->status_buf_count);
+			dp_rx_monitor_deliver_ppdu(soc, mac_id);
+			hold_mon_dest_ring = false;
+		} else {
+			pdev->hold_mon_dest_ring = true;
+			return quota;
+		}
+		quota -= work_done;
+	}
+
+	return quota;
+}
+
 /**
  * dp_rx_mon_process () - Core brain processing for monitor mode
  *
@@ -375,30 +662,42 @@ uint32_t dp_rx_mon_process(struct dp_soc *soc, uint32_t mac_id, uint32_t quota)
 	struct hal_rx_mon_desc_info *desc_info;
 	int mac_for_pdev = mac_id;
 	QDF_STATUS status;
+	uint32_t work_done = 0;
+
+	qdf_spin_lock_bh(&pdev->mon_lock);
+	if (qdf_unlikely(!dp_soc_is_full_mon_enable(pdev))) {
+		quota -= dp_rx_mon_status_process(soc, mac_id, quota);
+		qdf_spin_unlock_bh(&pdev->mon_lock);
+		return quota;
+	}
+
+	desc_info = pdev->mon_desc;
+
+	quota = dp_rx_mon_deliver_prev_ppdu(pdev, mac_id, quota);
 
-	if (qdf_unlikely(!dp_soc_is_full_mon_enable(pdev)))
-		return dp_rx_mon_status_process(soc, mac_id, quota);
+	/* Do not proceed if quota expires */
+	if (!quota || pdev->hold_mon_dest_ring) {
+		qdf_spin_unlock_bh(&pdev->mon_lock);
+		return quota;
+	}
 
 	mon_dest_srng = dp_rxdma_get_mon_dst_ring(pdev, mac_for_pdev);
 
 	if (qdf_unlikely(!mon_dest_srng ||
 			 !hal_srng_initialized(mon_dest_srng))) {
-		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
 			  FL("HAL Monitor Destination Ring Init Failed -- %pK"),
 			  mon_dest_srng);
-		goto done;
+		goto done1;
 	}
 
 	hal_soc = soc->hal_soc;
 
 	qdf_assert_always(hal_soc && pdev);
 
-	qdf_spin_lock_bh(&pdev->mon_lock);
-
-	desc_info = pdev->mon_desc;
 
 	if (qdf_unlikely(hal_srng_access_start(hal_soc, mon_dest_srng))) {
-		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
 			  FL("HAL Monitor Destination Ring access Failed -- %pK"),
 			  mon_dest_srng);
 		goto done1;
@@ -473,7 +772,7 @@ uint32_t dp_rx_mon_process(struct dp_soc *soc, uint32_t mac_id, uint32_t quota)
 							      head_msdu,
 							      tail_msdu);
 
-			QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+			QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
 				  FL("Dest_srng: %pK MPDU_OBJ: %pK "
 				  "head_msdu: %pK tail_msdu: %pK -- "),
 				  mon_dest_srng,
@@ -501,70 +800,16 @@ uint32_t dp_rx_mon_process(struct dp_soc *soc, uint32_t mac_id, uint32_t quota)
 		 */
 		rx_mon_stats->dest_ppdu_done++;
 
-#if 0
-		if (pdev->ppdu_info.com_info.ppdu_id !=
-		    pdev->mon_desc->ppdu_id) {
-			pdev->rx_mon_stats.ppdu_id_mismatch++;
-
-			qdf_err("PPDU id mismatch, status_ppdu_id: %d"
-				"dest_ppdu_id: %d status_ppdu_done: %d "
-				"dest_ppdu_done: %d ppdu_id_mismatch_cnt: %u"
-				"dest_mpdu_drop: %u",
-				pdev->ppdu_info.com_info.ppdu_id,
-				pdev->mon_desc->ppdu_id,
-				pdev->rx_mon_stats.status_ppdu_done,
-				pdev->rx_mon_stats.dest_ppdu_done,
-				pdev->rx_mon_stats.ppdu_id_mismatch,
-				pdev->rx_mon_stats.dest_mpdu_drop);
-
-		/* WAR: It is observed that in some cases, status ring ppdu_id
-		 *     and destination ring ppdu_id doesn't match.
-		 *     Following WAR is added to fix it.
-		 *     a. If status ppdu_id is less than destination ppdu_id,
-		 *        hold onto destination ring until ppdu_id matches
-		 *     b. If status ppdu_id is greater than destination ring
-		 *        ppdu_Id, move tp in destination ring.
-		 */
-			if (pdev->ppdu_info.com_info.ppdu_id <
-			    pdev->mon_desc->ppdu_id) {
-				break;
-			} else {
-				ring_desc = hal_srng_dst_get_next(hal_soc,
-								  mon_dest_srng);
-				continue;
-			}
-		}
-
-		/*
-		 * At this point, end_of_ppdu is one here,
-		 * When 'end_of_ppdu' is one, status buffer_count and
-		 * status_buf_addr must be valid.
-		 *
-		 *  Assert if
-		 *  a. status_buf_count is zero
-		 *  b. status_buf.paddr is NULL
-		 */
-		if (!pdev->mon_desc->status_buf_count ||
-		    !pdev->mon_desc->status_buf.paddr) {
-			qdf_assert_always(0);
-			QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-				  FL("Status buffer info is NULL"
-				  "status_buf_count: %d"
-				  "status_buf_addr: %pK"
-				  "ring_desc: %pK-- "),
-				  pdev->mon_desc->status_buf_count,
-				  pdev->mon_desc->status_buf.paddr,
-				  ring_desc);
-				goto done2;
-		}
-#endif
-
-		quota -= dp_rx_mon_reap_status_ring(soc, mac_id,
+		work_done = dp_rx_mon_reap_status_ring(soc, mac_id,
 							quota, desc_info);
 		/* Deliver all MPDUs for a PPDU */
-		dp_rx_monitor_deliver_ppdu(soc, mac_id);
+		if (desc_info->drop_ppdu)
+			dp_rx_mon_drop_ppdu(pdev, mac_id);
+		else if (!pdev->hold_mon_dest_ring)
+			dp_rx_monitor_deliver_ppdu(soc, mac_id);
 
 		hal_srng_dst_get_next(hal_soc, mon_dest_srng);
+		quota -= work_done;
 		break;
 	}
 
@@ -573,7 +818,6 @@ uint32_t dp_rx_mon_process(struct dp_soc *soc, uint32_t mac_id, uint32_t quota)
 done1:
 	qdf_spin_unlock_bh(&pdev->mon_lock);
 
-done:
 	return quota;
 }