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:

committed by
Madan Koyyalamudi

parent
d3e9c9ca97
commit
940984c6a6
@@ -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;
|
||||
|
Reference in New Issue
Block a user