Prechádzať zdrojové kódy

qcacmn: Fix mpdu re-stitch failure for fragmented wifi frames

When the length of a 802.11 frame is more than the
size of the posted receive buffers, it is split across
multiple buffers, which have to be accumulated before
submitting to network stack.

For all-but-last buffer which are used for the above
mentioned large 802.11 frame, the MSDU_CONTINUATION
flag will be set indicating that remaining data of the
same msdu has been DMA'ed to the next buffer.

The current implementation in monitor-1.0 works for
legacy chipsets, where MSDU_START tlv is available and
hence the aforementioned information was available in
all the buffers containing the large 802.11 frame.

For kiwi target, there is no MSDU_START tlv, and hence
a lot of information eg: mpdu_len_err, decap_format and
l2_hdr_offset are not available in the buffers where the
msdu_continuation flag is set. This leads to an attempt
of mpdu_restitch using incorrect data from tlv, and hence
the mpdu_restitch fails for such large 802.11 frame.

Fix this issue for kiwi target, by gathering all these
information which are available in the last buffer and
are used to process/accumulate the entire 802.11 frame.

Change-Id: I1c4fc9fd574c1c5fabc845407aa6f2d990c60906
CRs-Fixed: 3261494
Rakesh Pillai 2 rokov pred
rodič
commit
27f5c5e7b8

+ 79 - 1
dp/wifi3.0/monitor/1.0/dp_rx_mon_1.0.h

@@ -385,6 +385,7 @@ dp_rx_mon_parse_desc_buffer(struct dp_soc *dp_soc,
 			    bool *is_frag_p, uint32_t *total_frag_len_p,
 			    uint32_t *frag_len_p, uint16_t *l2_hdr_offset_p,
 			    qdf_frag_t rx_desc_tlv,
+			    void **first_rx_desc_tlv,
 			    bool *is_frag_non_raw_p, void *data)
 {
 	struct hal_rx_mon_dest_buf_info frame_info;
@@ -697,12 +698,88 @@ QDF_STATUS dp_rx_mon_alloc_parent_buffer(qdf_nbuf_t *head_msdu)
 	return QDF_STATUS_SUCCESS;
 }
 
+#ifdef QCA_WIFI_MONITOR_MODE_NO_MSDU_START_TLV_SUPPORT
+
+#define RXDMA_DATA_DMA_BLOCK_SIZE 128
 static inline void
 dp_rx_mon_parse_desc_buffer(struct dp_soc *dp_soc,
 			    struct hal_rx_msdu_desc_info *msdu_info,
 			    bool *is_frag_p, uint32_t *total_frag_len_p,
-			    uint32_t *frag_len_p, uint16_t *l2_hdr_offset_p,
+			    uint32_t *frag_len_p,
+			    uint16_t *l2_hdr_offset_p,
+			    qdf_frag_t rx_desc_tlv,
+			    void **first_rx_desc_tlv,
+			    bool *is_frag_non_raw_p, void *data)
+{
+	struct hal_rx_mon_dest_buf_info frame_info;
+	uint32_t rx_pkt_tlv_len = dp_soc->rx_mon_pkt_tlv_size;
+
+	/*
+	 * HW structures call this L3 header padding
+	 * -- even though this is actually the offset
+	 * from the buffer beginning where the L2
+	 * header begins.
+	 */
+	*l2_hdr_offset_p =
+	hal_rx_msdu_end_l3_hdr_padding_get(dp_soc->hal_soc, data);
+
+	if (msdu_info->msdu_flags & HAL_MSDU_F_MSDU_CONTINUATION) {
+		/*
+		 * Set l3_hdr_pad for first frag. This can be later
+		 * changed based on decap format, detected in last frag
+		 */
+		*l2_hdr_offset_p = DP_RX_MON_RAW_L2_HDR_PAD_BYTE;
+		if (!(*is_frag_p)) {
+			*l2_hdr_offset_p = DP_RX_MON_RAW_L2_HDR_PAD_BYTE;
+			*first_rx_desc_tlv = rx_desc_tlv;
+		}
+
+		*is_frag_p = true;
+		*frag_len_p = (RX_MONITOR_BUFFER_SIZE - rx_pkt_tlv_len -
+			       *l2_hdr_offset_p) &
+			      (RXDMA_DATA_DMA_BLOCK_SIZE - 1);
+		*total_frag_len_p += *frag_len_p;
+	} else {
+		if (hal_rx_tlv_decap_format_get(dp_soc->hal_soc, rx_desc_tlv) ==
+		    HAL_HW_RX_DECAP_FORMAT_RAW)
+			frame_info.is_decap_raw = 1;
+
+		if (hal_rx_tlv_mpdu_len_err_get(dp_soc->hal_soc, rx_desc_tlv))
+			frame_info.mpdu_len_err = 1;
+
+		frame_info.l2_hdr_pad = hal_rx_msdu_end_l3_hdr_padding_get(
+						dp_soc->hal_soc, rx_desc_tlv);
+
+		if (*is_frag_p) {
+			/* Last fragment of msdu */
+			*frag_len_p = msdu_info->msdu_len - *total_frag_len_p;
+
+			/* Set this in the first frag priv data */
+			hal_rx_priv_info_set_in_tlv(dp_soc->hal_soc,
+						    *first_rx_desc_tlv,
+						    (uint8_t *)&frame_info,
+						    sizeof(frame_info));
+		} else {
+			*frag_len_p = msdu_info->msdu_len;
+			hal_rx_priv_info_set_in_tlv(dp_soc->hal_soc,
+						    rx_desc_tlv,
+						    (uint8_t *)&frame_info,
+						    sizeof(frame_info));
+		}
+		*is_frag_p = false;
+		*first_rx_desc_tlv = NULL;
+	}
+}
+#else
+
+static inline void
+dp_rx_mon_parse_desc_buffer(struct dp_soc *dp_soc,
+			    struct hal_rx_msdu_desc_info *msdu_info,
+			    bool *is_frag_p, uint32_t *total_frag_len_p,
+			    uint32_t *frag_len_p,
+			    uint16_t *l2_hdr_offset_p,
 			    qdf_frag_t rx_desc_tlv,
+			    qdf_frag_t first_rx_desc_tlv,
 			    bool *is_frag_non_raw_p, void *data)
 {
 	/*
@@ -732,6 +809,7 @@ dp_rx_mon_parse_desc_buffer(struct dp_soc *dp_soc,
 		*is_frag_p = false;
 	}
 }
+#endif
 
 static inline void dp_rx_mon_buffer_set_pktlen(qdf_nbuf_t msdu, uint32_t size)
 {

+ 74 - 17
dp/wifi3.0/monitor/1.0/dp_rx_mon_dest_1.0.c

@@ -171,7 +171,7 @@ dp_rx_mon_mpdu_pop(struct dp_soc *soc, uint32_t mac_id,
 	union dp_rx_desc_list_elem_t **tail)
 {
 	struct dp_pdev *dp_pdev = dp_get_pdev_for_lmac_id(soc, mac_id);
-	void *rx_desc_tlv;
+	void *rx_desc_tlv, *first_rx_desc_tlv = NULL;
 	void *rx_msdu_link_desc;
 	qdf_nbuf_t msdu;
 	qdf_nbuf_t last;
@@ -380,6 +380,7 @@ dp_rx_mon_mpdu_pop(struct dp_soc *soc, uint32_t mac_id,
 						    &frag_len,
 						    &l2_hdr_offset,
 						    rx_desc_tlv,
+						    &first_rx_desc_tlv,
 						    &is_frag_non_raw, data);
 			if (!is_frag)
 				msdu_cnt--;
@@ -1211,15 +1212,59 @@ dp_rx_pdev_mon_desc_pool_alloc(struct dp_pdev *pdev)
 	return status;
 }
 
+#ifdef QCA_WIFI_MONITOR_MODE_NO_MSDU_START_TLV_SUPPORT
+static inline void
+hal_rx_populate_buf_info(struct dp_soc *soc,
+			 struct hal_rx_mon_dest_buf_info *buf_info,
+			 void *rx_desc)
+{
+	hal_rx_priv_info_get_from_tlv(soc->hal_soc, rx_desc,
+				      (uint8_t *)buf_info,
+				      sizeof(*buf_info));
+}
+
+static inline uint8_t
+hal_rx_frag_msdu_get_l2_hdr_offset(struct dp_soc *soc,
+				   struct hal_rx_mon_dest_buf_info *buf_info,
+				   void *rx_desc, bool is_first_frag)
+{
+	if (is_first_frag)
+		return buf_info->l2_hdr_pad;
+	else
+		return DP_RX_MON_RAW_L2_HDR_PAD_BYTE;
+}
+#else
+static inline void
+hal_rx_populate_buf_info(struct dp_soc *soc,
+			 struct hal_rx_mon_dest_buf_info *buf_info,
+			 void *rx_desc)
+{
+	if (hal_rx_tlv_decap_format_get(soc->hal_soc, rx_desc) ==
+	    HAL_HW_RX_DECAP_FORMAT_RAW)
+		buf_info->is_decap_raw = 1;
+
+	if (hal_rx_tlv_mpdu_len_err_get(soc->hal_soc, rx_desc))
+		buf_info->mpdu_len_err = 1;
+}
+
+static inline uint8_t
+hal_rx_frag_msdu_get_l2_hdr_offset(struct dp_soc *soc,
+				   struct hal_rx_mon_dest_buf_info *buf_info,
+				   void *rx_desc, bool is_first_frag)
+{
+	return hal_rx_msdu_end_l3_hdr_padding_get(soc->hal_soc, rx_desc);
+}
+#endif
+
 static inline
-void dp_rx_msdus_set_payload(struct dp_soc *soc, qdf_nbuf_t msdu)
+void dp_rx_msdus_set_payload(struct dp_soc *soc, qdf_nbuf_t msdu,
+			     uint8_t l2_hdr_offset)
 {
 	uint8_t *data;
-	uint32_t rx_pkt_offset, l2_hdr_offset;
+	uint32_t rx_pkt_offset;
 
 	data = qdf_nbuf_data(msdu);
 	rx_pkt_offset = soc->rx_mon_pkt_tlv_size;
-	l2_hdr_offset = hal_rx_msdu_end_l3_hdr_padding_get(soc->hal_soc, data);
 	qdf_nbuf_pull_head(msdu, rx_pkt_offset + l2_hdr_offset);
 }
 
@@ -1231,7 +1276,7 @@ dp_rx_mon_restitch_mpdu_from_msdus(struct dp_soc *soc,
 				   struct cdp_mon_status *rx_status)
 {
 	qdf_nbuf_t msdu, mpdu_buf, prev_buf, msdu_orig, head_frag_list;
-	uint32_t decap_format, wifi_hdr_len, sec_hdr_len, msdu_llc_len,
+	uint32_t wifi_hdr_len, sec_hdr_len, msdu_llc_len,
 		mpdu_buf_len, decap_hdr_pull_bytes, frag_list_sum_len, dir,
 		is_amsdu, is_first_frag, amsdu_pad;
 	void *rx_desc;
@@ -1241,6 +1286,8 @@ dp_rx_mon_restitch_mpdu_from_msdus(struct dp_soc *soc,
 	struct ieee80211_qoscntl *qos;
 	struct dp_pdev *dp_pdev = dp_get_pdev_for_lmac_id(soc, mac_id);
 	struct dp_mon_pdev *mon_pdev;
+	struct hal_rx_mon_dest_buf_info buf_info;
+	uint8_t l2_hdr_offset;
 
 	head_frag_list = NULL;
 	mpdu_buf = NULL;
@@ -1262,8 +1309,10 @@ dp_rx_mon_restitch_mpdu_from_msdus(struct dp_soc *soc,
 	msdu_orig = head_msdu;
 
 	rx_desc = qdf_nbuf_data(msdu_orig);
+	qdf_mem_zero(&buf_info, sizeof(buf_info));
+	hal_rx_populate_buf_info(soc, &buf_info, rx_desc);
 
-	if (hal_rx_tlv_mpdu_len_err_get(soc->hal_soc, rx_desc)) {
+	if (buf_info.mpdu_len_err) {
 		/* It looks like there is some issue on MPDU len err */
 		/* Need further investigate if drop the packet */
 		DP_STATS_INC(dp_pdev, dropped.mon_rx_drop, 1);
@@ -1281,21 +1330,23 @@ dp_rx_mon_restitch_mpdu_from_msdus(struct dp_soc *soc,
 
 	rx_desc = qdf_nbuf_data(head_msdu);
 
-	decap_format = hal_rx_tlv_decap_format_get(soc->hal_soc, rx_desc);
-
 	/* Easy case - The MSDU status indicates that this is a non-decapped
 	 * packet in RAW mode.
 	 */
-	if (decap_format == HAL_HW_RX_DECAP_FORMAT_RAW) {
+	if (buf_info.is_decap_raw) {
 		/* Note that this path might suffer from headroom unavailabilty
 		 * - but the RX status is usually enough
 		 */
 
-		dp_rx_msdus_set_payload(soc, head_msdu);
+		l2_hdr_offset = hal_rx_frag_msdu_get_l2_hdr_offset(soc,
+								   &buf_info,
+								   rx_desc,
+								   true);
+		dp_rx_msdus_set_payload(soc, head_msdu, l2_hdr_offset);
 
-			dp_rx_mon_dest_debug("%pK: decap format raw head %pK head->next %pK last_msdu %pK last_msdu->next %pK",
-					     soc, head_msdu, head_msdu->next,
-					     last_msdu, last_msdu->next);
+		dp_rx_mon_dest_debug("%pK: decap format raw head %pK head->next %pK last_msdu %pK last_msdu->next %pK",
+				     soc, head_msdu, head_msdu->next,
+				     last_msdu, last_msdu->next);
 
 		mpdu_buf = head_msdu;
 
@@ -1306,8 +1357,10 @@ dp_rx_mon_restitch_mpdu_from_msdus(struct dp_soc *soc,
 		is_first_frag = 1;
 
 		while (msdu) {
-
-			dp_rx_msdus_set_payload(soc, msdu);
+			l2_hdr_offset = hal_rx_frag_msdu_get_l2_hdr_offset(
+							soc, &buf_info,
+							rx_desc, false);
+			dp_rx_msdus_set_payload(soc, msdu, l2_hdr_offset);
 
 			if (is_first_frag) {
 				is_first_frag = 0;
@@ -1463,7 +1516,11 @@ dp_rx_mon_restitch_mpdu_from_msdus(struct dp_soc *soc,
 		dest += amsdu_pad;
 		qdf_mem_copy(dest, hdr_desc, msdu_llc_len);
 
-		dp_rx_msdus_set_payload(soc, msdu);
+		l2_hdr_offset = hal_rx_frag_msdu_get_l2_hdr_offset(soc,
+								   &buf_info,
+								   rx_desc,
+								   true);
+		dp_rx_msdus_set_payload(soc, msdu, l2_hdr_offset);
 
 		/* Push the MSDU buffer beyond the decap header */
 		qdf_nbuf_pull_head(msdu, decap_hdr_pull_bytes);
@@ -1518,7 +1575,7 @@ mpdu_stitch_done:
 	return mpdu_buf;
 
 mpdu_stitch_fail:
-	if ((mpdu_buf) && (decap_format != HAL_HW_RX_DECAP_FORMAT_RAW)) {
+	if ((mpdu_buf) && !buf_info.is_decap_raw) {
 		dp_rx_mon_dest_err("%pK: mpdu_stitch_fail mpdu_buf %pK",
 				   soc, mpdu_buf);
 		/* Free the head buffer */

+ 3 - 1
hal/wifi3.0/hal_rx.h

@@ -132,7 +132,9 @@ struct hal_rx_mon_dest_buf_info {
 	uint8_t first_buffer:1,
 		last_buffer:1,
 		is_decap_raw:1,
-		reserved_1:5;
+		mpdu_len_err:1,
+		l2_hdr_pad:2,
+		reserved_1:2;
 };
 
 /**