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
This commit is contained in:
Rakesh Pillai
2022-09-14 10:06:25 -07:00
committed by Madan Koyyalamudi
parent 9ab6f487c0
commit 27f5c5e7b8
3 changed files with 156 additions and 19 deletions

View File

@@ -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)
{

View File

@@ -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 */