Commit a5ebe0ba authored by George Dunlap's avatar George Dunlap Committed by Ingo Molnar

perf/x86: Check all MSRs before passing hw check

check_hw_exists() has a number of checks which go to two exit
paths: msr_fail and bios_fail.  Checks classified as msr_fail
will cause check_hw_exists() to return false, causing the PMU
not to be used; bios_fail checks will only cause a warning to be
printed, but will return true.

The problem is that if there are both msr failures and bios
failures, and the routine hits a bios_fail check first, it will
exit early and return true, not finishing the rest of the msr
checks.  If those msrs are in fact broken, it will cause them to
be used erroneously.

In the case of a Xen PV VM, the guest OS has read access to all
the MSRs, but write access is white-listed to supported
features.  Writes to unsupported MSRs have no effect.  The PMU
MSRs are not (typically) supported, because they are expensive
to save and restore on a VM context switch.  One of the
"msr_fail" checks is supposed to detect this circumstance (ether
for Xen or KVM) and disable the harware PMU.

However, on one of my AMD boxen, there is (apparently) a broken
BIOS which triggers one of the bios_fail checks.  In particular,
MSR_K7_EVNTSEL0 has the ARCH_PERFMON_EVENTSEL_ENABLE bit set.
The guest kernel detects this because it has read access to all
MSRs, and causes it to skip the rest of the checks and try to
use the non-existent hardware PMU.  This minimally causes a lot
of useless instruction emulation and Xen console spam; it may
cause other issues with the watchdog as well.

This changset causes check_hw_exists() to go through all of the
msr checks, failing and returning false if any of them fail.
This makes sure that a guest running under Xen without a virtual
PMU will detect that there is no functioning PMU and not attempt
to use it.

This problem affects kernels as far back as 3.2, and should thus
be considered for backport.
Signed-off-by: default avatarGeorge Dunlap <george.dunlap@eu.citrix.com>
Cc: Konrad Wilk <konrad.wilk@oracle.com>
Cc: Ian Campbell <ian.campbell@citrix.com>
Cc: David Vrabel <david.vrabel@citrix.com>
Cc: Andrew Cooper <andrew.cooper3@citrix.com>
Link: http://lkml.kernel.org/r/1365000388-32448-1-git-send-email-george.dunlap@eu.citrix.comSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent c43ca509
...@@ -180,8 +180,9 @@ static void release_pmc_hardware(void) {} ...@@ -180,8 +180,9 @@ static void release_pmc_hardware(void) {}
static bool check_hw_exists(void) static bool check_hw_exists(void)
{ {
u64 val, val_new = ~0; u64 val, val_fail, val_new= ~0;
int i, reg, ret = 0; int i, reg, reg_fail, ret = 0;
int bios_fail = 0;
/* /*
* Check to see if the BIOS enabled any of the counters, if so * Check to see if the BIOS enabled any of the counters, if so
...@@ -192,8 +193,11 @@ static bool check_hw_exists(void) ...@@ -192,8 +193,11 @@ static bool check_hw_exists(void)
ret = rdmsrl_safe(reg, &val); ret = rdmsrl_safe(reg, &val);
if (ret) if (ret)
goto msr_fail; goto msr_fail;
if (val & ARCH_PERFMON_EVENTSEL_ENABLE) if (val & ARCH_PERFMON_EVENTSEL_ENABLE) {
goto bios_fail; bios_fail = 1;
val_fail = val;
reg_fail = reg;
}
} }
if (x86_pmu.num_counters_fixed) { if (x86_pmu.num_counters_fixed) {
...@@ -202,8 +206,11 @@ static bool check_hw_exists(void) ...@@ -202,8 +206,11 @@ static bool check_hw_exists(void)
if (ret) if (ret)
goto msr_fail; goto msr_fail;
for (i = 0; i < x86_pmu.num_counters_fixed; i++) { for (i = 0; i < x86_pmu.num_counters_fixed; i++) {
if (val & (0x03 << i*4)) if (val & (0x03 << i*4)) {
goto bios_fail; bios_fail = 1;
val_fail = val;
reg_fail = reg;
}
} }
} }
...@@ -221,14 +228,13 @@ static bool check_hw_exists(void) ...@@ -221,14 +228,13 @@ static bool check_hw_exists(void)
if (ret || val != val_new) if (ret || val != val_new)
goto msr_fail; goto msr_fail;
return true;
bios_fail:
/* /*
* We still allow the PMU driver to operate: * We still allow the PMU driver to operate:
*/ */
if (bios_fail) {
printk(KERN_CONT "Broken BIOS detected, complain to your hardware vendor.\n"); printk(KERN_CONT "Broken BIOS detected, complain to your hardware vendor.\n");
printk(KERN_ERR FW_BUG "the BIOS has corrupted hw-PMU resources (MSR %x is %Lx)\n", reg, val); printk(KERN_ERR FW_BUG "the BIOS has corrupted hw-PMU resources (MSR %x is %Lx)\n", reg_fail, val_fail);
}
return true; return true;
......
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