qcacmn: Do not unmap buffer if MSDU done is not set

Currently if the msdu_done bit is not set in the
rx_pkt_tlvs, the buffer is unmapped and freed.
There was an issue seen where hardware accessed a
buffer address, which probably was delivered to driver
without DMA of data to the buffer (thereby unmapped
and freed before hw attempted second access). This
results in unwanted recovery.

Based on the above hypothesis, add a workaround
to skip the unmap-and-free of any buffer for which
the msdu_done bit was not set in the rx_pkt_tlv.
Also mark the corresponding rx_desc as in_err_state.

The rx_desc corresponding to such aforementioned
msdu_done failure msdus are stored in a rolling
list, where the old entry is force unmapped & freed
when a new entry needs to be added. This force
unmap-and-free of the oldest buffer gives an uncertain
amount of delay between it being reaped from rx ring and
actual unmap-and-free.

Change-Id: Ibf8f3c886118d532af818466cb46218d88146271
CRs-Fixed: 3667674
This commit is contained in:
Rakesh Pillai
2023-11-10 03:01:48 -08:00
committed by Ravindra Konda
parent 3ed7625c2a
commit d149d2a2e8
5 changed files with 161 additions and 6 deletions

View File

@@ -72,6 +72,7 @@ dp_rx_update_flow_info(qdf_nbuf_t nbuf, uint8_t *rx_tlv_hdr)
#ifdef DP_RX_MSDU_DONE_FAIL_HISTORY #ifdef DP_RX_MSDU_DONE_FAIL_HISTORY
static inline void static inline void
dp_rx_msdu_done_fail_event_record(struct dp_soc *soc, dp_rx_msdu_done_fail_event_record(struct dp_soc *soc,
struct dp_rx_desc *rx_desc,
qdf_nbuf_t nbuf) qdf_nbuf_t nbuf)
{ {
struct dp_msdu_done_fail_entry *entry; struct dp_msdu_done_fail_entry *entry;
@@ -84,10 +85,16 @@ dp_rx_msdu_done_fail_event_record(struct dp_soc *soc,
DP_MSDU_DONE_FAIL_HIST_MAX); DP_MSDU_DONE_FAIL_HIST_MAX);
entry = &soc->msdu_done_fail_hist->entry[idx]; entry = &soc->msdu_done_fail_hist->entry[idx];
entry->paddr = qdf_nbuf_get_frag_paddr(nbuf, 0); entry->paddr = qdf_nbuf_get_frag_paddr(nbuf, 0);
if (rx_desc)
entry->sw_cookie = rx_desc->cookie;
else
entry->sw_cookie = 0xDEAD;
} }
#else #else
static inline void static inline void
dp_rx_msdu_done_fail_event_record(struct dp_soc *soc, dp_rx_msdu_done_fail_event_record(struct dp_soc *soc,
struct dp_rx_desc *rx_desc,
qdf_nbuf_t nbuf) qdf_nbuf_t nbuf)
{ {
} }
@@ -192,6 +199,84 @@ dp_rx_wds_learn(struct dp_soc *soc,
} }
#endif #endif
#ifdef DP_RX_PEEK_MSDU_DONE_WAR
static inline int dp_rx_war_peek_msdu_done(struct dp_soc *soc,
struct dp_rx_desc *rx_desc)
{
uint8_t *rx_tlv_hdr;
qdf_nbuf_sync_for_cpu(soc->osdev, rx_desc->nbuf, QDF_DMA_FROM_DEVICE);
rx_tlv_hdr = qdf_nbuf_data(rx_desc->nbuf);
return hal_rx_tlv_msdu_done_get_be(rx_tlv_hdr);
}
/**
* dp_rx_delink_n_rel_rx_desc() - unmap & free the nbuf in the rx_desc
* @soc: DP SoC handle
* @rx_desc: rx_desc handle of the nbuf to be unmapped & freed
* @reo_ring_num: REO_RING_NUM corresponding to the REO for which the
* bottom half is being serviced.
*
* Return: None
*/
static inline void
dp_rx_delink_n_rel_rx_desc(struct dp_soc *soc, struct dp_rx_desc *rx_desc,
uint8_t reo_ring_num)
{
if (!rx_desc)
return;
dp_rx_nbuf_unmap(soc, rx_desc, reo_ring_num);
dp_rx_nbuf_free(rx_desc->nbuf);
/*
* RX_DESC flags:
* in_use = 0 will be set when this rx_desc is added to local freelist
* unmapped = 1 will be set by dp_rx_nbuf_unmap
* in_err_state = 0 will be set during replenish
* has_reuse_nbuf need not be touched.
* msdu_done_fail = 0 should be set here ..!!
*/
rx_desc->msdu_done_fail = 0;
}
static inline struct dp_rx_desc *
dp_rx_war_store_msdu_done_fail_desc(struct dp_soc *soc,
struct dp_rx_desc *rx_desc,
uint8_t reo_ring_num)
{
struct dp_rx_msdu_done_fail_desc_list *msdu_done_fail_desc_list =
&soc->msdu_done_fail_desc_list;
struct dp_rx_desc *old_rx_desc;
uint32_t idx;
idx = dp_get_next_index(&msdu_done_fail_desc_list->index,
DP_MSDU_DONE_FAIL_DESCS_MAX);
old_rx_desc = msdu_done_fail_desc_list->msdu_done_fail_descs[idx];
dp_rx_delink_n_rel_rx_desc(soc, old_rx_desc, reo_ring_num);
msdu_done_fail_desc_list->msdu_done_fail_descs[idx] = rx_desc;
return old_rx_desc;
}
#else
static inline int dp_rx_war_peek_msdu_done(struct dp_soc *soc,
struct dp_rx_desc *rx_desc)
{
return 1;
}
static inline struct dp_rx_desc *
dp_rx_war_store_msdu_done_fail_desc(struct dp_soc *soc,
struct dp_rx_desc *rx_desc,
uint8_t reo_ring_num)
{
return NULL;
}
#endif
uint32_t dp_rx_process_be(struct dp_intr *int_ctx, uint32_t dp_rx_process_be(struct dp_intr *int_ctx,
hal_ring_handle_t hal_ring_hdl, uint8_t reo_ring_num, hal_ring_handle_t hal_ring_hdl, uint8_t reo_ring_num,
uint32_t quota) uint32_t quota)
@@ -446,6 +531,29 @@ more_data:
} }
is_prev_msdu_last = false; is_prev_msdu_last = false;
} }
} else if (qdf_unlikely(!dp_rx_war_peek_msdu_done(soc,
rx_desc))) {
struct dp_rx_desc *old_rx_desc =
dp_rx_war_store_msdu_done_fail_desc(
soc, rx_desc,
reo_ring_num);
if (qdf_likely(old_rx_desc)) {
rx_bufs_reaped[rx_desc->chip_id][rx_desc->pool_id]++;
dp_rx_add_to_free_desc_list
(&head[rx_desc->chip_id][rx_desc->pool_id],
&tail[rx_desc->chip_id][rx_desc->pool_id],
old_rx_desc);
quota -= 1;
num_pending -= 1;
num_rx_bufs_reaped++;
}
rx_desc->msdu_done_fail = 1;
DP_STATS_INC(soc, rx.err.msdu_done_fail, 1);
dp_err("MSDU DONE failure %d",
soc->stats.rx.err.msdu_done_fail);
dp_rx_msdu_done_fail_event_record(soc, rx_desc,
rx_desc->nbuf);
continue;
} }
if (!is_prev_msdu_last && if (!is_prev_msdu_last &&
@@ -641,7 +749,7 @@ done:
soc->stats.rx.err.msdu_done_fail); soc->stats.rx.err.msdu_done_fail);
hal_rx_dump_pkt_tlvs(hal_soc, rx_tlv_hdr, hal_rx_dump_pkt_tlvs(hal_soc, rx_tlv_hdr,
QDF_TRACE_LEVEL_INFO); QDF_TRACE_LEVEL_INFO);
dp_rx_msdu_done_fail_event_record(soc, nbuf); dp_rx_msdu_done_fail_event_record(soc, NULL, nbuf);
tid_stats->fail_cnt[MSDU_DONE_FAILURE]++; tid_stats->fail_cnt[MSDU_DONE_FAILURE]++;
dp_rx_nbuf_free(nbuf); dp_rx_nbuf_free(nbuf);
qdf_assert(0); qdf_assert(0);

View File

@@ -4499,6 +4499,25 @@ void dp_set_max_page_size(struct qdf_mem_multi_page_t *pages,
} }
#endif /* MAX_ALLOC_PAGE_SIZE */ #endif /* MAX_ALLOC_PAGE_SIZE */
/**
* dp_get_next_index() - get the next entry to record an entry
* in the history.
* @curr_idx: Current index where the last entry is written.
* @max_entries: Max number of entries in the history
*
* This function assumes that the max number os entries is a power of 2.
*
* Return: The index where the next entry is to be written.
*/
static inline uint32_t dp_get_next_index(qdf_atomic_t *curr_idx,
uint32_t max_entries)
{
uint32_t idx = qdf_atomic_inc_return(curr_idx);
return idx & (max_entries - 1);
}
/** /**
* dp_history_get_next_index() - get the next entry to record an entry * dp_history_get_next_index() - get the next entry to record an entry
* in the history. * in the history.
@@ -4512,9 +4531,7 @@ void dp_set_max_page_size(struct qdf_mem_multi_page_t *pages,
static inline uint32_t dp_history_get_next_index(qdf_atomic_t *curr_idx, static inline uint32_t dp_history_get_next_index(qdf_atomic_t *curr_idx,
uint32_t max_entries) uint32_t max_entries)
{ {
uint32_t idx = qdf_atomic_inc_return(curr_idx); return dp_get_next_index(curr_idx, max_entries);
return idx & (max_entries - 1);
} }
/** /**

View File

@@ -3493,6 +3493,19 @@ static inline void dp_soc_msdu_done_fail_history_detach(struct dp_soc *soc)
} }
#endif #endif
#ifdef DP_RX_PEEK_MSDU_DONE_WAR
static void dp_soc_msdu_done_fail_desc_list_attach(struct dp_soc *soc)
{
qdf_atomic_init(&soc->msdu_done_fail_desc_list.index);
qdf_atomic_set(&soc->msdu_done_fail_desc_list.index,
DP_MSDU_DONE_FAIL_DESCS_MAX - 1);
}
#else
static void dp_soc_msdu_done_fail_desc_list_attach(struct dp_soc *soc)
{
}
#endif
#ifdef WLAN_SUPPORT_RX_FLOW_TAG #ifdef WLAN_SUPPORT_RX_FLOW_TAG
QDF_STATUS QDF_STATUS
dp_rx_fst_attach_wrapper(struct dp_soc *soc, struct dp_pdev *pdev) dp_rx_fst_attach_wrapper(struct dp_soc *soc, struct dp_pdev *pdev)
@@ -13675,6 +13688,7 @@ dp_soc_attach(struct cdp_ctrl_objmgr_psoc *ctrl_psoc,
dp_soc_rx_history_attach(soc); dp_soc_rx_history_attach(soc);
dp_soc_mon_status_ring_history_attach(soc); dp_soc_mon_status_ring_history_attach(soc);
dp_soc_tx_history_attach(soc); dp_soc_tx_history_attach(soc);
dp_soc_msdu_done_fail_desc_list_attach(soc);
dp_soc_msdu_done_fail_history_attach(soc); dp_soc_msdu_done_fail_history_attach(soc);
wlan_set_srng_cfg(&soc->wlan_srng_cfg); wlan_set_srng_cfg(&soc->wlan_srng_cfg);
soc->wlan_cfg_ctx = wlan_cfg_soc_attach(soc->ctrl_psoc); soc->wlan_cfg_ctx = wlan_cfg_soc_attach(soc->ctrl_psoc);

View File

@@ -138,6 +138,8 @@ struct dp_rx_desc_dbg_info {
* @in_err_state: Nbuf sanity failed for this descriptor. * @in_err_state: Nbuf sanity failed for this descriptor.
* @has_reuse_nbuf: the nbuf associated with this desc is also saved in * @has_reuse_nbuf: the nbuf associated with this desc is also saved in
* reuse_nbuf field * reuse_nbuf field
* @msdu_done_fail: this particular rx_desc was dequeued from REO with
* msdu_done bit not set in data buffer.
*/ */
struct dp_rx_desc { struct dp_rx_desc {
qdf_nbuf_t nbuf; qdf_nbuf_t nbuf;
@@ -158,7 +160,8 @@ struct dp_rx_desc {
uint8_t in_use:1, uint8_t in_use:1,
unmapped:1, unmapped:1,
in_err_state:1, in_err_state:1,
has_reuse_nbuf:1; has_reuse_nbuf:1,
msdu_done_fail:1;
}; };
#ifndef QCA_HOST_MODE_WIFI_DISABLED #ifndef QCA_HOST_MODE_WIFI_DISABLED

View File

@@ -2645,10 +2645,11 @@ struct test_qaddr_del {
#ifdef DP_RX_MSDU_DONE_FAIL_HISTORY #ifdef DP_RX_MSDU_DONE_FAIL_HISTORY
#define DP_MSDU_DONE_FAIL_HIST_MAX 32 #define DP_MSDU_DONE_FAIL_HIST_MAX 128
struct dp_msdu_done_fail_entry { struct dp_msdu_done_fail_entry {
qdf_dma_addr_t paddr; qdf_dma_addr_t paddr;
uint32_t sw_cookie;
}; };
struct dp_msdu_done_fail_history { struct dp_msdu_done_fail_history {
@@ -2657,6 +2658,15 @@ struct dp_msdu_done_fail_history {
}; };
#endif #endif
#ifdef DP_RX_PEEK_MSDU_DONE_WAR
#define DP_MSDU_DONE_FAIL_DESCS_MAX 64
struct dp_rx_msdu_done_fail_desc_list {
qdf_atomic_t index;
struct dp_rx_desc *msdu_done_fail_descs[DP_MSDU_DONE_FAIL_DESCS_MAX];
};
#endif
/* SOC level structure for data path */ /* SOC level structure for data path */
struct dp_soc { struct dp_soc {
/** /**
@@ -3229,6 +3239,9 @@ struct dp_soc {
#ifdef DP_RX_MSDU_DONE_FAIL_HISTORY #ifdef DP_RX_MSDU_DONE_FAIL_HISTORY
struct dp_msdu_done_fail_history *msdu_done_fail_hist; struct dp_msdu_done_fail_history *msdu_done_fail_hist;
#endif #endif
#ifdef DP_RX_PEEK_MSDU_DONE_WAR
struct dp_rx_msdu_done_fail_desc_list msdu_done_fail_desc_list;
#endif
}; };
#ifdef IPA_OFFLOAD #ifdef IPA_OFFLOAD