diff options
author | Adrian Hunter <adrian.hunter@intel.com> | 2013-06-28 09:22:18 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2013-07-23 06:17:45 -0400 |
commit | c73deb6aecda2955716f31572516f09d930ef450 (patch) | |
tree | 037f7945cb4289545d5ffbe9506e071a83d6c1a6 | |
parent | 860f085b74e9f0075de8140ed3a1e5b5e3e39aa8 (diff) |
perf/x86: Add ability to calculate TSC from perf sample timestamps
For modern CPUs, perf clock is directly related to TSC. TSC
can be calculated from perf clock and vice versa using a simple
calculation. Two of the three componenets of that calculation
are already exported in struct perf_event_mmap_page. This patch
exports the third.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Link: http://lkml.kernel.org/r/1372425741-1676-3-git-send-email-adrian.hunter@intel.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | arch/x86/include/asm/tsc.h | 1 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 6 | ||||
-rw-r--r-- | arch/x86/kernel/tsc.c | 6 | ||||
-rw-r--r-- | include/uapi/linux/perf_event.h | 22 |
4 files changed, 33 insertions, 2 deletions
diff --git a/arch/x86/include/asm/tsc.h b/arch/x86/include/asm/tsc.h index c91e8b9d588b..235be70d5bb4 100644 --- a/arch/x86/include/asm/tsc.h +++ b/arch/x86/include/asm/tsc.h | |||
@@ -49,6 +49,7 @@ extern void tsc_init(void); | |||
49 | extern void mark_tsc_unstable(char *reason); | 49 | extern void mark_tsc_unstable(char *reason); |
50 | extern int unsynchronized_tsc(void); | 50 | extern int unsynchronized_tsc(void); |
51 | extern int check_tsc_unstable(void); | 51 | extern int check_tsc_unstable(void); |
52 | extern int check_tsc_disabled(void); | ||
52 | extern unsigned long native_calibrate_tsc(void); | 53 | extern unsigned long native_calibrate_tsc(void); |
53 | 54 | ||
54 | extern int tsc_clocksource_reliable; | 55 | extern int tsc_clocksource_reliable; |
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index a7c7305030cc..8355c84b9729 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -1884,6 +1884,7 @@ static struct pmu pmu = { | |||
1884 | void arch_perf_update_userpage(struct perf_event_mmap_page *userpg, u64 now) | 1884 | void arch_perf_update_userpage(struct perf_event_mmap_page *userpg, u64 now) |
1885 | { | 1885 | { |
1886 | userpg->cap_usr_time = 0; | 1886 | userpg->cap_usr_time = 0; |
1887 | userpg->cap_usr_time_zero = 0; | ||
1887 | userpg->cap_usr_rdpmc = x86_pmu.attr_rdpmc; | 1888 | userpg->cap_usr_rdpmc = x86_pmu.attr_rdpmc; |
1888 | userpg->pmc_width = x86_pmu.cntval_bits; | 1889 | userpg->pmc_width = x86_pmu.cntval_bits; |
1889 | 1890 | ||
@@ -1897,6 +1898,11 @@ void arch_perf_update_userpage(struct perf_event_mmap_page *userpg, u64 now) | |||
1897 | userpg->time_mult = this_cpu_read(cyc2ns); | 1898 | userpg->time_mult = this_cpu_read(cyc2ns); |
1898 | userpg->time_shift = CYC2NS_SCALE_FACTOR; | 1899 | userpg->time_shift = CYC2NS_SCALE_FACTOR; |
1899 | userpg->time_offset = this_cpu_read(cyc2ns_offset) - now; | 1900 | userpg->time_offset = this_cpu_read(cyc2ns_offset) - now; |
1901 | |||
1902 | if (sched_clock_stable && !check_tsc_disabled()) { | ||
1903 | userpg->cap_usr_time_zero = 1; | ||
1904 | userpg->time_zero = this_cpu_read(cyc2ns_offset); | ||
1905 | } | ||
1900 | } | 1906 | } |
1901 | 1907 | ||
1902 | /* | 1908 | /* |
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 6ff49247edf8..930e5d48f560 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c | |||
@@ -89,6 +89,12 @@ int check_tsc_unstable(void) | |||
89 | } | 89 | } |
90 | EXPORT_SYMBOL_GPL(check_tsc_unstable); | 90 | EXPORT_SYMBOL_GPL(check_tsc_unstable); |
91 | 91 | ||
92 | int check_tsc_disabled(void) | ||
93 | { | ||
94 | return tsc_disabled; | ||
95 | } | ||
96 | EXPORT_SYMBOL_GPL(check_tsc_disabled); | ||
97 | |||
92 | #ifdef CONFIG_X86_TSC | 98 | #ifdef CONFIG_X86_TSC |
93 | int __init notsc_setup(char *str) | 99 | int __init notsc_setup(char *str) |
94 | { | 100 | { |
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 0041aedf2297..efef1d37a371 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h | |||
@@ -378,7 +378,8 @@ struct perf_event_mmap_page { | |||
378 | struct { | 378 | struct { |
379 | __u64 cap_usr_time : 1, | 379 | __u64 cap_usr_time : 1, |
380 | cap_usr_rdpmc : 1, | 380 | cap_usr_rdpmc : 1, |
381 | cap_____res : 62; | 381 | cap_usr_time_zero : 1, |
382 | cap_____res : 61; | ||
382 | }; | 383 | }; |
383 | }; | 384 | }; |
384 | 385 | ||
@@ -420,12 +421,29 @@ struct perf_event_mmap_page { | |||
420 | __u16 time_shift; | 421 | __u16 time_shift; |
421 | __u32 time_mult; | 422 | __u32 time_mult; |
422 | __u64 time_offset; | 423 | __u64 time_offset; |
424 | /* | ||
425 | * If cap_usr_time_zero, the hardware clock (e.g. TSC) can be calculated | ||
426 | * from sample timestamps. | ||
427 | * | ||
428 | * time = timestamp - time_zero; | ||
429 | * quot = time / time_mult; | ||
430 | * rem = time % time_mult; | ||
431 | * cyc = (quot << time_shift) + (rem << time_shift) / time_mult; | ||
432 | * | ||
433 | * And vice versa: | ||
434 | * | ||
435 | * quot = cyc >> time_shift; | ||
436 | * rem = cyc & ((1 << time_shift) - 1); | ||
437 | * timestamp = time_zero + quot * time_mult + | ||
438 | * ((rem * time_mult) >> time_shift); | ||
439 | */ | ||
440 | __u64 time_zero; | ||
423 | 441 | ||
424 | /* | 442 | /* |
425 | * Hole for extension of the self monitor capabilities | 443 | * Hole for extension of the self monitor capabilities |
426 | */ | 444 | */ |
427 | 445 | ||
428 | __u64 __reserved[120]; /* align to 1k */ | 446 | __u64 __reserved[119]; /* align to 1k */ |
429 | 447 | ||
430 | /* | 448 | /* |
431 | * Control data for the mmap() data buffer. | 449 | * Control data for the mmap() data buffer. |