qcacmn: Add support to enable tx hw latency stats at run time

This feature can be enabled runtime using sysfs interface.
Support is added to dump and clear the histogram stats.

The lower delay regions has to be more granular to indicate any
medium related issues for time sensitive XR applications.

Change-Id: I0a44a54d12d92ce016de349810cb2bedebaf9a58
CRs-Fixed: 2981006
This commit is contained in:
Yeshwanth Sriram Guntuka
2021-11-15 21:41:57 +05:30
committed by Madan Koyyalamudi
parent 3de51ff2c5
commit df666b7116
13 changed files with 515 additions and 103 deletions

View File

@@ -10979,6 +10979,7 @@ static QDF_STATUS dp_txrx_dump_stats(struct cdp_soc_t *psoc, uint16_t value,
dp_txrx_path_stats(soc);
dp_print_soc_interrupt_stats(soc);
hal_dump_reg_write_stats(soc->hal_soc);
dp_pdev_print_tx_delay_stats(soc);
break;
case CDP_RX_RING_STATS:
@@ -11012,6 +11013,10 @@ static QDF_STATUS dp_txrx_dump_stats(struct cdp_soc_t *psoc, uint16_t value,
dp_print_swlm_stats(soc);
break;
case CDP_DP_TX_HW_LATENCY_STATS:
dp_pdev_print_tx_delay_stats(soc);
break;
default:
status = QDF_STATUS_E_INVAL;
break;
@@ -11250,6 +11255,10 @@ QDF_STATUS dp_txrx_clear_dump_stats(struct cdp_soc_t *soc_hdl, uint8_t pdev_id,
dp_txrx_clear_tso_stats(soc);
break;
case CDP_DP_TX_HW_LATENCY_STATS:
dp_pdev_clear_tx_delay_stats(soc);
break;
default:
status = QDF_STATUS_E_INVAL;
break;
@@ -12315,6 +12324,58 @@ dp_set_pkt_capture_mode(struct cdp_soc_t *soc_handle, bool val)
}
#endif
#ifdef HW_TX_DELAY_STATS_ENABLE
/**
* dp_enable_disable_vdev_tx_delay_stats(): Start/Stop tx delay stats capture
* @soc: DP soc handle
* @vdev_id: vdev id
* @value: value
*
* Return: None
*/
static void
dp_enable_disable_vdev_tx_delay_stats(struct cdp_soc_t *soc_hdl,
uint8_t vdev_id,
uint8_t value)
{
struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
struct dp_vdev *vdev = NULL;
vdev = dp_vdev_get_ref_by_id(soc, vdev_id, DP_MOD_ID_CDP);
if (!vdev)
return;
vdev->hw_tx_delay_stats_enabled = value;
dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_CDP);
}
/**
* dp_check_vdev_tx_delay_stats_enabled() - check the feature is enabled or not
* @soc: DP soc handle
* @vdev_id: vdev id
*
* Returns: 1 if enabled, 0 if disabled
*/
static uint8_t
dp_check_vdev_tx_delay_stats_enabled(struct cdp_soc_t *soc_hdl,
uint8_t vdev_id)
{
struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
struct dp_vdev *vdev;
uint8_t ret_val = 0;
vdev = dp_vdev_get_ref_by_id(soc, vdev_id, DP_MOD_ID_CDP);
if (!vdev)
return ret_val;
ret_val = vdev->hw_tx_delay_stats_enabled;
dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_CDP);
return ret_val;
}
#endif
static struct cdp_cmn_ops dp_ops_cmn = {
.txrx_soc_attach_target = dp_soc_attach_target_wifi3,
.txrx_vdev_attach = dp_vdev_attach_wifi3,
@@ -12526,6 +12587,11 @@ static struct cdp_host_stats_ops dp_ops_host_stats = {
.get_peer_tx_capture_stats = dp_peer_get_tx_capture_stats,
.get_pdev_tx_capture_stats = dp_pdev_get_tx_capture_stats,
#endif /* WLAN_TX_PKT_CAPTURE_ENH */
#ifdef HW_TX_DELAY_STATS_ENABLE
.enable_disable_vdev_tx_delay_stats =
dp_enable_disable_vdev_tx_delay_stats,
.is_tx_delay_stats_enabled = dp_check_vdev_tx_delay_stats_enabled,
#endif
/* TODO */
};
@@ -14062,11 +14128,37 @@ static uint8_t dp_bucket_index(uint32_t delay, uint16_t *array)
return (CDP_DELAY_BUCKET_MAX - 1);
}
#ifdef HW_TX_DELAY_STATS_ENABLE
/*
* cdp_fw_to_hw_delay_range
* Fw to hw delay ranges in milliseconds
*/
static uint16_t cdp_fw_to_hw_delay[CDP_DELAY_BUCKET_MAX] = {
0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 250, 500};
#else
static uint16_t cdp_fw_to_hw_delay[CDP_DELAY_BUCKET_MAX] = {
0, 2, 4, 6, 8, 10, 20, 30, 40, 50, 100, 250, 500};
#endif
/*
* cdp_sw_enq_delay_range
* Software enqueue delay ranges in milliseconds
*/
static uint16_t cdp_sw_enq_delay[CDP_DELAY_BUCKET_MAX] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
/*
* cdp_intfrm_delay_range
* Interframe delay ranges in milliseconds
*/
static uint16_t cdp_intfrm_delay[CDP_DELAY_BUCKET_MAX] = {
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60};
/**
* dp_fill_delay_buckets() - Fill delay statistics bucket for each
* type of delay
*
* @pdev: pdev handle
* @tstats: tid tx stats
* @rstats: tid rx stats
* @delay: delay in ms
* @tid: tid value
* @mode: type of tx delay mode
@@ -14074,34 +14166,12 @@ static uint8_t dp_bucket_index(uint32_t delay, uint16_t *array)
* Return: pointer to cdp_delay_stats structure
*/
static struct cdp_delay_stats *
dp_fill_delay_buckets(struct dp_pdev *pdev, uint32_t delay,
dp_fill_delay_buckets(struct cdp_tid_tx_stats *tstats,
struct cdp_tid_rx_stats *rstats, uint32_t delay,
uint8_t tid, uint8_t mode, uint8_t ring_id)
{
uint8_t delay_index = 0;
struct cdp_tid_tx_stats *tstats =
&pdev->stats.tid_stats.tid_tx_stats[ring_id][tid];
struct cdp_tid_rx_stats *rstats =
&pdev->stats.tid_stats.tid_rx_stats[ring_id][tid];
/*
* cdp_fw_to_hw_delay_range
* Fw to hw delay ranges in milliseconds
*/
uint16_t cdp_fw_to_hw_delay[CDP_DELAY_BUCKET_MAX] = {
0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 250, 500};
/*
* cdp_sw_enq_delay_range
* Software enqueue delay ranges in milliseconds
*/
uint16_t cdp_sw_enq_delay[CDP_DELAY_BUCKET_MAX] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
/*
* cdp_intfrm_delay_range
* Interframe delay ranges in milliseconds
*/
uint16_t cdp_intfrm_delay[CDP_DELAY_BUCKET_MAX] = {
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60};
struct cdp_delay_stats *stats = NULL;
/*
* Update delay stats in proper bucket
@@ -14109,57 +14179,62 @@ dp_fill_delay_buckets(struct dp_pdev *pdev, uint32_t delay,
switch (mode) {
/* Software Enqueue delay ranges */
case CDP_DELAY_STATS_SW_ENQ:
if (!tstats)
break;
delay_index = dp_bucket_index(delay, cdp_sw_enq_delay);
tstats->swq_delay.delay_bucket[delay_index]++;
return &tstats->swq_delay;
stats = &tstats->swq_delay;
break;
/* Tx Completion delay ranges */
case CDP_DELAY_STATS_FW_HW_TRANSMIT:
if (!tstats)
break;
delay_index = dp_bucket_index(delay, cdp_fw_to_hw_delay);
tstats->hwtx_delay.delay_bucket[delay_index]++;
return &tstats->hwtx_delay;
stats = &tstats->hwtx_delay;
break;
/* Interframe tx delay ranges */
case CDP_DELAY_STATS_TX_INTERFRAME:
if (!tstats)
break;
delay_index = dp_bucket_index(delay, cdp_intfrm_delay);
tstats->intfrm_delay.delay_bucket[delay_index]++;
return &tstats->intfrm_delay;
stats = &tstats->intfrm_delay;
break;
/* Interframe rx delay ranges */
case CDP_DELAY_STATS_RX_INTERFRAME:
if (!rstats)
break;
delay_index = dp_bucket_index(delay, cdp_intfrm_delay);
rstats->intfrm_delay.delay_bucket[delay_index]++;
return &rstats->intfrm_delay;
stats = &rstats->intfrm_delay;
break;
/* Ring reap to indication to network stack */
case CDP_DELAY_STATS_REAP_STACK:
if (!rstats)
break;
delay_index = dp_bucket_index(delay, cdp_intfrm_delay);
rstats->to_stack_delay.delay_bucket[delay_index]++;
return &rstats->to_stack_delay;
stats = &rstats->to_stack_delay;
break;
default:
dp_debug("Incorrect delay mode: %d", mode);
}
return NULL;
return stats;
}
/**
* dp_update_delay_stats() - Update delay statistics in structure
* and fill min, max and avg delay
*
* @pdev: pdev handle
* @delay: delay in ms
* @tid: tid value
* @mode: type of tx delay mode
* @ring id: ring number
* Return: none
*/
void dp_update_delay_stats(struct dp_pdev *pdev, uint32_t delay,
void dp_update_delay_stats(struct cdp_tid_tx_stats *tstats,
struct cdp_tid_rx_stats *rstats, uint32_t delay,
uint8_t tid, uint8_t mode, uint8_t ring_id)
{
struct cdp_delay_stats *dstats = NULL;
@@ -14168,7 +14243,8 @@ void dp_update_delay_stats(struct dp_pdev *pdev, uint32_t delay,
* Delay ranges are different for different delay modes
* Get the correct index to update delay bucket
*/
dstats = dp_fill_delay_buckets(pdev, delay, tid, mode, ring_id);
dstats = dp_fill_delay_buckets(tstats, rstats, delay, tid, mode,
ring_id);
if (qdf_unlikely(!dstats))
return;
@@ -14189,7 +14265,7 @@ void dp_update_delay_stats(struct dp_pdev *pdev, uint32_t delay,
if (!dstats->avg_delay)
dstats->avg_delay = delay;
else
dstats->avg_delay = ((delay + dstats->avg_delay) / 2);
dstats->avg_delay = ((delay + dstats->avg_delay) >> 1);
}
}