Commit 1c5ac21a authored by Alexander Shishkin's avatar Alexander Shishkin Committed by Ingo Molnar

perf/x86/intel/pt: Don't die on VMXON

Some versions of Intel PT do not support tracing across VMXON, more
specifically, VMXON will clear TraceEn control bit and any attempt to
set it before VMXOFF will throw a #GP, which in the current state of
things will crash the kernel. Namely:

  $ perf record -e intel_pt// kvm -nographic

on such a machine will kill it.

To avoid this, notify the intel_pt driver before VMXON and after
VMXOFF so that it knows when not to enable itself.
Signed-off-by: default avatarAlexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Gleb Natapov <gleb@kernel.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vince Weaver <vincent.weaver@maine.edu>
Cc: hpa@zytor.com
Link: http://lkml.kernel.org/r/87oa9dwrfk.fsf@ashishki-desk.ger.corp.intel.comSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 79c9ce57
...@@ -136,9 +136,21 @@ static int __init pt_pmu_hw_init(void) ...@@ -136,9 +136,21 @@ static int __init pt_pmu_hw_init(void)
struct dev_ext_attribute *de_attrs; struct dev_ext_attribute *de_attrs;
struct attribute **attrs; struct attribute **attrs;
size_t size; size_t size;
u64 reg;
int ret; int ret;
long i; long i;
if (boot_cpu_has(X86_FEATURE_VMX)) {
/*
* Intel SDM, 36.5 "Tracing post-VMXON" says that
* "IA32_VMX_MISC[bit 14]" being 1 means PT can trace
* post-VMXON.
*/
rdmsrl(MSR_IA32_VMX_MISC, reg);
if (reg & BIT(14))
pt_pmu.vmx = true;
}
attrs = NULL; attrs = NULL;
for (i = 0; i < PT_CPUID_LEAVES; i++) { for (i = 0; i < PT_CPUID_LEAVES; i++) {
...@@ -269,20 +281,23 @@ static void pt_config(struct perf_event *event) ...@@ -269,20 +281,23 @@ static void pt_config(struct perf_event *event)
reg |= (event->attr.config & PT_CONFIG_MASK); reg |= (event->attr.config & PT_CONFIG_MASK);
event->hw.config = reg;
wrmsrl(MSR_IA32_RTIT_CTL, reg); wrmsrl(MSR_IA32_RTIT_CTL, reg);
} }
static void pt_config_start(bool start) static void pt_config_stop(struct perf_event *event)
{ {
u64 ctl; u64 ctl = READ_ONCE(event->hw.config);
/* may be already stopped by a PMI */
if (!(ctl & RTIT_CTL_TRACEEN))
return;
rdmsrl(MSR_IA32_RTIT_CTL, ctl);
if (start)
ctl |= RTIT_CTL_TRACEEN;
else
ctl &= ~RTIT_CTL_TRACEEN; ctl &= ~RTIT_CTL_TRACEEN;
wrmsrl(MSR_IA32_RTIT_CTL, ctl); wrmsrl(MSR_IA32_RTIT_CTL, ctl);
WRITE_ONCE(event->hw.config, ctl);
/* /*
* A wrmsr that disables trace generation serializes other PT * A wrmsr that disables trace generation serializes other PT
* registers and causes all data packets to be written to memory, * registers and causes all data packets to be written to memory,
...@@ -291,7 +306,6 @@ static void pt_config_start(bool start) ...@@ -291,7 +306,6 @@ static void pt_config_start(bool start)
* The below WMB, separating data store and aux_head store matches * The below WMB, separating data store and aux_head store matches
* the consumer's RMB that separates aux_head load and data load. * the consumer's RMB that separates aux_head load and data load.
*/ */
if (!start)
wmb(); wmb();
} }
...@@ -942,11 +956,17 @@ void intel_pt_interrupt(void) ...@@ -942,11 +956,17 @@ void intel_pt_interrupt(void)
if (!ACCESS_ONCE(pt->handle_nmi)) if (!ACCESS_ONCE(pt->handle_nmi))
return; return;
pt_config_start(false); /*
* If VMX is on and PT does not support it, don't touch anything.
*/
if (READ_ONCE(pt->vmx_on))
return;
if (!event) if (!event)
return; return;
pt_config_stop(event);
buf = perf_get_aux(&pt->handle); buf = perf_get_aux(&pt->handle);
if (!buf) if (!buf)
return; return;
...@@ -983,6 +1003,35 @@ void intel_pt_interrupt(void) ...@@ -983,6 +1003,35 @@ void intel_pt_interrupt(void)
} }
} }
void intel_pt_handle_vmx(int on)
{
struct pt *pt = this_cpu_ptr(&pt_ctx);
struct perf_event *event;
unsigned long flags;
/* PT plays nice with VMX, do nothing */
if (pt_pmu.vmx)
return;
/*
* VMXON will clear RTIT_CTL.TraceEn; we need to make
* sure to not try to set it while VMX is on. Disable
* interrupts to avoid racing with pmu callbacks;
* concurrent PMI should be handled fine.
*/
local_irq_save(flags);
WRITE_ONCE(pt->vmx_on, on);
if (on) {
/* prevent pt_config_stop() from writing RTIT_CTL */
event = pt->handle.event;
if (event)
event->hw.config = 0;
}
local_irq_restore(flags);
}
EXPORT_SYMBOL_GPL(intel_pt_handle_vmx);
/* /*
* PMU callbacks * PMU callbacks
*/ */
...@@ -992,6 +1041,9 @@ static void pt_event_start(struct perf_event *event, int mode) ...@@ -992,6 +1041,9 @@ static void pt_event_start(struct perf_event *event, int mode)
struct pt *pt = this_cpu_ptr(&pt_ctx); struct pt *pt = this_cpu_ptr(&pt_ctx);
struct pt_buffer *buf = perf_get_aux(&pt->handle); struct pt_buffer *buf = perf_get_aux(&pt->handle);
if (READ_ONCE(pt->vmx_on))
return;
if (!buf || pt_buffer_is_full(buf, pt)) { if (!buf || pt_buffer_is_full(buf, pt)) {
event->hw.state = PERF_HES_STOPPED; event->hw.state = PERF_HES_STOPPED;
return; return;
...@@ -1014,7 +1066,8 @@ static void pt_event_stop(struct perf_event *event, int mode) ...@@ -1014,7 +1066,8 @@ static void pt_event_stop(struct perf_event *event, int mode)
* see comment in intel_pt_interrupt(). * see comment in intel_pt_interrupt().
*/ */
ACCESS_ONCE(pt->handle_nmi) = 0; ACCESS_ONCE(pt->handle_nmi) = 0;
pt_config_start(false);
pt_config_stop(event);
if (event->hw.state == PERF_HES_STOPPED) if (event->hw.state == PERF_HES_STOPPED)
return; return;
......
...@@ -65,6 +65,7 @@ enum pt_capabilities { ...@@ -65,6 +65,7 @@ enum pt_capabilities {
struct pt_pmu { struct pt_pmu {
struct pmu pmu; struct pmu pmu;
u32 caps[PT_CPUID_REGS_NUM * PT_CPUID_LEAVES]; u32 caps[PT_CPUID_REGS_NUM * PT_CPUID_LEAVES];
bool vmx;
}; };
/** /**
...@@ -107,10 +108,12 @@ struct pt_buffer { ...@@ -107,10 +108,12 @@ struct pt_buffer {
* struct pt - per-cpu pt context * struct pt - per-cpu pt context
* @handle: perf output handle * @handle: perf output handle
* @handle_nmi: do handle PT PMI on this cpu, there's an active event * @handle_nmi: do handle PT PMI on this cpu, there's an active event
* @vmx_on: 1 if VMX is ON on this cpu
*/ */
struct pt { struct pt {
struct perf_output_handle handle; struct perf_output_handle handle;
int handle_nmi; int handle_nmi;
int vmx_on;
}; };
#endif /* __INTEL_PT_H__ */ #endif /* __INTEL_PT_H__ */
...@@ -285,6 +285,10 @@ static inline void perf_events_lapic_init(void) { } ...@@ -285,6 +285,10 @@ static inline void perf_events_lapic_init(void) { }
static inline void perf_check_microcode(void) { } static inline void perf_check_microcode(void) { }
#endif #endif
#ifdef CONFIG_CPU_SUP_INTEL
extern void intel_pt_handle_vmx(int on);
#endif
#if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD) #if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD)
extern void amd_pmu_enable_virt(void); extern void amd_pmu_enable_virt(void);
extern void amd_pmu_disable_virt(void); extern void amd_pmu_disable_virt(void);
......
...@@ -3103,6 +3103,8 @@ static __init int vmx_disabled_by_bios(void) ...@@ -3103,6 +3103,8 @@ static __init int vmx_disabled_by_bios(void)
static void kvm_cpu_vmxon(u64 addr) static void kvm_cpu_vmxon(u64 addr)
{ {
intel_pt_handle_vmx(1);
asm volatile (ASM_VMX_VMXON_RAX asm volatile (ASM_VMX_VMXON_RAX
: : "a"(&addr), "m"(addr) : : "a"(&addr), "m"(addr)
: "memory", "cc"); : "memory", "cc");
...@@ -3172,6 +3174,8 @@ static void vmclear_local_loaded_vmcss(void) ...@@ -3172,6 +3174,8 @@ static void vmclear_local_loaded_vmcss(void)
static void kvm_cpu_vmxoff(void) static void kvm_cpu_vmxoff(void)
{ {
asm volatile (__ex(ASM_VMX_VMXOFF) : : : "cc"); asm volatile (__ex(ASM_VMX_VMXOFF) : : : "cc");
intel_pt_handle_vmx(0);
} }
static void hardware_disable(void) static void hardware_disable(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment