From 27f5c5e7b8a414eb09c065d21d5dcb2a07e6c543 Mon Sep 17 00:00:00 2001 From: Rakesh Pillai Date: Wed, 14 Sep 2022 10:06:25 -0700 Subject: [PATCH] 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 --- dp/wifi3.0/monitor/1.0/dp_rx_mon_1.0.h | 80 +++++++++++++++++- dp/wifi3.0/monitor/1.0/dp_rx_mon_dest_1.0.c | 91 +++++++++++++++++---- hal/wifi3.0/hal_rx.h | 4 +- 3 files changed, 156 insertions(+), 19 deletions(-) diff --git a/dp/wifi3.0/monitor/1.0/dp_rx_mon_1.0.h b/dp/wifi3.0/monitor/1.0/dp_rx_mon_1.0.h index f4523448d1..f3674da4c6 100644 --- a/dp/wifi3.0/monitor/1.0/dp_rx_mon_1.0.h +++ b/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) { diff --git a/dp/wifi3.0/monitor/1.0/dp_rx_mon_dest_1.0.c b/dp/wifi3.0/monitor/1.0/dp_rx_mon_dest_1.0.c index 5ad560ac6a..60dec70c43 100644 --- a/dp/wifi3.0/monitor/1.0/dp_rx_mon_dest_1.0.c +++ b/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 */ diff --git a/hal/wifi3.0/hal_rx.h b/hal/wifi3.0/hal_rx.h index 7035ad252b..b037caf9a8 100644 --- a/hal/wifi3.0/hal_rx.h +++ b/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; }; /**