diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2010-12-08 09:56:23 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-12-16 05:36:42 -0500 |
commit | 4407204c5c9037763aadce39b025529dfbfcac9e (patch) | |
tree | e9493f1e9f485c5299a07d5b618b6c983029aa65 | |
parent | 006b20fe4c69189b0d854e5eabf269e50ca86cdd (diff) |
perf, x86: Detect broken BIOSes that corrupt the PMU
Some BIOSes use PMU resources, which can cause various bugs:
- Non-working or erratic PMU based statistics - the PMU can end up
counting the wrong thing, resulting in misleading statistics
- Profiling can stop working or it can profile the wrong thing
- A non-working or erratic NMI watchdog that cannot be relied on
- The kernel may disturb whatever thing the BIOS tries to use the
PMU for - possibly causing hardware malfunction in extreme cases.
- ... and other forms of potential misbehavior
Various forms of such misbehavior has been observed in practice - there are
BIOSes that just corrupt the PMU state, consequences be damned.
The PMU is a CPU resource that is handled by the kernel and the BIOS
stealing+corrupting it is not acceptable nor robust, so we detect it,
warn about it and further refuse to touch the PMU ourselves.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Jason Wessel <jason.wessel@windriver.com>
Cc: Don Zickus <dzickus@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 48 |
1 files changed, 42 insertions, 6 deletions
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 817d2b195e8e..ce27c547fe78 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -375,15 +375,53 @@ static void release_pmc_hardware(void) {} | |||
375 | static bool check_hw_exists(void) | 375 | static bool check_hw_exists(void) |
376 | { | 376 | { |
377 | u64 val, val_new = 0; | 377 | u64 val, val_new = 0; |
378 | int ret = 0; | 378 | int i, reg, ret = 0; |
379 | |||
380 | /* | ||
381 | * Check to see if the BIOS enabled any of the counters, if so | ||
382 | * complain and bail. | ||
383 | */ | ||
384 | for (i = 0; i < x86_pmu.num_counters; i++) { | ||
385 | reg = x86_pmu.eventsel + i; | ||
386 | ret = rdmsrl_safe(reg, &val); | ||
387 | if (ret) | ||
388 | goto msr_fail; | ||
389 | if (val & ARCH_PERFMON_EVENTSEL_ENABLE) | ||
390 | goto bios_fail; | ||
391 | } | ||
392 | |||
393 | if (x86_pmu.num_counters_fixed) { | ||
394 | reg = MSR_ARCH_PERFMON_FIXED_CTR_CTRL; | ||
395 | ret = rdmsrl_safe(reg, &val); | ||
396 | if (ret) | ||
397 | goto msr_fail; | ||
398 | for (i = 0; i < x86_pmu.num_counters_fixed; i++) { | ||
399 | if (val & (0x03 << i*4)) | ||
400 | goto bios_fail; | ||
401 | } | ||
402 | } | ||
379 | 403 | ||
404 | /* | ||
405 | * Now write a value and read it back to see if it matches, | ||
406 | * this is needed to detect certain hardware emulators (qemu/kvm) | ||
407 | * that don't trap on the MSR access and always return 0s. | ||
408 | */ | ||
380 | val = 0xabcdUL; | 409 | val = 0xabcdUL; |
381 | ret |= checking_wrmsrl(x86_pmu.perfctr, val); | 410 | ret = checking_wrmsrl(x86_pmu.perfctr, val); |
382 | ret |= rdmsrl_safe(x86_pmu.perfctr, &val_new); | 411 | ret |= rdmsrl_safe(x86_pmu.perfctr, &val_new); |
383 | if (ret || val != val_new) | 412 | if (ret || val != val_new) |
384 | return false; | 413 | goto msr_fail; |
385 | 414 | ||
386 | return true; | 415 | return true; |
416 | |||
417 | bios_fail: | ||
418 | printk(KERN_CONT "Broken BIOS detected, using software events only.\n"); | ||
419 | printk(KERN_ERR FW_BUG "the BIOS has corrupted hw-PMU resources (MSR %x is %Lx)\n", reg, val); | ||
420 | return false; | ||
421 | |||
422 | msr_fail: | ||
423 | printk(KERN_CONT "Broken PMU hardware detected, using software events only.\n"); | ||
424 | return false; | ||
387 | } | 425 | } |
388 | 426 | ||
389 | static void reserve_ds_buffers(void); | 427 | static void reserve_ds_buffers(void); |
@@ -1378,10 +1416,8 @@ int __init init_hw_perf_events(void) | |||
1378 | pmu_check_apic(); | 1416 | pmu_check_apic(); |
1379 | 1417 | ||
1380 | /* sanity check that the hardware exists or is emulated */ | 1418 | /* sanity check that the hardware exists or is emulated */ |
1381 | if (!check_hw_exists()) { | 1419 | if (!check_hw_exists()) |
1382 | pr_cont("Broken PMU hardware detected, software events only.\n"); | ||
1383 | return 0; | 1420 | return 0; |
1384 | } | ||
1385 | 1421 | ||
1386 | pr_cont("%s PMU driver.\n", x86_pmu.name); | 1422 | pr_cont("%s PMU driver.\n", x86_pmu.name); |
1387 | 1423 | ||