qcacmn: Check for tx desc leak or corruption

Tx descriptor is not being freed and put back in tx desc
pool for a long time. As a result, system is not going
into suspended state.
To find this descriptor, during dp_bus_suspend and
dp_runtime_suspend, parse through the descriptor pool to
check for any descriptors not freed for a long and trigger
self recovery when found.

Change-Id: Id97c5c8537c9bec922f4e254b5bf094505ee61ff
CRs-Fixed: 3109868
This commit is contained in:
Ananya Gupta
2022-01-14 08:20:37 +05:30
committed by Madan Koyyalamudi
parent d3e9c9ca97
commit 940984c6a6
8 changed files with 169 additions and 6 deletions

View File

@@ -12039,6 +12039,91 @@ void dp_flush_ring_hptp(struct dp_soc *soc, hal_ring_handle_t hal_srng)
}
#endif
#ifdef DP_TX_TRACKING
#define DP_TX_COMP_MAX_LATENCY_MS 120000
/**
* dp_tx_comp_delay() - calculate time latency for tx completion per pkt
* @timestamp - tx descriptor timestamp
*
* Calculate time latency for tx completion per pkt and trigger self recovery
* when the delay is more than threshold value.
*
* Return: None.
*/
static void dp_tx_comp_delay(uint64_t timestamp)
{
uint64_t time_latency;
if (dp_tx_pkt_tracepoints_enabled())
time_latency = qdf_ktime_to_ms(qdf_ktime_real_get()) -
timestamp;
else
time_latency = qdf_system_ticks_to_msecs(qdf_system_ticks() -
timestamp);
if (time_latency >= DP_TX_COMP_MAX_LATENCY_MS) {
dp_err_rl("tx completion not rcvd for %llu ms.", time_latency);
qdf_trigger_self_recovery(NULL, QDF_TX_DESC_LEAK);
}
}
/**
* dp_find_missing_tx_comp() - check for leaked descriptor in tx path
* @soc - DP SOC context
*
* Parse through descriptors in all pools and validate magic number and
* completion time. Trigger self recovery if magic value is corrupted.
*
* Return: None.
*/
static void dp_find_missing_tx_comp(struct dp_soc *soc)
{
uint8_t i;
uint32_t j;
uint32_t num_desc, page_id, offset;
uint16_t num_desc_per_page;
struct dp_tx_desc_s *tx_desc = NULL;
struct dp_tx_desc_pool_s *tx_desc_pool = NULL;
for (i = 0; i < MAX_TXDESC_POOLS; i++) {
tx_desc_pool = &soc->tx_desc[i];
if (!(tx_desc_pool->pool_size) ||
IS_TX_DESC_POOL_STATUS_INACTIVE(tx_desc_pool) ||
!(tx_desc_pool->desc_pages.cacheable_pages))
continue;
num_desc = tx_desc_pool->pool_size;
num_desc_per_page =
tx_desc_pool->desc_pages.num_element_per_page;
for (j = 0; j < num_desc; j++) {
page_id = j / num_desc_per_page;
offset = j % num_desc_per_page;
if (qdf_unlikely(!(tx_desc_pool->
desc_pages.cacheable_pages)))
break;
tx_desc = dp_tx_desc_find(soc, i, page_id, offset);
if (tx_desc->magic == DP_TX_MAGIC_PATTERN_FREE) {
continue;
} else if (tx_desc->magic ==
DP_TX_MAGIC_PATTERN_INUSE) {
dp_tx_comp_delay(tx_desc->timestamp);
} else {
dp_err("tx desc %d corrupted", tx_desc->id);
qdf_trigger_self_recovery(NULL,
QDF_TX_DESC_LEAK);
}
}
}
}
#else
static inline void dp_find_missing_tx_comp(struct dp_soc *soc)
{
}
#endif
#ifdef FEATURE_RUNTIME_PM
/**
* dp_runtime_suspend() - ensure DP is ready to runtime suspend
@@ -12065,9 +12150,9 @@ static QDF_STATUS dp_runtime_suspend(struct cdp_soc_t *soc_hdl, uint8_t pdev_id)
/* Abort if there are any pending TX packets */
tx_pending = dp_get_tx_pending(dp_pdev_to_cdp_pdev(pdev));
if (tx_pending) {
dp_init_info("%pK: Abort suspend due to pending TX packets %d",
soc, tx_pending);
dp_info_rl("%pK: Abort suspend due to pending TX packets %d",
soc, tx_pending);
dp_find_missing_tx_comp(soc);
/* perform a force flush if tx is pending */
for (i = 0; i < soc->num_tcl_data_rings; i++) {
hal_srng_set_event(soc->tcl_data_ring[i].hal_srng,
@@ -12559,6 +12644,7 @@ static QDF_STATUS dp_bus_suspend(struct cdp_soc_t *soc_hdl, uint8_t pdev_id)
if (timeout <= 0) {
dp_info("TX frames are pending %d, abort suspend",
tx_pending);
dp_find_missing_tx_comp(soc);
return QDF_STATUS_E_TIMEOUT;
}
timeout = timeout - drain_wait_delay;