Sfoglia il codice sorgente

qcacmn: Add WAR for fragmented TKIP MIC error

HW wrongly calculates MIC for the last fragment of TKIP
encrypted packets due to which this packet ends up on WBM
execption ring. Add code to handle the last fragment
in WBM execption ring.

Change-Id: I2dea5b3fbcb48036435e26f32f4641e7a0840bcd
CRs-Fixed: 2214749
Pramod Simha 6 anni fa
parent
commit
366c1e01e6
3 ha cambiato i file con 136 aggiunte e 20 eliminazioni
  1. 111 19
      dp/wifi3.0/dp_rx_defrag.c
  2. 4 0
      dp/wifi3.0/dp_rx_defrag.h
  3. 21 1
      dp/wifi3.0/dp_rx_err.c

+ 111 - 19
dp/wifi3.0/dp_rx_defrag.c

@@ -83,6 +83,29 @@ static void dp_rx_clear_saved_desc_info(struct dp_peer *peer, unsigned tid)
 	peer->rx_tid[tid].dst_ring_desc = NULL;
 }
 
+static void dp_rx_return_head_frag_desc(struct dp_peer *peer,
+					unsigned int tid)
+{
+	struct dp_soc *soc;
+	struct dp_pdev *pdev;
+	struct dp_srng *dp_rxdma_srng;
+	struct rx_desc_pool *rx_desc_pool;
+	union dp_rx_desc_list_elem_t *head = NULL;
+	union dp_rx_desc_list_elem_t *tail = NULL;
+
+	if (peer->rx_tid[tid].head_frag_desc) {
+		pdev = peer->vdev->pdev;
+		soc = pdev->soc;
+		dp_rxdma_srng = &pdev->rx_refill_buf_ring;
+		rx_desc_pool = &soc->rx_desc_buf[0];
+
+		dp_rx_add_to_free_desc_list(&head, &tail,
+					    peer->rx_tid[tid].head_frag_desc);
+		dp_rx_buffers_replenish(soc, 0, dp_rxdma_srng, rx_desc_pool,
+					1, &head, &tail, HAL_RX_BUF_RBM_SW3_BM);
+	}
+}
+
 /*
  * dp_rx_reorder_flush_frag(): Flush the frag list
  * @peer: Pointer to the peer data structure
@@ -96,11 +119,6 @@ void dp_rx_reorder_flush_frag(struct dp_peer *peer,
 			 unsigned int tid)
 {
 	struct dp_soc *soc;
-	struct dp_srng *dp_rxdma_srng;
-	struct rx_desc_pool *rx_desc_pool;
-	struct dp_pdev *pdev;
-	union dp_rx_desc_list_elem_t *head = NULL;
-	union dp_rx_desc_list_elem_t *tail = NULL;
 
 	QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
 				FL("Flushing TID %d"), tid);
@@ -111,8 +129,7 @@ void dp_rx_reorder_flush_frag(struct dp_peer *peer,
 		return;
 	}
 
-	pdev = peer->vdev->pdev;
-	soc = pdev->soc;
+	soc = peer->vdev->pdev->soc;
 
 	if (peer->rx_tid[tid].dst_ring_desc) {
 		if (dp_rx_link_desc_return(soc,
@@ -124,16 +141,7 @@ void dp_rx_reorder_flush_frag(struct dp_peer *peer,
 					__func__);
 	}
 
-	if (peer->rx_tid[tid].head_frag_desc) {
-		dp_rxdma_srng = &pdev->rx_refill_buf_ring;
-		rx_desc_pool = &soc->rx_desc_buf[0];
-
-		dp_rx_add_to_free_desc_list(&head, &tail,
-				peer->rx_tid[tid].head_frag_desc);
-		dp_rx_buffers_replenish(soc, 0, dp_rxdma_srng, rx_desc_pool,
-			1, &head, &tail);
-	}
-
+	dp_rx_return_head_frag_desc(peer, tid);
 	dp_rx_defrag_cleanup(peer, tid);
 }
 
@@ -1531,10 +1539,11 @@ static QDF_STATUS dp_rx_defrag_store_fragment(struct dp_soc *soc,
 		rx_reorder_array_elem->tail = NULL;
 		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO,
 		"Fragmented sequence successfully reinjected");
-	}
-	else
+	} else {
 		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
 		"Fragmented sequence reinjection failed");
+		dp_rx_return_head_frag_desc(peer, tid);
+	}
 
 	dp_rx_defrag_cleanup(peer, tid);
 	return QDF_STATUS_SUCCESS;
@@ -1646,3 +1655,86 @@ uint32_t dp_rx_frag_handle(struct dp_soc *soc, void *ring_desc,
 
 	return rx_bufs_used;
 }
+
+QDF_STATUS dp_rx_defrag_add_last_frag(struct dp_soc *soc,
+				      struct dp_peer *peer, uint16_t tid,
+		uint16_t rxseq, qdf_nbuf_t nbuf)
+{
+	struct dp_rx_tid *rx_tid = &peer->rx_tid[tid];
+	struct dp_rx_reorder_array_elem *rx_reorder_array_elem;
+	uint8_t all_frag_present;
+	QDF_STATUS status;
+
+	rx_reorder_array_elem = peer->rx_tid[tid].array;
+
+	if (rx_reorder_array_elem->head &&
+	    rxseq != rx_tid->curr_seq_num) {
+		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
+			  "%s: No list found for TID %d Seq# %d\n",
+				__func__, tid, rxseq);
+		qdf_nbuf_free(nbuf);
+		goto fail;
+	}
+
+	status = dp_rx_defrag_fraglist_insert(peer, tid,
+					      &rx_reorder_array_elem->head,
+			&rx_reorder_array_elem->tail, nbuf,
+			&all_frag_present);
+
+	if (QDF_IS_STATUS_ERROR(status)) {
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+			  "%s Fragment insert failed\n", __func__);
+
+		goto fail;
+	}
+
+	if (soc->rx.flags.defrag_timeout_check)
+		dp_rx_defrag_waitlist_remove(peer, tid);
+
+	if (!all_frag_present) {
+		uint32_t now_ms =
+			qdf_system_ticks_to_msecs(qdf_system_ticks());
+
+		peer->rx_tid[tid].defrag_timeout_ms =
+			now_ms + soc->rx.defrag.timeout_ms;
+
+		dp_rx_defrag_waitlist_add(peer, tid);
+
+		return QDF_STATUS_SUCCESS;
+	}
+
+	status = dp_rx_defrag(peer, tid, rx_reorder_array_elem->head,
+			      rx_reorder_array_elem->tail);
+
+	if (QDF_IS_STATUS_ERROR(status)) {
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+			  "%s Fragment processing failed\n", __func__);
+
+		dp_rx_return_head_frag_desc(peer, tid);
+		dp_rx_defrag_cleanup(peer, tid);
+
+		goto fail;
+	}
+
+	/* Re-inject the fragments back to REO for further processing */
+	status = dp_rx_defrag_reo_reinject(peer, tid,
+					   rx_reorder_array_elem->head);
+	if (QDF_IS_STATUS_SUCCESS(status)) {
+		rx_reorder_array_elem->head = NULL;
+		rx_reorder_array_elem->tail = NULL;
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO,
+			  "%s: Frag seq successfully reinjected\n",
+			__func__);
+	} else {
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+			  "%s: Frag seq reinjection failed\n",
+			__func__);
+		dp_rx_return_head_frag_desc(peer, tid);
+	}
+
+	dp_rx_defrag_cleanup(peer, tid);
+	return QDF_STATUS_SUCCESS;
+
+fail:
+	return QDF_STATUS_E_DEFRAG_ERROR;
+}

+ 4 - 0
dp/wifi3.0/dp_rx_defrag.h

@@ -144,4 +144,8 @@ void dp_rx_reorder_flush_frag(struct dp_peer *peer,
 			 unsigned int tid);
 void dp_rx_defrag_waitlist_remove(struct dp_peer *peer, unsigned tid);
 void dp_rx_defrag_cleanup(struct dp_peer *peer, unsigned tid);
+
+QDF_STATUS dp_rx_defrag_add_last_frag(struct dp_soc *soc,
+				      struct dp_peer *peer, uint16_t tid,
+		uint16_t rxseq, qdf_nbuf_t nbuf);
 #endif /* _DP_RX_DEFRAG_H */

+ 21 - 1
dp/wifi3.0/dp_rx_err.c

@@ -794,7 +794,9 @@ dp_rx_process_mic_error(struct dp_soc *soc,
 	struct ieee80211_frame *wh;
 	uint8_t *rx_pkt_hdr;
 	struct dp_peer *peer;
-	uint16_t peer_id;
+	uint16_t peer_id, rx_seq, fragno;
+	unsigned int tid;
+	QDF_STATUS status;
 
 	if (!hal_rx_msdu_end_first_msdu_get(rx_tlv_hdr))
 		return;
@@ -824,6 +826,24 @@ dp_rx_process_mic_error(struct dp_soc *soc,
 		goto fail;
 	}
 
+	tid = hal_rx_mpdu_start_tid_get(qdf_nbuf_data(nbuf));
+	rx_seq = (((*(uint16_t *)wh->i_seq) &
+			IEEE80211_SEQ_SEQ_MASK) >>
+			IEEE80211_SEQ_SEQ_SHIFT);
+
+	fragno = dp_rx_frag_get_mpdu_frag_number(qdf_nbuf_data(nbuf));
+
+	/* Can get only last fragment */
+	if (fragno) {
+		status = dp_rx_defrag_add_last_frag(soc, peer,
+						    tid, rx_seq, nbuf);
+
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+			  "%s: Frag pkt seq# %d frag# %d consumed status %d !\n",
+				__func__, rx_seq, fragno, status);
+			return;
+	}
+
 	tops = pdev->soc->cdp_soc.ol_ops;
 	if (tops->rx_mic_error)
 		tops->rx_mic_error(pdev->osif_pdev, vdev->vdev_id, wh);