Pārlūkot izejas kodu

qcacmn: Handle MPDU having more than QDF_NBUF_MAX_FRAGS MSDU

Handle case in mon dest ring buffer processing,
where one MPDU can have more than QDF_NBUF_MAX_FRAGS MSDU.

Change-Id: If09099814b7f701efbaa20b1406958ad97d64829
CRs-Fixed: 2788919
Ankit Kumar 4 gadi atpakaļ
vecāks
revīzija
1f4a0ce9e6
2 mainītis faili ar 396 papildinājumiem un 184 dzēšanām
  1. 161 38
      dp/wifi3.0/dp_rx_mon.h
  2. 235 146
      dp/wifi3.0/dp_rx_mon_dest.c

+ 161 - 38
dp/wifi3.0/dp_rx_mon.h

@@ -32,17 +32,18 @@
  */
 #ifdef DP_RX_MON_MEM_FRAG
 /*
- *----------------------------------
- *| Reserve | PF Tag | Radiotap hdr|
- *|  64B    |   64B  |    128B     |
- *----------------------------------
- * Reserved 64B is used to fill Protocol & Flow tag before writing into
- * actual offset, data gets written to actual offset after updating
- * radiotap HDR.
- */
-#define DP_RX_MON_MAX_MONITOR_HEADER (256)
-#else
-#define DP_RX_MON_MAX_MONITOR_HEADER (512)
+ *  -------------------------------------------------
+ * |       Protocol & Flow TAG      | Radiotap header|
+ * |                                |  Length(128 B) |
+ * |  ((4* QDF_NBUF_MAX_FRAGS) * 2) |                |
+ *  -------------------------------------------------
+ */
+#define DP_RX_MON_MAX_RADIO_TAP_HDR (128)
+#define DP_RX_MON_PF_TAG_LEN_PER_FRAG (4)
+#define DP_RX_MON_TOT_PF_TAG_LEN \
+	((DP_RX_MON_PF_TAG_LEN_PER_FRAG) * (QDF_NBUF_MAX_FRAGS))
+#define DP_RX_MON_MAX_MONITOR_HEADER \
+	((DP_RX_MON_TOT_PF_TAG_LEN * 2) + (DP_RX_MON_MAX_RADIO_TAP_HDR))
 #endif
 
 /* The maximum buffer length allocated for radiotap for monitor status buffer */
@@ -356,6 +357,41 @@ void dp_rx_mon_frag_adjust_frag_len(uint32_t *total_len, uint32_t *frag_len,
 #define DP_RX_MON_GET_NBUF_FROM_DESC(rx_desc) \
 	NULL
 
+/**
+ * dp_rx_mon_add_msdu_to_list_failure_handler() - Handler for nbuf buffer
+ *                                                  attach failure
+ *
+ * @rx_tlv_hdr: rx_tlv_hdr
+ * @pdev: struct dp_pdev *
+ * @last: skb pointing to last skb in chained list at any moment
+ * @head_msdu: parent skb in the chained list
+ * @tail_msdu: Last skb in the chained list
+ * @func_name: caller function name
+ *
+ * Return: void
+ */
+static inline void
+dp_rx_mon_add_msdu_to_list_failure_handler(void *rx_tlv_hdr,
+					   struct dp_pdev *pdev,
+					   qdf_nbuf_t *last,
+					   qdf_nbuf_t *head_msdu,
+					   qdf_nbuf_t *tail_msdu,
+					   const char *func_name)
+{
+	DP_STATS_INC(pdev, replenish.nbuf_alloc_fail, 1);
+	qdf_frag_free(rx_tlv_hdr);
+	if (head_msdu)
+		qdf_nbuf_list_free(*head_msdu);
+	dp_err("[%s] failed to allocate subsequent parent buffer to hold all frag\n",
+	       func_name);
+	if (head_msdu)
+		*head_msdu = NULL;
+	if (last)
+		*last = NULL;
+	if (tail_msdu)
+		*tail_msdu = NULL;
+}
+
 /**
  * dp_rx_mon_get_paddr_from_desc() - Get paddr from desc
  */
@@ -413,20 +449,28 @@ dp_rx_mon_buffer_unmap(struct dp_soc *soc, struct dp_rx_desc *rx_desc,
 static inline
 QDF_STATUS dp_rx_mon_alloc_parent_buffer(qdf_nbuf_t *head_msdu)
 {
-	/* Headroom should accommodate radiotap header
+	/*
+	 * Headroom should accommodate radiotap header
 	 * and protocol and flow tag for all frag
+	 * Length reserved to accommodate Radiotap header
+	 * is 128 bytes and length reserved for Protocol
+	 * flow tag will vary based on QDF_NBUF_MAX_FRAGS.
 	 */
-	/* --------------------------------------
-	 * | Protocol & Flow TAG | Radiotap header|
-	 * |     64 B            |  Length(128 B) |
-	 * --------------------------------------
+	/*  -------------------------------------------------
+	 * |       Protocol & Flow TAG      | Radiotap header|
+	 * |                                |  Length(128 B) |
+	 * |  ((4* QDF_NBUF_MAX_FRAGS) * 2) |                |
+	 *  -------------------------------------------------
 	 */
+
 	*head_msdu = qdf_nbuf_alloc_no_recycler(DP_RX_MON_MAX_MONITOR_HEADER,
 						DP_RX_MON_MAX_MONITOR_HEADER, 4);
 
 	if (!(*head_msdu))
 		return QDF_STATUS_E_FAILURE;
 
+	qdf_mem_zero(qdf_nbuf_head(*head_msdu), qdf_nbuf_headroom(*head_msdu));
+
 	/* Set *head_msdu->next as NULL as all msdus are
 	 * mapped via nr frags
 	 */
@@ -577,26 +621,85 @@ static inline void dp_rx_mon_buffer_set_pktlen(qdf_nbuf_t msdu, uint32_t size)
  * @l2_hdr_offset: l2 hdr padding
  */
 static inline
-void dp_rx_mon_add_msdu_to_list(qdf_nbuf_t *head_msdu, qdf_nbuf_t msdu,
-				qdf_nbuf_t *last, qdf_frag_t rx_desc_tlv,
-				uint32_t frag_len, uint32_t l2_hdr_offset)
+QDF_STATUS dp_rx_mon_add_msdu_to_list(qdf_nbuf_t *head_msdu, qdf_nbuf_t msdu,
+				      qdf_nbuf_t *last, qdf_frag_t rx_desc_tlv,
+				      uint32_t frag_len,
+				      uint32_t l2_hdr_offset)
 {
-	qdf_nbuf_add_rx_frag(rx_desc_tlv, *head_msdu, SIZE_OF_MONITOR_TLV,
+	uint32_t num_frags;
+	qdf_nbuf_t msdu_curr;
+
+	/* Here head_msdu and *head_msdu must not be NULL */
+	if (qdf_unlikely(!head_msdu || !(*head_msdu))) {
+		dp_err("[%s] head_msdu || *head_msdu is Null while adding frag to skb\n",
+		       __func__);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	/* In case of first desc of MPDU, assign curr msdu to *head_msdu */
+	if (!qdf_nbuf_get_nr_frags(*head_msdu))
+		msdu_curr = *head_msdu;
+	else
+		msdu_curr = *last;
+
+	/* Current msdu must not be NULL */
+	if (qdf_unlikely(!msdu_curr)) {
+		dp_err("[%s] Current msdu can't be Null while adding frag to skb\n",
+		       __func__);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	num_frags = qdf_nbuf_get_nr_frags(msdu_curr);
+	if (num_frags < QDF_NBUF_MAX_FRAGS) {
+		qdf_nbuf_add_rx_frag(rx_desc_tlv, msdu_curr,
+				     SIZE_OF_MONITOR_TLV,
+				     frag_len + l2_hdr_offset,
+				     RX_MONITOR_BUFFER_SIZE,
+				     false);
+		if (*last != msdu_curr)
+			*last = msdu_curr;
+		return QDF_STATUS_SUCCESS;
+	}
+
+	/* Execution will reach here only if num_frags == QDF_NBUF_MAX_FRAGS */
+	msdu_curr = NULL;
+	if ((dp_rx_mon_alloc_parent_buffer(&msdu_curr))
+	    != QDF_STATUS_SUCCESS)
+		return QDF_STATUS_E_FAILURE;
+
+	qdf_nbuf_add_rx_frag(rx_desc_tlv, msdu_curr, SIZE_OF_MONITOR_TLV,
 			     frag_len + l2_hdr_offset, RX_MONITOR_BUFFER_SIZE,
 			     false);
+
+	/* Add allocated nbuf in the chain */
+	qdf_nbuf_set_next(*last, msdu_curr);
+
+	/* Assign current msdu to last to avoid traversal */
+	*last = msdu_curr;
+
+	return QDF_STATUS_SUCCESS;
 }
 
 /**
  * dp_rx_mon_init_tail_msdu() - Initialize tail msdu
  *
+ * @head_msdu: Parent buffer to hold MPDU data
  * @msdu: Msdu to be updated in tail_msdu
  * @last: last msdu
  * @tail_msdu: Last msdu
  */
 static inline
-void dp_rx_mon_init_tail_msdu(qdf_nbuf_t msdu, qdf_nbuf_t last,
-			      qdf_nbuf_t *tail_msdu)
+void dp_rx_mon_init_tail_msdu(qdf_nbuf_t *head_msdu, qdf_nbuf_t msdu,
+			      qdf_nbuf_t last, qdf_nbuf_t *tail_msdu)
 {
+	if (!head_msdu || !(*head_msdu)) {
+		*tail_msdu = NULL;
+		return;
+	}
+
+	if (last)
+		qdf_nbuf_set_next(last, NULL);
+	*tail_msdu = last;
 }
 
 /**
@@ -605,22 +708,29 @@ void dp_rx_mon_init_tail_msdu(qdf_nbuf_t msdu, qdf_nbuf_t last,
  * If feature is disabled, then removal happens in restitch logic.
  *
  * @head_msdu: Head msdu
+ * @tail_msdu: Tail msdu
  */
 static inline
-void dp_rx_mon_remove_raw_frame_fcs_len(qdf_nbuf_t *head_msdu)
+void dp_rx_mon_remove_raw_frame_fcs_len(qdf_nbuf_t *head_msdu,
+					qdf_nbuf_t *tail_msdu)
 {
 	qdf_frag_t addr;
 
+	if (qdf_unlikely(!head_msdu || !tail_msdu || !(*head_msdu)))
+		return;
+
+	/* If *head_msdu is valid, then *tail_msdu must be valid */
+	/* If head_msdu is valid, then it must have nr_frags */
+	/* If tail_msdu is valid, then it must have nr_frags */
+
 	/* Strip FCS_LEN for Raw frame */
-	if (head_msdu && *head_msdu) {
-		addr = qdf_nbuf_get_frag_addr(*head_msdu, 0);
-		addr -= SIZE_OF_MONITOR_TLV;
-		if (HAL_RX_DESC_GET_DECAP_FORMAT(addr) ==
-		    HAL_HW_RX_DECAP_FORMAT_RAW) {
-			qdf_nbuf_trim_add_frag_size(*head_msdu,
-				qdf_nbuf_get_nr_frags(*head_msdu) - 1,
-						-HAL_RX_FCS_LEN, 0);
-		}
+	addr = qdf_nbuf_get_frag_addr(*head_msdu, 0);
+	addr -= SIZE_OF_MONITOR_TLV;
+	if (HAL_RX_DESC_GET_DECAP_FORMAT(addr) ==
+	    HAL_HW_RX_DECAP_FORMAT_RAW) {
+		qdf_nbuf_trim_add_frag_size(*tail_msdu,
+			qdf_nbuf_get_nr_frags(*tail_msdu) - 1,
+					-HAL_RX_FCS_LEN, 0);
 	}
 }
 
@@ -658,6 +768,16 @@ qdf_frag_t dp_rx_mon_get_nbuf_80211_hdr(qdf_nbuf_t nbuf)
 #define DP_RX_MON_GET_NBUF_FROM_DESC(rx_desc) \
 	(rx_desc->nbuf)
 
+static inline void
+dp_rx_mon_add_msdu_to_list_failure_handler(void *rx_tlv_hdr,
+					   struct dp_pdev *pdev,
+					   qdf_nbuf_t *last,
+					   qdf_nbuf_t *head_msdu,
+					   qdf_nbuf_t *tail_msdu,
+					   const char *func_name)
+{
+}
+
 static inline
 qdf_dma_addr_t dp_rx_mon_get_paddr_from_desc(struct dp_rx_desc *rx_desc)
 {
@@ -738,9 +858,10 @@ static inline void dp_rx_mon_buffer_set_pktlen(qdf_nbuf_t msdu, uint32_t size)
 }
 
 static inline
-void dp_rx_mon_add_msdu_to_list(qdf_nbuf_t *head_msdu, qdf_nbuf_t msdu,
-				qdf_nbuf_t *last, qdf_frag_t rx_desc_tlv,
-				uint32_t frag_len, uint32_t l2_hdr_offset)
+QDF_STATUS dp_rx_mon_add_msdu_to_list(qdf_nbuf_t *head_msdu, qdf_nbuf_t msdu,
+				      qdf_nbuf_t *last, qdf_frag_t rx_desc_tlv,
+				      uint32_t frag_len,
+				      uint32_t l2_hdr_offset)
 {
 	if (head_msdu && !*head_msdu) {
 		*head_msdu = msdu;
@@ -749,11 +870,12 @@ void dp_rx_mon_add_msdu_to_list(qdf_nbuf_t *head_msdu, qdf_nbuf_t msdu,
 			qdf_nbuf_set_next(*last, msdu);
 	}
 	*last = msdu;
+	return QDF_STATUS_SUCCESS;
 }
 
 static inline
-void dp_rx_mon_init_tail_msdu(qdf_nbuf_t msdu, qdf_nbuf_t last,
-			      qdf_nbuf_t *tail_msdu)
+void dp_rx_mon_init_tail_msdu(qdf_nbuf_t *head_msdu, qdf_nbuf_t msdu,
+			      qdf_nbuf_t last, qdf_nbuf_t *tail_msdu)
 {
 	if (last)
 		qdf_nbuf_set_next(last, NULL);
@@ -762,7 +884,8 @@ void dp_rx_mon_init_tail_msdu(qdf_nbuf_t msdu, qdf_nbuf_t last,
 }
 
 static inline
-void dp_rx_mon_remove_raw_frame_fcs_len(qdf_nbuf_t *head_msdu)
+void dp_rx_mon_remove_raw_frame_fcs_len(qdf_nbuf_t *head_msdu,
+					qdf_nbuf_t *tail_msdu)
 {
 }
 

+ 235 - 146
dp/wifi3.0/dp_rx_mon_dest.c

@@ -70,17 +70,6 @@ dp_tx_capture_get_user_id(struct dp_pdev *dp_pdev, void *rx_desc_tlv)
 }
 #endif
 
-/*
- * The protocol flow tag size
- * if DP_RX_MON_MEM_FRAG is enabled.
- */
-#ifdef DP_RX_MON_MEM_FRAG
-#if defined(WLAN_SUPPORT_RX_PROTOCOL_TYPE_TAG) ||\
-	defined(WLAN_SUPPORT_RX_FLOW_TAG)
-#define DP_RX_MON_PF_TAG_TOT_LEN (64)
-#endif
-#endif
-
 /*
  * PPDU id is from 0 to 64k-1. PPDU id read from status ring and PPDU id
  * read from destination ring shall track each other. If the distance of
@@ -432,9 +421,16 @@ dp_rx_mon_mpdu_pop(struct dp_soc *soc, uint32_t mac_id,
 					  msdu_list.msdu_info[i].msdu_len,
 					  frag_len);
 
-			dp_rx_mon_add_msdu_to_list(head_msdu, msdu, &last,
-						   rx_desc_tlv, frag_len,
-						   l2_hdr_offset);
+			if (dp_rx_mon_add_msdu_to_list(head_msdu, msdu, &last,
+						       rx_desc_tlv, frag_len,
+						       l2_hdr_offset)
+					!= QDF_STATUS_SUCCESS) {
+				dp_rx_mon_add_msdu_to_list_failure_handler(rx_desc_tlv,
+						dp_pdev, &last, head_msdu,
+						tail_msdu, __func__);
+				drop_mpdu = true;
+				goto next_msdu;
+			}
 
 next_msdu:
 			dp_pdev->mon_last_buf_cookie = msdu_list.sw_cookie[i];
@@ -460,10 +456,10 @@ next_msdu:
 			dp_err_rl("monitor link desc return failed");
 	} while (buf_info.paddr && msdu_cnt);
 
-	dp_rx_mon_init_tail_msdu(msdu, last, tail_msdu);
-	dp_rx_mon_remove_raw_frame_fcs_len(head_msdu);
-	return rx_bufs_used;
+	dp_rx_mon_init_tail_msdu(head_msdu, msdu, last, tail_msdu);
+	dp_rx_mon_remove_raw_frame_fcs_len(head_msdu, tail_msdu);
 
+	return rx_bufs_used;
 }
 
 static inline
@@ -479,6 +475,49 @@ void dp_rx_msdus_set_payload(struct dp_soc *soc, qdf_nbuf_t msdu)
 }
 
 #ifdef DP_RX_MON_MEM_FRAG
+/**
+ * dp_rx_mon_fraglist_prepare() - Prepare nbuf fraglist from chained skb
+ *
+ * @head_msdu: Parent SKB
+ * @tail_msdu: Last skb in the chained list
+ *
+ * Return: Void
+ */
+void dp_rx_mon_fraglist_prepare(qdf_nbuf_t head_msdu, qdf_nbuf_t tail_msdu)
+{
+	qdf_nbuf_t msdu, mpdu_buf, prev_buf, head_frag_list;
+	uint32_t frag_list_sum_len;
+
+	dp_err("[%s][%d] decap format raw head %pK head->next %pK last_msdu %pK last_msdu->next %pK",
+	       __func__, __LINE__, head_msdu, head_msdu->next,
+	       tail_msdu, tail_msdu->next);
+
+	/* Single skb accommodating MPDU worth Data */
+	if (tail_msdu == head_msdu)
+		return;
+
+	mpdu_buf = head_msdu;
+	prev_buf = mpdu_buf;
+	frag_list_sum_len = 0;
+
+	msdu = qdf_nbuf_next(head_msdu);
+	/* msdu can't be NULL here as it is multiple skb case here */
+
+	/* Head frag list to point to second skb */
+	head_frag_list  = msdu;
+
+	while (msdu) {
+		frag_list_sum_len += qdf_nbuf_len(msdu);
+		prev_buf = msdu;
+		msdu = qdf_nbuf_next(msdu);
+	}
+
+	qdf_nbuf_append_ext_list(mpdu_buf, head_frag_list, frag_list_sum_len);
+
+	/* Make Parent skb next to NULL */
+	qdf_nbuf_set_next(mpdu_buf, NULL);
+}
+
 /**
  * dp_rx_mon_frag_restitch_mpdu_from_msdus() - Restitch logic to
  *      convert to 802.3 header and adjust frag memory pointing to
@@ -487,6 +526,7 @@ void dp_rx_msdus_set_payload(struct dp_soc *soc, qdf_nbuf_t msdu)
  * @soc: struct dp_soc *
  * @mac_id: MAC id
  * @head_msdu: MPDU containing all MSDU as a frag
+ * @tail_msdu: last skb which accommodate MPDU info
  * @rx_status: struct cdp_mon_status *
  *
  * Return: Adjusted nbuf containing MPDU worth info.
@@ -495,6 +535,7 @@ static inline
 qdf_nbuf_t dp_rx_mon_frag_restitch_mpdu_from_msdus(struct dp_soc *soc,
 						   uint32_t mac_id,
 						   qdf_nbuf_t head_msdu,
+						   qdf_nbuf_t tail_msdu,
 						   struct cdp_mon_status *rx_status)
 {
 	uint32_t wifi_hdr_len, sec_hdr_len, msdu_llc_len,
@@ -509,6 +550,7 @@ qdf_nbuf_t dp_rx_mon_frag_restitch_mpdu_from_msdus(struct dp_soc *soc,
 	int16_t frag_page_offset = 0;
 	struct hal_rx_mon_dest_buf_info buf_info;
 	uint32_t pad_byte_pholder = 0;
+	qdf_nbuf_t msdu_curr;
 
 	if (qdf_unlikely(!dp_pdev)) {
 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
@@ -517,10 +559,9 @@ qdf_nbuf_t dp_rx_mon_frag_restitch_mpdu_from_msdus(struct dp_soc *soc,
 	}
 	qdf_mem_zero(&buf_info, sizeof(struct hal_rx_mon_dest_buf_info));
 
-	if (!head_msdu)
+	if (!head_msdu || !tail_msdu)
 		goto mpdu_stitch_fail;
 
-	num_frags = qdf_nbuf_get_nr_frags(head_msdu);
 	rx_desc = qdf_nbuf_get_frag_addr(head_msdu, 0) - SIZE_OF_MONITOR_TLV;
 
 	if (HAL_RX_DESC_GET_MPDU_LENGTH_ERR(rx_desc)) {
@@ -531,8 +572,9 @@ qdf_nbuf_t dp_rx_mon_frag_restitch_mpdu_from_msdus(struct dp_soc *soc,
 	}
 
 	/* Look for FCS error */
+	num_frags = qdf_nbuf_get_nr_frags(tail_msdu);
 	rx_desc =
-		qdf_nbuf_get_frag_addr(head_msdu,
+		qdf_nbuf_get_frag_addr(tail_msdu,
 				       num_frags - 1) - SIZE_OF_MONITOR_TLV;
 	rx_status->cdp_rs_fcs_err = HAL_RX_DESC_GET_MPDU_FCS_ERR(rx_desc);
 	dp_pdev->ppdu_info.rx_status.rs_fcs_err =
@@ -544,8 +586,10 @@ qdf_nbuf_t dp_rx_mon_frag_restitch_mpdu_from_msdus(struct dp_soc *soc,
 	/* Easy case - The MSDU status indicates that this is a non-decapped
 	 * packet in RAW mode.
 	*/
-	if (buf_info.is_decap_raw == 1)
+	if (buf_info.is_decap_raw == 1) {
+		dp_rx_mon_fraglist_prepare(head_msdu, tail_msdu);
 		goto mpdu_stitch_done;
+	}
 
 	l2_hdr_offset = DP_RX_MON_NONRAW_L2_HDR_PAD_BYTE;
 
@@ -617,7 +661,6 @@ qdf_nbuf_t dp_rx_mon_frag_restitch_mpdu_from_msdus(struct dp_soc *soc,
 	 * update frag index in ctx_idx field.
 	 * Reset head pointer data of nbuf before updating.
 	 */
-	qdf_mem_zero(qdf_nbuf_head(head_msdu),  DP_RX_MON_PF_TAG_TOT_LEN);
 	QDF_NBUF_CB_RX_CTX_ID(head_msdu) = 0;
 	dp_rx_mon_update_protocol_flow_tag(soc, dp_pdev, head_msdu, rx_desc);
 
@@ -673,9 +716,16 @@ qdf_nbuf_t dp_rx_mon_frag_restitch_mpdu_from_msdus(struct dp_soc *soc,
 		/* MSDU with single bufffer */
 		amsdu_pad = frag_size & 0x3;
 		amsdu_pad = amsdu_pad ? (4 - amsdu_pad) : 0;
-		if (amsdu_pad <= pad_byte_pholder) {
+		if (amsdu_pad && (amsdu_pad <= pad_byte_pholder)) {
+			char *frag_addr_temp;
 			qdf_nbuf_trim_add_frag_size(head_msdu, 0, amsdu_pad,
 						    0);
+			frag_addr_temp =
+				(char *)qdf_nbuf_get_frag_addr(head_msdu, 0);
+			frag_addr_temp = (frag_addr_temp +
+				qdf_nbuf_get_frag_size_by_idx(head_msdu, 0)) -
+					amsdu_pad;
+			qdf_mem_zero(frag_addr_temp, amsdu_pad);
 			amsdu_pad = 0;
 		}
 	} else {
@@ -695,7 +745,16 @@ qdf_nbuf_t dp_rx_mon_frag_restitch_mpdu_from_msdus(struct dp_soc *soc,
 	 * So dont initialize to ZERO here
 	 */
 	pad_byte_pholder = 0;
-	for (frags_iter = 1; frags_iter < num_frags; frags_iter++) {
+	for (msdu_curr = head_msdu; msdu_curr;) {
+		/* frag_iter will start from 0 for second skb onwards */
+		if (msdu_curr == head_msdu)
+			frags_iter = 1;
+		else
+			frags_iter = 0;
+
+		num_frags = qdf_nbuf_get_nr_frags(msdu_curr);
+
+		for (; frags_iter < num_frags; frags_iter++) {
 		/* Construct destination address
 		 *  ----------------------------------------------------------
 		 * | RX_PKT_TLV | L2_HDR_PAD   |   Decap HDR | Payload | Pad  |
@@ -717,119 +776,137 @@ qdf_nbuf_t dp_rx_mon_frag_restitch_mpdu_from_msdus(struct dp_soc *soc,
 		 * |       (First Buffer)                |    |               |
 		 *  ----------------------------------------------------------
 		 */
-		frag_addr = qdf_nbuf_get_frag_addr(head_msdu, frags_iter);
-		rx_desc = frag_addr - SIZE_OF_MONITOR_TLV;
-
-		/*
-		 * Update protocol and flow tag for MSDU
-		 * update frag index in ctx_idx field
-		 */
-		QDF_NBUF_CB_RX_CTX_ID(head_msdu) = frags_iter;
-		dp_rx_mon_update_protocol_flow_tag(soc, dp_pdev,
-						   head_msdu, rx_desc);
-
-		/* Read buffer info from stored data in tlvs */
-		hal_rx_mon_dest_get_buffer_info_from_tlv(rx_desc,
-							 &buf_info);
-
-		frag_size = qdf_nbuf_get_frag_size_by_idx(head_msdu,
-							  frags_iter);
-
-		/* If Middle buffer, dont add any header */
-		if ((!buf_info.first_buffer) && (!buf_info.last_buffer)) {
-			tot_msdu_len += frag_size;
-			amsdu_pad = 0;
-			pad_byte_pholder = 0;
-			continue;
-		}
-
-		/* Calculate if current buffer has placeholder
-		 * to accommodate amsdu pad byte
-		 */
-		pad_byte_pholder =
-			(RX_MONITOR_BUFFER_SIZE - RX_PKT_TLVS_LEN) - frag_size;
-		/*
-		 * We will come here only only three condition:
-		 * 1. Msdu with single Buffer
-		 * 2. First buffer in case MSDU is spread in multiple buffer
-		 * 3. Last buffer in case MSDU is spread in multiple buffer
-		 *
-		 *         First buffER | Last buffer
-		 * Case 1:      1       |     1
-		 * Case 2:      1       |     0
-		 * Case 3:      0       |     1
-		 *
-		 * In 3rd case only l2_hdr_padding byte will be Zero and in
-		 * other case, It will be 2 Bytes.
-		 */
-		if (buf_info.first_buffer)
-			l2_hdr_offset = DP_RX_MON_NONRAW_L2_HDR_PAD_BYTE;
-		else
-			l2_hdr_offset = DP_RX_MON_RAW_L2_HDR_PAD_BYTE;
+			frag_addr =
+				qdf_nbuf_get_frag_addr(msdu_curr, frags_iter);
+			rx_desc = frag_addr - SIZE_OF_MONITOR_TLV;
 
-		if (buf_info.first_buffer) {
-			/* Src addr from whre llc header needs to be copied */
-			rx_src_desc = HAL_RX_DESC_GET_80211_HDR(rx_desc);
+			/*
+			 * Update protocol and flow tag for MSDU
+			 * update frag index in ctx_idx field
+			 */
+			QDF_NBUF_CB_RX_CTX_ID(msdu_curr) = frags_iter;
+			dp_rx_mon_update_protocol_flow_tag(soc, dp_pdev,
+							   msdu_curr, rx_desc);
 
-			/* Size of buffer with llc header */
-			frag_size = frag_size -
-				(l2_hdr_offset + decap_hdr_pull_bytes);
-			frag_size += msdu_llc_len;
+			/* Read buffer info from stored data in tlvs */
+			hal_rx_mon_dest_get_buffer_info_from_tlv(rx_desc,
+								 &buf_info);
 
-			/* Construct destination address */
-			rx_dest_desc = frag_addr + decap_hdr_pull_bytes +
-						l2_hdr_offset;
-			rx_dest_desc = rx_dest_desc - (msdu_llc_len);
+			frag_size = qdf_nbuf_get_frag_size_by_idx(msdu_curr,
+								  frags_iter);
 
-			qdf_mem_copy(rx_dest_desc, rx_src_desc, msdu_llc_len);
+			/* If Middle buffer, dont add any header */
+			if ((!buf_info.first_buffer) && (!buf_info.last_buffer)) {
+				tot_msdu_len += frag_size;
+				amsdu_pad = 0;
+				pad_byte_pholder = 0;
+				continue;
+			}
 
+			/* Calculate if current buffer has placeholder
+			 * to accommodate amsdu pad byte
+			 */
+			pad_byte_pholder =
+				(RX_MONITOR_BUFFER_SIZE - RX_PKT_TLVS_LEN) - frag_size;
 			/*
-			 * Calculate new page offset and create hole
-			 * if amsdu_pad required.
+			 * We will come here only only three condition:
+			 * 1. Msdu with single Buffer
+			 * 2. First buffer in case MSDU is spread in multiple
+			 *    buffer
+			 * 3. Last buffer in case MSDU is spread in multiple
+			 *    buffer
+			 *
+			 *         First buffER | Last buffer
+			 * Case 1:      1       |     1
+			 * Case 2:      1       |     0
+			 * Case 3:      0       |     1
+			 *
+			 * In 3rd case only l2_hdr_padding byte will be Zero and
+			 * in other case, It will be 2 Bytes.
 			 */
-			frag_page_offset = l2_hdr_offset +
-					decap_hdr_pull_bytes;
-			frag_page_offset = frag_page_offset -
-					(msdu_llc_len + amsdu_pad);
+			if (buf_info.first_buffer)
+				l2_hdr_offset = DP_RX_MON_NONRAW_L2_HDR_PAD_BYTE;
+			else
+				l2_hdr_offset = DP_RX_MON_RAW_L2_HDR_PAD_BYTE;
+
+			if (buf_info.first_buffer) {
+				/* Src addr from where llc header needs to be copied */
+				rx_src_desc =
+					HAL_RX_DESC_GET_80211_HDR(rx_desc);
+
+				/* Size of buffer with llc header */
+				frag_size = frag_size -
+					(l2_hdr_offset + decap_hdr_pull_bytes);
+				frag_size += msdu_llc_len;
+
+				/* Construct destination address */
+				rx_dest_desc = frag_addr +
+					decap_hdr_pull_bytes + l2_hdr_offset;
+				rx_dest_desc = rx_dest_desc - (msdu_llc_len);
+
+				qdf_mem_copy(rx_dest_desc, rx_src_desc,
+					     msdu_llc_len);
+
+				/*
+				 * Calculate new page offset and create hole
+				 * if amsdu_pad required.
+				 */
+				frag_page_offset = l2_hdr_offset +
+						decap_hdr_pull_bytes;
+				frag_page_offset = frag_page_offset -
+						(msdu_llc_len + amsdu_pad);
+
+				qdf_nbuf_move_frag_page_offset(msdu_curr,
+							       frags_iter,
+							       frag_page_offset);
+
+				tot_msdu_len = frag_size;
+				/*
+				 * No amsdu padding required for first frame of
+				 * continuation buffer
+				 */
+				if (!buf_info.last_buffer) {
+					amsdu_pad = 0;
+					continue;
+				}
+			} else {
+				tot_msdu_len += frag_size;
+			}
 
-			qdf_nbuf_move_frag_page_offset(head_msdu, frags_iter,
-						       frag_page_offset);
+			/* Will reach to this place in only two case:
+			 * 1. Single buffer MSDU
+			 * 2. Last buffer of MSDU in case of multiple buf MSDU
+			 */
 
-			tot_msdu_len = frag_size;
-			/*
-			 * No amsdu padding required for first frame of
-			 * continuation buffer
+			/* Check size of buffer if amsdu padding required */
+			amsdu_pad = tot_msdu_len & 0x3;
+			amsdu_pad = amsdu_pad ? (4 - amsdu_pad) : 0;
+
+			/* Create placeholder if current bufer can
+			 * accommodate padding.
 			 */
-			if (!buf_info.last_buffer) {
+			if (amsdu_pad && (amsdu_pad <= pad_byte_pholder)) {
+				char *frag_addr_temp;
+
+				qdf_nbuf_trim_add_frag_size(msdu_curr,
+							    frags_iter,
+							    amsdu_pad, 0);
+				frag_addr_temp = (char *)qdf_nbuf_get_frag_addr(msdu_curr, 0);
+				frag_addr_temp = (frag_addr_temp +
+					qdf_nbuf_get_frag_size_by_idx(msdu_curr, frags_iter)) -
+					amsdu_pad;
+				qdf_mem_zero(frag_addr_temp, amsdu_pad);
 				amsdu_pad = 0;
-				continue;
 			}
-		} else {
-			tot_msdu_len += frag_size;
-		}
 
-		/* Will reach to this place in only two case:
-		 * 1. Single buffer MSDU
-		 * 2. Last buffer of MSDU in case of multiple buffer MSDU
-		 */
-
-		/* Check size of buffer if amsdu padding required */
-		amsdu_pad = tot_msdu_len & 0x3;
-		amsdu_pad = amsdu_pad ? (4 - amsdu_pad) : 0;
-
-		/* Create placeholder if current bufer can
-		 * accommodate padding.
-		 */
-		if (amsdu_pad <= pad_byte_pholder) {
-			qdf_nbuf_trim_add_frag_size(head_msdu, frags_iter,
-						    amsdu_pad, 0);
-			amsdu_pad = 0;
+			/* reset tot_msdu_len */
+			tot_msdu_len = 0;
 		}
-
-		/* reset tot_msdu_len */
-		tot_msdu_len = 0;
+		msdu_curr = qdf_nbuf_next(msdu_curr);
 	}
 
+	dp_rx_mon_fraglist_prepare(head_msdu, tail_msdu);
+
 	QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
 		  "%s %d head_msdu %pK head_msdu->len %u",
 		  __func__, __LINE__,
@@ -1155,42 +1232,57 @@ mpdu_stitch_fail:
 	defined(WLAN_SUPPORT_RX_FLOW_TAG)
 static inline
 void dp_rx_mon_update_pf_tag_to_buf_headroom(struct dp_soc *soc,
-					     struct dp_pdev *pdev,
 					     qdf_nbuf_t nbuf)
 {
-	bool is_mon_protocol_flow_tag_enabled;
+	qdf_nbuf_t ext_list;
 
-	if (qdf_unlikely(!soc || !pdev || !nbuf))
-		return;
+	if (qdf_unlikely(!soc)) {
+		dp_err("Soc[%pK] Null. Can't update pftag to nbuf headroom\n",
+		       soc);
+		qdf_assert_always(0);
+	}
 
-	/* Return if it dint came from mon Path */
-	if (!qdf_nbuf_get_nr_frags(nbuf))
+	if (!wlan_cfg_is_rx_mon_protocol_flow_tag_enabled(soc->wlan_cfg_ctx))
 		return;
 
-	is_mon_protocol_flow_tag_enabled =
-		wlan_cfg_is_rx_mon_protocol_flow_tag_enabled(soc->wlan_cfg_ctx);
-
-	if (qdf_unlikely(!is_mon_protocol_flow_tag_enabled))
+	if (qdf_unlikely(!nbuf))
 		return;
 
-	if (qdf_likely(!pdev->is_rx_protocol_tagging_enabled))
+	/* Return if it dint came from mon Path */
+	if (!qdf_nbuf_get_nr_frags(nbuf))
 		return;
 
-	if (qdf_unlikely(qdf_nbuf_headroom(nbuf) < DP_RX_MON_PF_TAG_TOT_LEN)) {
-		dp_err("Nbuf avail Headroom[%d] < DP_RX_MON_PF_TAG_TOT_LEN[%d]",
-		       qdf_nbuf_headroom(nbuf), DP_RX_MON_PF_TAG_TOT_LEN);
+	/* Headroom must be double of PF_TAG_SIZE as we copy it 1stly to head */
+	if (qdf_unlikely(qdf_nbuf_headroom(nbuf) < (DP_RX_MON_TOT_PF_TAG_LEN * 2))) {
+		dp_err("Nbuf avail Headroom[%d] < 2 * DP_RX_MON_PF_TAG_TOT_LEN[%d]",
+		       qdf_nbuf_headroom(nbuf), DP_RX_MON_TOT_PF_TAG_LEN);
 		return;
 	}
 
-	qdf_nbuf_push_head(nbuf, DP_RX_MON_PF_TAG_TOT_LEN);
+	qdf_nbuf_push_head(nbuf, DP_RX_MON_TOT_PF_TAG_LEN);
 	qdf_mem_copy(qdf_nbuf_data(nbuf), qdf_nbuf_head(nbuf),
-		     DP_RX_MON_PF_TAG_TOT_LEN);
-	qdf_nbuf_pull_head(nbuf, DP_RX_MON_PF_TAG_TOT_LEN);
+		     DP_RX_MON_TOT_PF_TAG_LEN);
+	qdf_nbuf_pull_head(nbuf, DP_RX_MON_TOT_PF_TAG_LEN);
+
+	ext_list = qdf_nbuf_get_ext_list(nbuf);
+	while (ext_list) {
+		/* Headroom must be double of PF_TAG_SIZE as we copy it 1stly to head */
+		if (qdf_unlikely(qdf_nbuf_headroom(ext_list) < (DP_RX_MON_TOT_PF_TAG_LEN * 2))) {
+			dp_err("Fraglist Nbuf avail Headroom[%d] < 2 * DP_RX_MON_PF_TAG_TOT_LEN[%d]",
+			       qdf_nbuf_headroom(ext_list), DP_RX_MON_TOT_PF_TAG_LEN);
+			ext_list = qdf_nbuf_queue_next(ext_list);
+			continue;
+		}
+		qdf_nbuf_push_head(ext_list, DP_RX_MON_TOT_PF_TAG_LEN);
+		qdf_mem_copy(qdf_nbuf_data(ext_list), qdf_nbuf_head(ext_list),
+			     DP_RX_MON_TOT_PF_TAG_LEN);
+		qdf_nbuf_pull_head(ext_list, DP_RX_MON_TOT_PF_TAG_LEN);
+		ext_list = qdf_nbuf_queue_next(ext_list);
+	}
 }
 #else
 static inline
 void dp_rx_mon_update_pf_tag_to_buf_headroom(struct dp_soc *soc,
-					     struct dp_pdev *pdev,
 					     qdf_nbuf_t nbuf)
 {
 }
@@ -1198,7 +1290,6 @@ void dp_rx_mon_update_pf_tag_to_buf_headroom(struct dp_soc *soc,
 #else
 static inline
 void dp_rx_mon_update_pf_tag_to_buf_headroom(struct dp_soc *soc,
-					     struct dp_pdev *pdev,
 					     qdf_nbuf_t nbuf)
 {
 }
@@ -1252,8 +1343,6 @@ static inline QDF_STATUS dp_send_mgmt_packet_to_stack(struct dp_soc *soc,
 	}
 	*nbuf_data = pdev->ppdu_info.com_info.ppdu_id;
 
-	dp_rx_mon_update_pf_tag_to_buf_headroom(soc, pdev, nbuf);
-
 	dp_wdi_event_handler(WDI_EVENT_RX_MGMT_CTRL, soc, nbuf,
 			     HTT_INVALID_PEER,
 			     WDI_NO_VAL, pdev->pdev_id);
@@ -1304,7 +1393,8 @@ qdf_nbuf_t dp_rx_mon_restitch_mpdu(struct dp_soc *soc, uint32_t mac_id,
 {
 	if (qdf_nbuf_get_nr_frags(head_msdu))
 		return dp_rx_mon_frag_restitch_mpdu_from_msdus(soc, mac_id,
-							       head_msdu, rs);
+							       head_msdu,
+							       tail_msdu, rs);
 	else
 		return dp_rx_mon_restitch_mpdu_from_msdus(soc, mac_id,
 							  head_msdu,
@@ -1368,7 +1458,7 @@ QDF_STATUS dp_rx_mon_deliver(struct dp_soc *soc, uint32_t mac_id,
 			goto mon_deliver_fail;
 		}
 
-		dp_rx_mon_update_pf_tag_to_buf_headroom(soc, pdev, head_msdu);
+		dp_rx_mon_update_pf_tag_to_buf_headroom(soc, mon_mpdu);
 		pdev->monitor_vdev->osif_rx_mon(pdev->monitor_vdev->osif_vdev,
 						mon_mpdu,
 						&pdev->ppdu_info.rx_status);
@@ -1558,8 +1648,7 @@ void dp_rx_mon_dest_process(struct dp_soc *soc, struct dp_intr *int_ctx,
 			break;
 		}
 
-		if (qdf_likely((head_msdu) &&
-			       (DP_RX_MON_IS_MSDU_NOT_NULL(tail_msdu)))) {
+		if (qdf_likely((head_msdu) && (tail_msdu))) {
 			rx_mon_stats->dest_mpdu_done++;
 			dp_rx_mon_deliver(soc, mac_id, head_msdu, tail_msdu);
 		}