qcacmn: HIF schedule latency measurement

Add support to measure interrupt latencies, store them
in different "bins" and display the constructed histogram
through user command.

Change-Id: I268be6b39745d550ac7db5d321c2d1fdb2d17d41
CRs-Fixed: 2365812
This commit is contained in:
Pamidipati, Vijay
2018-12-31 14:46:14 +05:30
committed by nshrivas
parent 6d22eeb468
commit 37d107d2d1
8 changed files with 247 additions and 18 deletions

View File

@@ -199,6 +199,7 @@ enum htt_cmn_dbg_stats_type {
* @TXRX_REO_QUEUE_STATS: Print Per peer REO Queue Stats * @TXRX_REO_QUEUE_STATS: Print Per peer REO Queue Stats
* @TXRX_SOC_CFG_PARAMS: Print soc cfg params info * @TXRX_SOC_CFG_PARAMS: Print soc cfg params info
* @TXRX_PDEV_CFG_PARAMS: Print pdev cfg params info * @TXRX_PDEV_CFG_PARAMS: Print pdev cfg params info
* @TXRX_NAPI_STATS: Print NAPI scheduling statistics
*/ */
enum cdp_host_txrx_stats { enum cdp_host_txrx_stats {
TXRX_HOST_STATS_INVALID = -1, TXRX_HOST_STATS_INVALID = -1,
@@ -213,6 +214,7 @@ enum cdp_host_txrx_stats {
TXRX_REO_QUEUE_STATS = 8, TXRX_REO_QUEUE_STATS = 8,
TXRX_SOC_CFG_PARAMS = 9, TXRX_SOC_CFG_PARAMS = 9,
TXRX_PDEV_CFG_PARAMS = 10, TXRX_PDEV_CFG_PARAMS = 10,
TXRX_NAPI_STATS = 11,
TXRX_HOST_STATS_MAX, TXRX_HOST_STATS_MAX,
}; };

View File

@@ -1447,7 +1447,6 @@ static uint32_t dp_service_srngs(void *dp_ctx, uint32_t dp_budget)
for (mac_id = 0; mac_id < NUM_RXDMA_RINGS_PER_PDEV; mac_id++) { for (mac_id = 0; mac_id < NUM_RXDMA_RINGS_PER_PDEV; mac_id++) {
int mac_for_pdev = dp_get_mac_id_for_pdev(mac_id, int mac_for_pdev = dp_get_mac_id_for_pdev(mac_id,
pdev->pdev_id); pdev->pdev_id);
if (int_ctx->rx_mon_ring_mask & (1 << mac_for_pdev)) { if (int_ctx->rx_mon_ring_mask & (1 << mac_for_pdev)) {
work_done = dp_mon_process(soc, mac_for_pdev, work_done = dp_mon_process(soc, mac_for_pdev,
remaining_quota); remaining_quota);
@@ -6981,6 +6980,15 @@ dp_print_ring_stat_from_hal(struct dp_soc *soc, struct dp_srng *srng,
} }
/*
* dp_print_napi_stats(): NAPI stats
* @soc - soc handle
*/
static void dp_print_napi_stats(struct dp_soc *soc)
{
hif_print_napi_stats(soc->hif_handle);
}
/** /**
* dp_print_mon_ring_stats_from_hal() - Print stat for monitor rings based * dp_print_mon_ring_stats_from_hal() - Print stat for monitor rings based
* on target * on target
@@ -7079,7 +7087,6 @@ dp_print_ring_stats(struct dp_pdev *pdev)
dp_print_ring_stat_from_hal(pdev->soc, dp_print_ring_stat_from_hal(pdev->soc,
&pdev->rxdma_err_dst_ring[i], &pdev->rxdma_err_dst_ring[i],
RXDMA_DST); RXDMA_DST);
} }
/** /**
@@ -7099,6 +7106,9 @@ dp_txrx_host_stats_clr(struct dp_vdev *vdev)
DP_STATS_CLR(vdev->pdev); DP_STATS_CLR(vdev->pdev);
DP_STATS_CLR(vdev->pdev->soc); DP_STATS_CLR(vdev->pdev->soc);
DP_STATS_CLR(vdev); DP_STATS_CLR(vdev);
hif_clear_napi_stats(vdev->pdev->soc->hif_handle);
TAILQ_FOREACH(peer, &vdev->peer_list, peer_list_elem) { TAILQ_FOREACH(peer, &vdev->peer_list, peer_list_elem) {
if (!peer) if (!peer)
return; return;
@@ -7856,6 +7866,9 @@ dp_print_host_stats(struct cdp_vdev *vdev_handle,
case TXRX_PDEV_CFG_PARAMS: case TXRX_PDEV_CFG_PARAMS:
dp_print_pdev_cfg_params(pdev); dp_print_pdev_cfg_params(pdev);
break; break;
case TXRX_NAPI_STATS:
dp_print_napi_stats(pdev->soc);
break;
default: default:
dp_info("Wrong Input For TxRx Host Stats"); dp_info("Wrong Input For TxRx Host Stats");
dp_txrx_stats_help(); dp_txrx_stats_help();
@@ -8762,15 +8775,6 @@ QDF_STATUS dp_txrx_stats_request(struct cdp_vdev *vdev,
return QDF_STATUS_SUCCESS; return QDF_STATUS_SUCCESS;
} }
/*
* dp_print_napi_stats(): NAPI stats
* @soc - soc handle
*/
static void dp_print_napi_stats(struct dp_soc *soc)
{
hif_print_napi_stats(soc->hif_handle);
}
/* /*
* dp_print_per_ring_stats(): Packet count per ring * dp_print_per_ring_stats(): Packet count per ring
* @soc - soc handle * @soc - soc handle

View File

@@ -929,6 +929,15 @@ void hif_update_pipe_callback(struct hif_opaque_softc *osc,
struct hif_msg_callbacks *callbacks); struct hif_msg_callbacks *callbacks);
void hif_print_napi_stats(struct hif_opaque_softc *hif_ctx); void hif_print_napi_stats(struct hif_opaque_softc *hif_ctx);
/* hif_clear_napi_stats() - function clears the stats of the
* latency when called.
* @hif_ctx - the HIF context to assign the callback to
*
* Return: None
*/
void hif_clear_napi_stats(struct hif_opaque_softc *hif_ctx);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016-2018 The Linux Foundation. All rights reserved. * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
* *
* Permission to use, copy, modify, and/or distribute this software for * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * any purpose with or without fee is hereby granted, provided that the
@@ -52,4 +52,5 @@ void hif_pci_display_stats(struct hif_softc *hif_ctx);
void hif_pci_clear_stats(struct hif_softc *hif_ctx); void hif_pci_clear_stats(struct hif_softc *hif_ctx);
int hif_pci_legacy_map_ce_to_irq(struct hif_softc *scn, int ce_id); int hif_pci_legacy_map_ce_to_irq(struct hif_softc *scn, int ce_id);
bool hif_pci_needs_bmi(struct hif_softc *scn); bool hif_pci_needs_bmi(struct hif_softc *scn);
const char *hif_pci_get_irq_name(int irq_no);
#endif /* _PCI_API_H_ */ #endif /* _PCI_API_H_ */

View File

@@ -33,6 +33,99 @@
static struct hif_exec_context *hif_exec_tasklet_create(void); static struct hif_exec_context *hif_exec_tasklet_create(void);
/**
* hif_clear_napi_stats() - reset NAPI stats
* @hif_ctx: hif context
*
* return: void
*/
void hif_clear_napi_stats(struct hif_opaque_softc *hif_ctx)
{
struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx);
struct hif_exec_context *hif_ext_group;
size_t i;
for (i = 0; i < hif_state->hif_num_extgroup; i++) {
hif_ext_group = hif_state->hif_ext_group[i];
if (!hif_ext_group)
return;
qdf_mem_set(hif_ext_group->sched_latency_stats,
sizeof(hif_ext_group->sched_latency_stats),
0x0);
}
}
qdf_export_symbol(hif_clear_napi_stats);
/**
* hif_print_napi_latency_stats() - print NAPI scheduling latency stats
* @hif_state: hif context
*
* return: void
*/
#ifdef HIF_LATENCY_PROFILE_ENABLE
static void hif_print_napi_latency_stats(struct HIF_CE_state *hif_state)
{
struct hif_exec_context *hif_ext_group;
int i, j;
int64_t cur_tstamp;
const char time_str[HIF_SCHED_LATENCY_BUCKETS][15] = {
"0-2 ms",
"3-10 ms",
"11-20 ms",
"21-50 ms",
"51-100 ms",
"101-250 ms",
"251-500 ms",
"> 500 ms"
};
cur_tstamp = qdf_ktime_to_ms(qdf_ktime_get());
QDF_TRACE(QDF_MODULE_ID_HIF, QDF_TRACE_LEVEL_FATAL,
"Current timestamp: %lld", cur_tstamp);
for (i = 0; i < hif_state->hif_num_extgroup; i++) {
if (hif_state->hif_ext_group[i]) {
hif_ext_group = hif_state->hif_ext_group[i];
QDF_TRACE(QDF_MODULE_ID_HIF, QDF_TRACE_LEVEL_FATAL,
"Interrupts in the HIF Group");
for (j = 0; j < hif_ext_group->numirq; j++) {
QDF_TRACE(QDF_MODULE_ID_HIF,
QDF_TRACE_LEVEL_FATAL,
" %s",
hif_ext_group->irq_name
(hif_ext_group->irq[j]));
}
QDF_TRACE(QDF_MODULE_ID_HIF, QDF_TRACE_LEVEL_FATAL,
"Last serviced timestamp: %lld",
hif_ext_group->tstamp);
QDF_TRACE(QDF_MODULE_ID_HIF, QDF_TRACE_LEVEL_FATAL,
"Latency Bucket | Time elapsed");
for (j = 0; j < HIF_SCHED_LATENCY_BUCKETS; j++) {
QDF_TRACE(QDF_MODULE_ID_HIF,
QDF_TRACE_LEVEL_FATAL,
"%s | %lld", time_str[j],
hif_ext_group->
sched_latency_stats[j]);
}
}
}
}
#else
static void hif_print_napi_latency_stats(struct HIF_CE_state *hif_state)
{
}
#endif
/** /**
* hif_print_napi_stats() - print NAPI stats * hif_print_napi_stats() - print NAPI stats
* @hif_ctx: hif context * @hif_ctx: hif context
@@ -67,6 +160,8 @@ void hif_print_napi_stats(struct hif_opaque_softc *hif_ctx)
} }
} }
} }
hif_print_napi_latency_stats(hif_state);
} }
qdf_export_symbol(hif_print_napi_stats); qdf_export_symbol(hif_print_napi_stats);
@@ -102,10 +197,72 @@ static void hif_exec_tasklet_fn(unsigned long data)
} }
/** /**
* hif_exec_poll() - grp tasklet * hif_latency_profile_measure() - calculate latency and update histogram
* data: context * hif_ext_group: hif exec context
* *
* return: void * return: None
*/
#ifdef HIF_LATENCY_PROFILE_ENABLE
static void hif_latency_profile_measure(struct hif_exec_context *hif_ext_group)
{
int64_t cur_tstamp;
int64_t time_elapsed;
cur_tstamp = qdf_ktime_to_ms(qdf_ktime_get());
if (cur_tstamp > hif_ext_group->tstamp)
time_elapsed = (cur_tstamp - hif_ext_group->tstamp);
else
time_elapsed = ~0x0 - (hif_ext_group->tstamp - cur_tstamp);
hif_ext_group->tstamp = cur_tstamp;
if (time_elapsed <= HIF_SCHED_LATENCY_BUCKET_0_2)
hif_ext_group->sched_latency_stats[0]++;
else if (time_elapsed <= HIF_SCHED_LATENCY_BUCKET_3_10)
hif_ext_group->sched_latency_stats[1]++;
else if (time_elapsed <= HIF_SCHED_LATENCY_BUCKET_11_20)
hif_ext_group->sched_latency_stats[2]++;
else if (time_elapsed <= HIF_SCHED_LATENCY_BUCKET_21_50)
hif_ext_group->sched_latency_stats[3]++;
else if (time_elapsed <= HIF_SCHED_LATENCY_BUCKET_51_100)
hif_ext_group->sched_latency_stats[4]++;
else if (time_elapsed <= HIF_SCHED_LATENCY_BUCKET_101_250)
hif_ext_group->sched_latency_stats[5]++;
else if (time_elapsed <= HIF_SCHED_LATENCY_BUCKET_251_500)
hif_ext_group->sched_latency_stats[6]++;
else
hif_ext_group->sched_latency_stats[7]++;
}
#else
static void hif_latency_profile_measure(struct hif_exec_context *hif_ext_group)
{
}
#endif
/**
* hif_latency_profile_start() - Update the start timestamp for HIF ext group
* hif_ext_group: hif exec context
*
* return: None
*/
#ifdef HIF_LATENCY_PROFILE_ENABLE
static void hif_latency_profile_start(struct hif_exec_context *hif_ext_group)
{
hif_ext_group->tstamp = qdf_ktime_to_ms(qdf_ktime_get());
}
#else
static void hif_latency_profile_start(struct hif_exec_context *hif_ext_group)
{
}
#endif
/**
* hif_exec_poll() - napi pool
* napi: napi struct
* budget: budget for napi
*
* return: mapping of internal budget to napi
*/ */
static int hif_exec_poll(struct napi_struct *napi, int budget) static int hif_exec_poll(struct napi_struct *napi, int budget)
{ {
@@ -120,6 +277,9 @@ static int hif_exec_poll(struct napi_struct *napi, int budget)
if (budget) if (budget)
normalized_budget = NAPI_BUDGET_TO_INTERNAL_BUDGET(budget, shift); normalized_budget = NAPI_BUDGET_TO_INTERNAL_BUDGET(budget, shift);
hif_latency_profile_measure(hif_ext_group);
work_done = hif_ext_group->handler(hif_ext_group->context, work_done = hif_ext_group->handler(hif_ext_group->context,
normalized_budget); normalized_budget);
@@ -357,6 +517,8 @@ irqreturn_t hif_ext_group_interrupt_handler(int irq, void *context)
struct hif_softc *scn = HIF_GET_SOFTC(hif_ext_group->hif); struct hif_softc *scn = HIF_GET_SOFTC(hif_ext_group->hif);
if (hif_ext_group->irq_requested) { if (hif_ext_group->irq_requested) {
hif_latency_profile_start(hif_ext_group);
hif_ext_group->irq_disable(hif_ext_group); hif_ext_group->irq_disable(hif_ext_group);
/* /*
* if private ioctl has issued fake suspend command to put * if private ioctl has issued fake suspend command to put

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved.
* *
* Permission to use, copy, modify, and/or distribute this software for * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * any purpose with or without fee is hereby granted, provided that the
@@ -21,6 +21,23 @@
#include <hif.h> #include <hif.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
/*Number of buckets for latency*/
#define HIF_SCHED_LATENCY_BUCKETS 8
/*Buckets for latency between 0 to 2 ms*/
#define HIF_SCHED_LATENCY_BUCKET_0_2 2
/*Buckets for latency between 3 to 10 ms*/
#define HIF_SCHED_LATENCY_BUCKET_3_10 10
/*Buckets for latency between 11 to 20 ms*/
#define HIF_SCHED_LATENCY_BUCKET_11_20 20
/*Buckets for latency between 21 to 50 ms*/
#define HIF_SCHED_LATENCY_BUCKET_21_50 50
/*Buckets for latency between 50 to 100 ms*/
#define HIF_SCHED_LATENCY_BUCKET_51_100 100
/*Buckets for latency between 100 to 250 ms*/
#define HIF_SCHED_LATENCY_BUCKET_101_250 250
/*Buckets for latency between 250 to 500 ms*/
#define HIF_SCHED_LATENCY_BUCKET_251_500 500
struct hif_exec_context; struct hif_exec_context;
@@ -46,8 +63,11 @@ struct hif_execution_ops {
* determine if this context should reschedule or wait for an interrupt. * determine if this context should reschedule or wait for an interrupt.
* This function may be used as a hook for post processing. * This function may be used as a hook for post processing.
* *
* @sched_latency_stats: schdule latency stats for different latency buckets
* @tstamp: timestamp when napi poll happens
* @irq_disable: called before scheduling the context. * @irq_disable: called before scheduling the context.
* @irq_enable: called when the context leaves polling mode * @irq_enable: called when the context leaves polling mode
* @irq_name: pointer to function to return irq name/string mapped to irq number
*/ */
struct hif_exec_context { struct hif_exec_context {
struct hif_execution_ops *sched_ops; struct hif_execution_ops *sched_ops;
@@ -65,6 +85,9 @@ struct hif_exec_context {
bool (*work_complete)(struct hif_exec_context *, int work_done); bool (*work_complete)(struct hif_exec_context *, int work_done);
void (*irq_enable)(struct hif_exec_context *); void (*irq_enable)(struct hif_exec_context *);
void (*irq_disable)(struct hif_exec_context *); void (*irq_disable)(struct hif_exec_context *);
const char* (*irq_name)(int irq_no);
uint64_t sched_latency_stats[HIF_SCHED_LATENCY_BUCKETS];
uint64_t tstamp;
uint8_t cpu; uint8_t cpu;
struct qca_napi_stat stats[NR_CPUS]; struct qca_napi_stat stats[NR_CPUS];

View File

@@ -3424,6 +3424,19 @@ static void hif_exec_grp_irq_enable(struct hif_exec_context *hif_ext_group)
enable_irq(hif_ext_group->os_irq[i]); enable_irq(hif_ext_group->os_irq[i]);
} }
/**
* hif_pci_get_irq_name() - get irqname
* This function gives irqnumber to irqname
* mapping.
*
* @irq_no: irq number
*
* Return: irq name
*/
const char *hif_pci_get_irq_name(int irq_no)
{
return "pci-dummy";
}
int hif_pci_configure_grp_irq(struct hif_softc *scn, int hif_pci_configure_grp_irq(struct hif_softc *scn,
struct hif_exec_context *hif_ext_group) struct hif_exec_context *hif_ext_group)
@@ -3434,6 +3447,7 @@ int hif_pci_configure_grp_irq(struct hif_softc *scn,
hif_ext_group->irq_enable = &hif_exec_grp_irq_enable; hif_ext_group->irq_enable = &hif_exec_grp_irq_enable;
hif_ext_group->irq_disable = &hif_exec_grp_irq_disable; hif_ext_group->irq_disable = &hif_exec_grp_irq_disable;
hif_ext_group->irq_name = &hif_pci_get_irq_name;
hif_ext_group->work_complete = &hif_dummy_grp_done; hif_ext_group->work_complete = &hif_dummy_grp_done;
for (j = 0; j < hif_ext_group->numirq; j++) { for (j = 0; j < hif_ext_group->numirq; j++) {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2018 The Linux Foundation. All rights reserved. * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
* *
* Permission to use, copy, modify, and/or distribute this software for * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * any purpose with or without fee is hereby granted, provided that the
@@ -101,6 +101,19 @@ const char *ic_irqname[HIF_IC_MAX_IRQ] = {
"tcl2host-status-ring", "tcl2host-status-ring",
}; };
/** hif_ahb_get_irq_name() - get irqname
* This function gives irqnumber to irqname
* mapping.
*
* @irq_no: irq number
*
* Return: irq name
*/
const char *hif_ahb_get_irq_name(int irq_no)
{
return ic_irqname[irq_no];
}
/** /**
* hif_disable_isr() - disable isr * hif_disable_isr() - disable isr
* *
@@ -289,6 +302,7 @@ int hif_ahb_configure_grp_irq(struct hif_softc *scn,
/* configure external interrupts */ /* configure external interrupts */
hif_ext_group->irq_enable = &hif_ahb_exec_grp_irq_enable; hif_ext_group->irq_enable = &hif_ahb_exec_grp_irq_enable;
hif_ext_group->irq_disable = &hif_ahb_exec_grp_irq_disable; hif_ext_group->irq_disable = &hif_ahb_exec_grp_irq_disable;
hif_ext_group->irq_name = &hif_ahb_get_irq_name;
hif_ext_group->work_complete = &hif_dummy_grp_done; hif_ext_group->work_complete = &hif_dummy_grp_done;
qdf_spin_lock_irqsave(&hif_ext_group->irq_lock); qdf_spin_lock_irqsave(&hif_ext_group->irq_lock);