forked from torvalds/linux
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cpuidle: Extract IPI based and timer based wakeup latency from idle s…
…tates Introduce a mechanism to fire directed IPIs from a specified source CPU to a specified target CPU and measure the difference in time incurred on wakeup. Also, introduce a mechanism to queue a HR timer on a specified CPU and subsequently measure the time taken to wakeup the CPU. Finally define a simple debugfs interface to control the knobs to fire the IPI and Timer events on specified CPU and view their incurred idle wakeup latencies. Signed-off-by: Pratik Rajesh Sampat <psampat@linux.ibm.com>
- Loading branch information
1 parent
a38fd87
commit d03a0be
Showing
3 changed files
with
168 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
/* | ||
* Module-based API test facility for cpuidle latency using IPIs and timers | ||
*/ | ||
|
||
#include <linux/debugfs.h> | ||
#include <linux/kernel.h> | ||
#include <linux/module.h> | ||
|
||
/* | ||
* IPI based wakeup latencies | ||
* Measure time taken for a CPU to wakeup on a IPI sent from another CPU | ||
* The latency measured also includes the latency of sending the IPI | ||
*/ | ||
struct latency { | ||
unsigned int src_cpu; | ||
unsigned int dest_cpu; | ||
ktime_t time_start; | ||
ktime_t time_end; | ||
u64 latency_ns; | ||
} ipi_wakeup; | ||
|
||
static void measure_latency(void *info) | ||
{ | ||
struct latency *v; | ||
ktime_t time_diff; | ||
|
||
v = (struct latency *)info; | ||
v->time_end = ktime_get(); | ||
time_diff = ktime_sub(v->time_end, v->time_start); | ||
v->latency_ns = ktime_to_ns(time_diff); | ||
} | ||
|
||
void run_smp_call_function_test(unsigned int cpu) | ||
{ | ||
ipi_wakeup.src_cpu = smp_processor_id(); | ||
ipi_wakeup.dest_cpu = cpu; | ||
ipi_wakeup.time_start = ktime_get(); | ||
smp_call_function_single(cpu, measure_latency, &ipi_wakeup, 1); | ||
} | ||
|
||
/* | ||
* Timer based wakeup latencies | ||
* Measure time taken for a CPU to wakeup on a timer being armed and fired | ||
*/ | ||
struct timer_data { | ||
unsigned int src_cpu; | ||
u64 timeout; | ||
ktime_t time_start; | ||
ktime_t time_end; | ||
struct hrtimer timer; | ||
u64 timeout_diff_ns; | ||
} timer_wakeup; | ||
|
||
static enum hrtimer_restart timer_called(struct hrtimer *hrtimer) | ||
{ | ||
struct timer_data *w; | ||
ktime_t time_diff; | ||
|
||
w = container_of(hrtimer, struct timer_data, timer); | ||
w->time_end = ktime_get(); | ||
|
||
time_diff = ktime_sub(w->time_end, w->time_start); | ||
time_diff = ktime_sub(time_diff, ns_to_ktime(w->timeout)); | ||
w->timeout_diff_ns = ktime_to_ns(time_diff); | ||
return HRTIMER_NORESTART; | ||
} | ||
|
||
static void run_timer_test(unsigned int ns) | ||
{ | ||
hrtimer_init(&timer_wakeup.timer, CLOCK_MONOTONIC, | ||
HRTIMER_MODE_REL); | ||
timer_wakeup.timer.function = timer_called; | ||
timer_wakeup.src_cpu = smp_processor_id(); | ||
timer_wakeup.timeout = ns; | ||
timer_wakeup.time_start = ktime_get(); | ||
|
||
hrtimer_start(&timer_wakeup.timer, ns_to_ktime(ns), | ||
HRTIMER_MODE_REL_PINNED); | ||
} | ||
|
||
static struct dentry *dir; | ||
|
||
static int cpu_read_op(void *data, u64 *dest_cpu) | ||
{ | ||
*dest_cpu = ipi_wakeup.dest_cpu; | ||
return 0; | ||
} | ||
|
||
static int cpu_write_op(void *data, u64 value) | ||
{ | ||
run_smp_call_function_test(value); | ||
return 0; | ||
} | ||
DEFINE_SIMPLE_ATTRIBUTE(ipi_ops, cpu_read_op, cpu_write_op, "%llu\n"); | ||
|
||
static int timeout_read_op(void *data, u64 *timeout) | ||
{ | ||
*timeout = timer_wakeup.timeout; | ||
return 0; | ||
} | ||
|
||
static int timeout_write_op(void *data, u64 value) | ||
{ | ||
run_timer_test(value); | ||
return 0; | ||
} | ||
DEFINE_SIMPLE_ATTRIBUTE(timeout_ops, timeout_read_op, timeout_write_op, "%llu\n"); | ||
|
||
static int __init latency_init(void) | ||
{ | ||
struct dentry *temp; | ||
|
||
dir = debugfs_create_dir("latency_test", 0); | ||
if (!dir) { | ||
pr_alert("latency_test: failed to create /sys/kernel/debug/latency_test\n"); | ||
return -1; | ||
} | ||
temp = debugfs_create_file("ipi_cpu_dest", | ||
0666, | ||
dir, | ||
NULL, | ||
&ipi_ops); | ||
if (!temp) { | ||
pr_alert("latency_test: failed to create /sys/kernel/debug/ipi_cpu_dest\n"); | ||
return -1; | ||
} | ||
debugfs_create_u64("ipi_latency_ns", 0444, dir, &ipi_wakeup.latency_ns); | ||
debugfs_create_u32("ipi_cpu_src", 0444, dir, &ipi_wakeup.src_cpu); | ||
|
||
temp = debugfs_create_file("timeout_expected_ns", | ||
0666, | ||
dir, | ||
NULL, | ||
&timeout_ops); | ||
if (!temp) { | ||
pr_alert("latency_test: failed to create /sys/kernel/debug/timeout_expected_ns\n"); | ||
return -1; | ||
} | ||
debugfs_create_u64("timeout_diff_ns", 0444, dir, &timer_wakeup.timeout_diff_ns); | ||
debugfs_create_u32("timeout_cpu_src", 0444, dir, &timer_wakeup.src_cpu); | ||
pr_info("Latency Test module loaded\n"); | ||
return 0; | ||
} | ||
|
||
static void __exit latency_cleanup(void) | ||
{ | ||
pr_info("Cleaning up Latency Test module.\n"); | ||
debugfs_remove_recursive(dir); | ||
} | ||
|
||
module_init(latency_init); | ||
module_exit(latency_cleanup); | ||
|
||
MODULE_LICENSE("GPL"); | ||
MODULE_AUTHOR("IBM Corporation"); | ||
MODULE_DESCRIPTION("Measuring idle latency for IPIs and Timers"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters