Updated MSR tracing patchkit v2
Subsystems (3)
This patchkit adds proper trace points for all MSR accesses. Very useful for PMU (perf) debugging, but also various other CPU near areas.
Updated version:
- Now traces RDPMC too
- Add benchmarks on the overhead of moving MSR access out of line
PATCH 1/3 x86: Move msr accesses out of line
From: Andi Kleen <[email protected]>
To add trace points to msr accesses we need to include linux/tracepoint.h. Unfortunately this causes hellish include loops when with the msr inlines in asm/msr.h, which are included all over. I tried to fix several of them, but eventually gave up.
This patch moves the MSR functions out of line. A MSR access is typically 40-100 cycles or even slower, a call is a few cycles at best, so the additional function call is not really significant.
Kernel text size is neutral:
11852945 1671656 1822720 15347321 ea2e79 vmlinux-no-msr
11852969 1671656 1822720 15347345 ea2e91 vmlinux-msr
As requested, some benchmarking on the difference to inline MSR (including the trace points from the next patch):
The absolute differences are fairly low, 6-8 cycles for out of line + trace point. 6-7% on Haswell. On Avoton the percentages are higher because the base costs are lower, but the absolute cycle deltas are very low too and in the same range.
I think it's reasonable to spend 6-8 cycles/call for much better debuggability. In fact looking at the traces already exposed a number of optimization possibilities for optimizing away unnecessary accesses, that should give much higher gains.
haswell:
136 cycles ool wrmsr
128 cycles inline wrmsr 6%
90 cycles ool rdmsr
84 cycles inline rdmsr 7%
avoton:
68 cycles ool wrmsr
54 cycles inline wrmsr 20%
60 cycles ool rdmsr
44 cycles inline rdmsr 26%
--- arch/x86/include/asm/msr.h | 51 ++++---------------------------------------- arch/x86/lib/msr.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 47 deletions(-)
arch/x86/include/asm/msr.h +4 -47
@@ -57,53 +57,10 @@ static inline unsigned long long native_read_tscp(unsigned int *aux)#define EAX_EDX_RET (val, low, high) "=A" (val)#endif -static inline unsigned long long native_read_msr (unsigned int msr) -{ -DECLARE_ARGS (val, low, high); - - asm volatile("rdmsr" : EAX_EDX_RET(val, low, high) : "c" (msr)); -return EAX_EDX_VAL (val, low, high); -} - -static inline unsigned long long native_read_msr_safe (unsigned int msr, -int *err) -{ -DECLARE_ARGS (val, low, high); - - asmvolatile ("2: rdmsr ; xor %[err],%[err]\n" -"1:\n\t" -".section .fixup,\"ax\"\n\t" -"3: mov %[fault],%[err] ; jmp 1b\n\t" -".previous\n\t" - _ASM_EXTABLE(2b, 3b) - : [err] "=r" (*err), EAX_EDX_RET(val, low, high) - : "c" (msr), [fault] "i" (-EIO)); - returnEAX_EDX_VAL (val, low, high); -} - -static inline void native_write_msr (unsigned int msr, -unsigned low,unsigned high) -{ - asmvolatile ("wrmsr" : :"c" (msr),"a" (low),"d" (high) :"memory" ); -} - -/* Can be uninlined because referenced by paravirt */ -notrace static inline int native_write_msr_safe(unsigned int msr, -unsigned low,unsigned high) -{ -int err; - asmvolatile ("2: wrmsr ; xor %[err],%[err]\n" -"1:\n\t" -".section .fixup,\"ax\"\n\t" -"3: mov %[fault],%[err] ; jmp 1b\n\t" -".previous\n\t" - _ASM_EXTABLE(2b, 3b) - : [err] "=a" (err) - : "c" (msr), "0" (low), "d" (high), - [fault] "i" (-EIO) - : "memory"); -return err; -} +extern unsigned long long native_read_msr (unsigned int msr); +extern unsigned long long native_read_msr_safe (unsigned int msr,int *err); +extern int native_write_msr_safe (unsigned int msr,unsigned low,unsigned high); +extern void native_write_msr (unsigned int msr,unsigned low,unsigned high);extern unsigned long long native_read_tsc (void );
arch/x86/lib/msr.c +53 -0
56 lines not on this page · raw message
Signature · 1 line
1.9.3
PATCH 2/3 x86: Add trace point for MSR accesses
In reply to Andi KleenFrom: Andi Kleen <[email protected]>
For debugging low level code interacting with the CPU it is often useful to trace the MSR read/writes. This gives a concise summary of PMU and other operations.
perf has an ad-hoc way to do this using trace_printk, but it's somewhat limited (and also now spews ugly messages when enabled)
Instead define real trace points for all MSR accesses.
This adds two new trace point: read_msr and write_msr. They also report if the access faulted (if *_safe is used)
This allows filtering and triggering on specific
MSR values, which allows various more advanced
debugging techniques.
All the values are well defined in the CPU documentation.
I only added it to native MSR accesses in C, not paravirtualized or in entry*.S (which is not too interesting)
--- arch/x86/lib/msr.c | 14 ++++++++++++-- include/trace/events/msr.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 include/trace/events/msr.h
arch/x86/lib/msr.c +12 -2
@@ -1,6 +1,8 @@#include <linux/module.h> #include <linux/preempt.h> #include <asm/msr.h> +#define CREATE_TRACE_POINTS +#include <trace/events/msr.h> struct msr *msrs_alloc (void ) {
@@ -111,16 +113,20 @@ int msr_clear_bit(u32 msr, u8 bit)inline unsigned long long native_read_msr (unsigned int msr) { +unsigned long long lval;DECLARE_ARGS (val, low, high); asm volatile("rdmsr" : EAX_EDX_RET(val, low, high) : "c" (msr)); -return EAX_EDX_VAL (val, low, high); + lval =EAX_EDX_VAL (val, low, high); +trace_read_msr (msr, lval,0 ); +return lval; }EXPORT_SYMBOL (native_read_msr);inline unsigned long long native_read_msr_safe (unsigned int msr,int *err) { +unsigned long long lval;DECLARE_ARGS (val, low, high); asm volatile("2: rdmsr ; xor %[err],%[err]\n"
@@ -131,7 +137,9 @@ inline unsigned long long native_read_msr_safe(unsigned int msr,_ASM_EXTABLE (2b ,3b ) : [err] "=r" (*err), EAX_EDX_RET(val, low, high) : "c" (msr), [fault] "i" (-EIO)); - returnEAX_EDX_VAL (val, low, high); + lval = EAX_EDX_VAL(val, low, high); +trace_read_msr (msr, lval, *err); +return lval; }EXPORT_SYMBOL (native_read_msr_safe);
@@ -139,6 +147,7 @@ inline void native_write_msr(unsigned int msr, unsigned low, unsigned high) { asmvolatile ("wrmsr" : :"c" (msr),"a" (low),"d" (high) :"memory" ); +trace_write_msr (msr, ((u64 )high <<32 | low),0 ); }EXPORT_SYMBOL (native_write_msr);
@@ -158,6 +167,7 @@ notrace inline int native_write_msr_safe(unsigned int msr, : "c" (msr), "0" (low), "d" (high), [fault] "i" (-EIO) : "memory"); +trace_write_msr (msr, ((u64 )high <<32 | low), err);return err; }EXPORT_SYMBOL (native_write_msr_safe);
include/trace/events/msr.h new file +46 -0
46 lines not on this page · raw message
Signature · 1 line
1.9.3
PATCH 3/3 x86, perf: Trace rdpmc too
In reply to Andi KleenFrom: Andi Kleen <[email protected]>
perf uses RDPMC to read the performance counters, so it's useful to trace that too. Add a trace point for RDPMC too, similar to the existing MSR ones. Since there is only a single call of rdpmc in the whole kernel (in perf)
just add the trace statement to that call, instead of moving the rdpmcl inline out of line.
--- arch/x86/kernel/cpu/perf_event.c | 3 +++ include/trace/events/msr.h | 6 ++++++ 2 files changed, 9 insertions(+)
arch/x86/kernel/cpu/perf_event.c +3 -0
@@ -26,6 +26,8 @@#include <linux/bitops.h> #include <linux/device.h> +#include <trace/events/msr.h> +#include <asm/apic.h> #include <asm/stacktrace.h> #include <asm/nmi.h>
@@ -82,6 +84,7 @@ u64 x86_perf_event_update(struct perf_event *event) again: prev_raw_count =local64_read (&hwc->prev_count);rdpmcl (hwc->event_base_rdpmc, new_raw_count); +trace_rdpmc (hwc->event_base_rdpmc, new_raw_count,0 );if (local64_cmpxchg (&hwc->prev_count, prev_raw_count, new_raw_count) != prev_raw_count)
include/trace/events/msr.h +6 -0
@@ -40,6 +40,12 @@ DEFINE_EVENT(msr_trace_class, write_msr,TP_ARGS (msr, val, failed) ); +DEFINE_EVENT (msr_trace_class, rdpmc, + TP_PROTO(unsigned msr, u64 val, int failed), +TP_ARGS (msr, val, failed) +); + +#endif /* _TRACE_MSR_H */ /* This part must be outside protection */
Signature · 1 line
1.9.3