qcacmn: extend hif tasklet latency detection

Extend the feature to detect tasklet latency for all CEs.
And also introduce a build flag
DETECTION_LATENCY_TASKLET_MASK to control the enablement
of each, set bit-X to enable detection for CE-X, the
default value is 0x84(BIT(2) | BIT(7)).

Change-Id: I2874d1d50fc5febbf028ebb48ba64e11977c8c34
CRs-Fixed: 3474314
This commit is contained in:
Yu Wang
2023-04-21 15:19:56 +08:00
committed by Rahul Choudhary
parent 0ece3dfc96
commit bfebabf7bd
4 changed files with 156 additions and 88 deletions

View File

@@ -2444,8 +2444,6 @@ void hif_latency_detect_credit_record_time(
void hif_latency_detect_timer_start(struct hif_opaque_softc *hif_ctx); void hif_latency_detect_timer_start(struct hif_opaque_softc *hif_ctx);
void hif_latency_detect_timer_stop(struct hif_opaque_softc *hif_ctx); void hif_latency_detect_timer_stop(struct hif_opaque_softc *hif_ctx);
void hif_tasklet_latency(struct hif_softc *scn, bool from_timer);
void hif_credit_latency(struct hif_softc *scn, bool from_timer);
void hif_check_detection_latency(struct hif_softc *scn, void hif_check_detection_latency(struct hif_softc *scn,
bool from_timer, bool from_timer,
uint32_t bitmap_type); uint32_t bitmap_type);

View File

@@ -368,11 +368,14 @@ void hif_latency_detect_tasklet_sched(
struct hif_softc *scn, struct hif_softc *scn,
struct ce_tasklet_entry *tasklet_entry) struct ce_tasklet_entry *tasklet_entry)
{ {
if (tasklet_entry->ce_id != CE_ID_2) int idx = tasklet_entry->ce_id;
if (idx >= HIF_TASKLET_IN_MONITOR ||
!qdf_test_bit(idx, scn->latency_detect.tasklet_bmap))
return; return;
scn->latency_detect.ce2_tasklet_sched_cpuid = qdf_get_cpu(); scn->latency_detect.tasklet_info[idx].sched_cpuid = qdf_get_cpu();
scn->latency_detect.ce2_tasklet_sched_time = qdf_system_ticks(); scn->latency_detect.tasklet_info[idx].sched_time = qdf_system_ticks();
} }
static inline static inline
@@ -380,10 +383,13 @@ void hif_latency_detect_tasklet_exec(
struct hif_softc *scn, struct hif_softc *scn,
struct ce_tasklet_entry *tasklet_entry) struct ce_tasklet_entry *tasklet_entry)
{ {
if (tasklet_entry->ce_id != CE_ID_2) int idx = tasklet_entry->ce_id;
if (idx >= HIF_TASKLET_IN_MONITOR ||
!qdf_test_bit(idx, scn->latency_detect.tasklet_bmap))
return; return;
scn->latency_detect.ce2_tasklet_exec_time = qdf_system_ticks(); scn->latency_detect.tasklet_info[idx].exec_time = qdf_system_ticks();
hif_check_detection_latency(scn, false, BIT(HIF_DETECT_TASKLET)); hif_check_detection_latency(scn, false, BIT(HIF_DETECT_TASKLET));
} }
#else #else

View File

@@ -850,17 +850,25 @@ static void hif_cpuhp_unregister(struct hif_softc *scn)
#endif /* ifdef HIF_CPU_PERF_AFFINE_MASK */ #endif /* ifdef HIF_CPU_PERF_AFFINE_MASK */
#ifdef HIF_DETECTION_LATENCY_ENABLE #ifdef HIF_DETECTION_LATENCY_ENABLE
/*
* Bitmask to control enablement of latency detection for the tasklets,
* bit-X represents for tasklet of WLAN_CE_X.
*/
#ifndef DETECTION_LATENCY_TASKLET_MASK
#define DETECTION_LATENCY_TASKLET_MASK (BIT(2) | BIT(7))
#endif
void hif_tasklet_latency(struct hif_softc *scn, bool from_timer) static inline int
__hif_tasklet_latency(struct hif_softc *scn, bool from_timer, int idx)
{ {
qdf_time_t ce2_tasklet_sched_time = qdf_time_t sched_time =
scn->latency_detect.ce2_tasklet_sched_time; scn->latency_detect.tasklet_info[idx].sched_time;
qdf_time_t ce2_tasklet_exec_time = qdf_time_t exec_time =
scn->latency_detect.ce2_tasklet_exec_time; scn->latency_detect.tasklet_info[idx].exec_time;
qdf_time_t curr_jiffies = qdf_system_ticks(); qdf_time_t curr_time = qdf_system_ticks();
uint32_t detect_latency_threshold = uint32_t threshold = scn->latency_detect.threshold;
scn->latency_detect.detect_latency_threshold; qdf_time_t expect_exec_time =
int cpu_id = qdf_get_cpu(); sched_time + qdf_system_msecs_to_ticks(threshold);
/* 2 kinds of check here. /* 2 kinds of check here.
* from_timer==true: check if tasklet stall * from_timer==true: check if tasklet stall
@@ -868,36 +876,45 @@ void hif_tasklet_latency(struct hif_softc *scn, bool from_timer)
*/ */
if ((from_timer ? if ((from_timer ?
qdf_system_time_after(ce2_tasklet_sched_time, qdf_system_time_after(sched_time, exec_time) :
ce2_tasklet_exec_time) : qdf_system_time_after(exec_time, sched_time)) &&
qdf_system_time_after(ce2_tasklet_exec_time, qdf_system_time_after(curr_time, expect_exec_time)) {
ce2_tasklet_sched_time)) && hif_err("tasklet[%d] latency detected: from_timer %d, curr_time %lu, sched_time %lu, exec_time %lu, threshold %ums, timeout %ums, cpu_id %d, called: %ps",
qdf_system_time_after( idx, from_timer, curr_time, sched_time,
curr_jiffies, exec_time, threshold,
ce2_tasklet_sched_time + scn->latency_detect.timeout,
qdf_system_msecs_to_ticks(detect_latency_threshold))) { qdf_get_cpu(), (void *)_RET_IP_);
hif_err("tasklet ce2 latency: from_timer %d, curr_jiffies %lu, ce2_tasklet_sched_time %lu,ce2_tasklet_exec_time %lu, detect_latency_threshold %ums detect_latency_timer_timeout %ums, cpu_id %d, called: %ps", return -ETIMEDOUT;
from_timer, curr_jiffies, ce2_tasklet_sched_time,
ce2_tasklet_exec_time, detect_latency_threshold,
scn->latency_detect.detect_latency_timer_timeout,
cpu_id, (void *)_RET_IP_);
goto latency;
} }
return;
latency: return 0;
qdf_trigger_self_recovery(NULL, QDF_TASKLET_CREDIT_LATENCY_DETECT);
} }
void hif_credit_latency(struct hif_softc *scn, bool from_timer) static inline void hif_tasklet_latency(struct hif_softc *scn, bool from_timer)
{
int i, ret;
for (i = 0; i < HIF_TASKLET_IN_MONITOR; i++) {
if (!qdf_test_bit(i, scn->latency_detect.tasklet_bmap))
continue;
ret = __hif_tasklet_latency(scn, from_timer, i);
if (!ret)
continue;
qdf_trigger_self_recovery(NULL,
QDF_TASKLET_CREDIT_LATENCY_DETECT);
return;
}
}
static inline void hif_credit_latency(struct hif_softc *scn, bool from_timer)
{ {
qdf_time_t credit_request_time = qdf_time_t credit_request_time =
scn->latency_detect.credit_request_time; scn->latency_detect.credit_request_time;
qdf_time_t credit_report_time = qdf_time_t credit_report_time = scn->latency_detect.credit_report_time;
scn->latency_detect.credit_report_time;
qdf_time_t curr_jiffies = qdf_system_ticks(); qdf_time_t curr_jiffies = qdf_system_ticks();
uint32_t detect_latency_threshold = uint32_t threshold = scn->latency_detect.threshold;
scn->latency_detect.detect_latency_threshold;
int cpu_id = qdf_get_cpu(); int cpu_id = qdf_get_cpu();
/* 2 kinds of check here. /* 2 kinds of check here.
@@ -906,18 +923,15 @@ void hif_credit_latency(struct hif_softc *scn, bool from_timer)
*/ */
if ((from_timer ? if ((from_timer ?
qdf_system_time_after(credit_request_time, qdf_system_time_after(credit_request_time, credit_report_time) :
credit_report_time) : qdf_system_time_after(credit_report_time, credit_request_time)) &&
qdf_system_time_after(credit_report_time, qdf_system_time_after(curr_jiffies,
credit_request_time)) &&
qdf_system_time_after(
curr_jiffies,
credit_request_time + credit_request_time +
qdf_system_msecs_to_ticks(detect_latency_threshold))) { qdf_system_msecs_to_ticks(threshold))) {
hif_err("credit report latency: from timer %d, curr_jiffies %lu, credit_request_time %lu,credit_report_time %lu, detect_latency_threshold %ums, detect_latency_timer_timeout %ums, cpu_id %d, called: %ps", hif_err("credit report latency: from timer %d, curr_jiffies %lu, credit_request_time %lu, credit_report_time %lu, threshold %ums, timeout %ums, cpu_id %d, called: %ps",
from_timer, curr_jiffies, credit_request_time, from_timer, curr_jiffies, credit_request_time,
credit_report_time, detect_latency_threshold, credit_report_time, threshold,
scn->latency_detect.detect_latency_timer_timeout, scn->latency_detect.timeout,
cpu_id, (void *)_RET_IP_); cpu_id, (void *)_RET_IP_);
goto latency; goto latency;
} }
@@ -956,7 +970,9 @@ void hif_check_detection_latency(struct hif_softc *scn,
static void hif_latency_detect_timeout_handler(void *arg) static void hif_latency_detect_timeout_handler(void *arg)
{ {
struct hif_softc *scn = (struct hif_softc *)arg; struct hif_softc *scn = (struct hif_softc *)arg;
int next_cpu; int next_cpu, i;
qdf_cpu_mask cpu_mask = {0};
struct hif_latency_detect *detect = &scn->latency_detect;
hif_check_detection_latency(scn, true, hif_check_detection_latency(scn, true,
BIT(HIF_DETECT_TASKLET) | BIT(HIF_DETECT_TASKLET) |
@@ -971,47 +987,40 @@ static void hif_latency_detect_timeout_handler(void *arg)
* if tasklet stall, anyway other place will detect it, just * if tasklet stall, anyway other place will detect it, just
* a little later. * a little later.
*/ */
next_cpu = cpumask_any_but( qdf_cpumask_copy(&cpu_mask, (const qdf_cpu_mask *)cpu_active_mask);
cpu_active_mask, for (i = 0; i < HIF_TASKLET_IN_MONITOR; i++) {
scn->latency_detect.ce2_tasklet_sched_cpuid); if (!qdf_test_bit(i, detect->tasklet_bmap))
continue;
qdf_cpumask_clear_cpu(detect->tasklet_info[i].sched_cpuid,
&cpu_mask);
}
next_cpu = cpumask_first(&cpu_mask);
if (qdf_unlikely(next_cpu >= nr_cpu_ids)) { if (qdf_unlikely(next_cpu >= nr_cpu_ids)) {
hif_debug("start timer on local"); hif_debug("start timer on local");
/* it doesn't found a available cpu, start on local cpu*/ /* it doesn't found a available cpu, start on local cpu*/
qdf_timer_mod( qdf_timer_mod(&detect->timer, detect->timeout);
&scn->latency_detect.detect_latency_timer,
scn->latency_detect.detect_latency_timer_timeout);
} else { } else {
qdf_timer_start_on( qdf_timer_start_on(&detect->timer, detect->timeout, next_cpu);
&scn->latency_detect.detect_latency_timer,
scn->latency_detect.detect_latency_timer_timeout,
next_cpu);
} }
} }
static void hif_latency_detect_timer_init(struct hif_softc *scn) static void hif_latency_detect_timer_init(struct hif_softc *scn)
{ {
if (!scn) { scn->latency_detect.timeout =
hif_info_high("scn is null");
return;
}
if (QDF_GLOBAL_MISSION_MODE != hif_get_conparam(scn))
return;
scn->latency_detect.detect_latency_timer_timeout =
DETECTION_TIMER_TIMEOUT; DETECTION_TIMER_TIMEOUT;
scn->latency_detect.detect_latency_threshold = scn->latency_detect.threshold =
DETECTION_LATENCY_THRESHOLD; DETECTION_LATENCY_THRESHOLD;
hif_info("timer timeout %u, latency threshold %u", hif_info("timer timeout %u, latency threshold %u",
scn->latency_detect.detect_latency_timer_timeout, scn->latency_detect.timeout,
scn->latency_detect.detect_latency_threshold); scn->latency_detect.threshold);
scn->latency_detect.is_timer_started = false; scn->latency_detect.is_timer_started = false;
qdf_timer_init(NULL, qdf_timer_init(NULL,
&scn->latency_detect.detect_latency_timer, &scn->latency_detect.timer,
&hif_latency_detect_timeout_handler, &hif_latency_detect_timeout_handler,
scn, scn,
QDF_TIMER_TYPE_SW_SPIN); QDF_TIMER_TYPE_SW_SPIN);
@@ -1019,11 +1028,38 @@ static void hif_latency_detect_timer_init(struct hif_softc *scn)
static void hif_latency_detect_timer_deinit(struct hif_softc *scn) static void hif_latency_detect_timer_deinit(struct hif_softc *scn)
{ {
hif_info("deinit timer");
qdf_timer_free(&scn->latency_detect.timer);
}
static void hif_latency_detect_init(struct hif_softc *scn)
{
uint32_t tasklet_mask;
int i;
if (QDF_GLOBAL_MISSION_MODE != hif_get_conparam(scn)) if (QDF_GLOBAL_MISSION_MODE != hif_get_conparam(scn))
return; return;
hif_info("deinit timer"); tasklet_mask = DETECTION_LATENCY_TASKLET_MASK;
qdf_timer_free(&scn->latency_detect.detect_latency_timer); hif_info("tasklet mask is 0x%x", tasklet_mask);
for (i = 0; i < HIF_TASKLET_IN_MONITOR; i++) {
if (BIT(i) & tasklet_mask)
qdf_set_bit(i, scn->latency_detect.tasklet_bmap);
}
hif_latency_detect_timer_init(scn);
}
static void hif_latency_detect_deinit(struct hif_softc *scn)
{
int i;
if (QDF_GLOBAL_MISSION_MODE != hif_get_conparam(scn))
return;
hif_latency_detect_timer_deinit(scn);
for (i = 0; i < HIF_TASKLET_IN_MONITOR; i++)
qdf_clear_bit(i, scn->latency_detect.tasklet_bmap);
} }
void hif_latency_detect_timer_start(struct hif_opaque_softc *hif_ctx) void hif_latency_detect_timer_start(struct hif_opaque_softc *hif_ctx)
@@ -1039,8 +1075,8 @@ void hif_latency_detect_timer_start(struct hif_opaque_softc *hif_ctx)
return; return;
} }
qdf_timer_start(&scn->latency_detect.detect_latency_timer, qdf_timer_start(&scn->latency_detect.timer,
scn->latency_detect.detect_latency_timer_timeout); scn->latency_detect.timeout);
scn->latency_detect.is_timer_started = true; scn->latency_detect.is_timer_started = true;
} }
@@ -1053,7 +1089,7 @@ void hif_latency_detect_timer_stop(struct hif_opaque_softc *hif_ctx)
hif_debug_rl("stop timer"); hif_debug_rl("stop timer");
qdf_timer_sync_cancel(&scn->latency_detect.detect_latency_timer); qdf_timer_sync_cancel(&scn->latency_detect.timer);
scn->latency_detect.is_timer_started = false; scn->latency_detect.is_timer_started = false;
} }
@@ -1094,10 +1130,10 @@ void hif_set_enable_detection(struct hif_opaque_softc *hif_ctx, bool value)
scn->latency_detect.enable_detection = value; scn->latency_detect.enable_detection = value;
} }
#else #else
static void hif_latency_detect_timer_init(struct hif_softc *scn) static inline void hif_latency_detect_init(struct hif_softc *scn)
{} {}
static void hif_latency_detect_timer_deinit(struct hif_softc *scn) static inline void hif_latency_detect_deinit(struct hif_softc *scn)
{} {}
#endif #endif
struct hif_opaque_softc *hif_open(qdf_device_t qdf_ctx, struct hif_opaque_softc *hif_open(qdf_device_t qdf_ctx,
@@ -1147,7 +1183,7 @@ struct hif_opaque_softc *hif_open(qdf_device_t qdf_ctx,
hif_rtpm_lock_init(scn); hif_rtpm_lock_init(scn);
hif_cpuhp_register(scn); hif_cpuhp_register(scn);
hif_latency_detect_timer_init(scn); hif_latency_detect_init(scn);
out: out:
return GET_HIF_OPAQUE_HDL(scn); return GET_HIF_OPAQUE_HDL(scn);
@@ -1186,7 +1222,7 @@ void hif_close(struct hif_opaque_softc *hif_ctx)
return; return;
} }
hif_latency_detect_timer_deinit(scn); hif_latency_detect_deinit(scn);
if (scn->athdiag_procfs_inited) { if (scn->athdiag_procfs_inited) {
athdiag_procfs_remove(); athdiag_procfs_remove();

View File

@@ -176,16 +176,44 @@ struct hif_ce_stats {
}; };
#ifdef HIF_DETECTION_LATENCY_ENABLE #ifdef HIF_DETECTION_LATENCY_ENABLE
/**
* struct hif_tasklet_running_info - running info of tasklet
* @sched_cpuid: id of cpu on which the tasklet was scheduled
* @sched_time: time when the tasklet was scheduled
* @exec_time: time when the tasklet was executed
*/
struct hif_tasklet_running_info {
int sched_cpuid;
qdf_time_t sched_time;
qdf_time_t exec_time;
};
#define HIF_TASKLET_IN_MONITOR CE_COUNT_MAX
struct hif_latency_detect { struct hif_latency_detect {
qdf_timer_t detect_latency_timer; qdf_timer_t timer;
uint32_t detect_latency_timer_timeout; uint32_t timeout;
bool is_timer_started; bool is_timer_started;
bool enable_detection; bool enable_detection;
/* threshold when stall happens */ /* threshold when stall happens */
uint32_t detect_latency_threshold; uint32_t threshold;
int ce2_tasklet_sched_cpuid;
qdf_time_t ce2_tasklet_sched_time; /*
qdf_time_t ce2_tasklet_exec_time; * Bitmap to indicate the enablement of latency detection for
* the tasklets. bit-X represents for tasklet of WLAN_CE_X,
* latency detection is enabled on the corresponding tasklet
* when a bit is set.
* At the same time, this bitmap also indicates the validity of
* elements in array 'tasklet_info', bit-X represents for index-X,
* the corresponding element is valid when a bit is set.
*/
qdf_bitmap(tasklet_bmap, HIF_TASKLET_IN_MONITOR);
/*
* Array to record running info of tasklets, info of tasklet
* for WLAN_CE_X is stored at index-X.
*/
struct hif_tasklet_running_info tasklet_info[HIF_TASKLET_IN_MONITOR];
qdf_time_t credit_request_time; qdf_time_t credit_request_time;
qdf_time_t credit_report_time; qdf_time_t credit_report_time;
}; };