|
@@ -26,6 +26,7 @@
|
|
|
#include <asm/smp.h>
|
|
|
#include <asm/nmi.h>
|
|
|
#include <asm/kdebug.h>
|
|
|
+#include <asm/intel_arch_perfmon.h>
|
|
|
|
|
|
#include "mach_traps.h"
|
|
|
|
|
@@ -77,6 +78,9 @@ static inline unsigned int nmi_perfctr_msr_to_bit(unsigned int msr)
|
|
|
case X86_VENDOR_AMD:
|
|
|
return (msr - MSR_K7_PERFCTR0);
|
|
|
case X86_VENDOR_INTEL:
|
|
|
+ if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON))
|
|
|
+ return (msr - MSR_ARCH_PERFMON_PERFCTR0);
|
|
|
+
|
|
|
switch (boot_cpu_data.x86) {
|
|
|
case 6:
|
|
|
return (msr - MSR_P6_PERFCTR0);
|
|
@@ -95,6 +99,9 @@ static inline unsigned int nmi_evntsel_msr_to_bit(unsigned int msr)
|
|
|
case X86_VENDOR_AMD:
|
|
|
return (msr - MSR_K7_EVNTSEL0);
|
|
|
case X86_VENDOR_INTEL:
|
|
|
+ if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON))
|
|
|
+ return (msr - MSR_ARCH_PERFMON_EVENTSEL0);
|
|
|
+
|
|
|
switch (boot_cpu_data.x86) {
|
|
|
case 6:
|
|
|
return (msr - MSR_P6_EVNTSEL0);
|
|
@@ -174,7 +181,10 @@ static __cpuinit inline int nmi_known_cpu(void)
|
|
|
case X86_VENDOR_AMD:
|
|
|
return ((boot_cpu_data.x86 == 15) || (boot_cpu_data.x86 == 6));
|
|
|
case X86_VENDOR_INTEL:
|
|
|
- return ((boot_cpu_data.x86 == 15) || (boot_cpu_data.x86 == 6));
|
|
|
+ if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON))
|
|
|
+ return 1;
|
|
|
+ else
|
|
|
+ return ((boot_cpu_data.x86 == 15) || (boot_cpu_data.x86 == 6));
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
@@ -261,8 +271,24 @@ static int __init check_nmi_watchdog(void)
|
|
|
|
|
|
/* now that we know it works we can reduce NMI frequency to
|
|
|
something more reasonable; makes a difference in some configs */
|
|
|
- if (nmi_watchdog == NMI_LOCAL_APIC)
|
|
|
+ if (nmi_watchdog == NMI_LOCAL_APIC) {
|
|
|
+ struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
|
|
|
+
|
|
|
nmi_hz = 1;
|
|
|
+ /*
|
|
|
+ * On Intel CPUs with ARCH_PERFMON only 32 bits in the counter
|
|
|
+ * are writable, with higher bits sign extending from bit 31.
|
|
|
+ * So, we can only program the counter with 31 bit values and
|
|
|
+ * 32nd bit should be 1, for 33.. to be 1.
|
|
|
+ * Find the appropriate nmi_hz
|
|
|
+ */
|
|
|
+ if (wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0 &&
|
|
|
+ ((u64)cpu_khz * 1000) > 0x7fffffffULL) {
|
|
|
+ u64 count = (u64)cpu_khz * 1000;
|
|
|
+ do_div(count, 0x7fffffffUL);
|
|
|
+ nmi_hz = count + 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
kfree(prev_nmi_count);
|
|
|
return 0;
|
|
@@ -637,6 +663,85 @@ static void stop_p4_watchdog(void)
|
|
|
release_perfctr_nmi(wd->perfctr_msr);
|
|
|
}
|
|
|
|
|
|
+#define ARCH_PERFMON_NMI_EVENT_SEL ARCH_PERFMON_UNHALTED_CORE_CYCLES_SEL
|
|
|
+#define ARCH_PERFMON_NMI_EVENT_UMASK ARCH_PERFMON_UNHALTED_CORE_CYCLES_UMASK
|
|
|
+
|
|
|
+static int setup_intel_arch_watchdog(void)
|
|
|
+{
|
|
|
+ unsigned int ebx;
|
|
|
+ union cpuid10_eax eax;
|
|
|
+ unsigned int unused;
|
|
|
+ unsigned int perfctr_msr, evntsel_msr;
|
|
|
+ unsigned int evntsel;
|
|
|
+ struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Check whether the Architectural PerfMon supports
|
|
|
+ * Unhalted Core Cycles Event or not.
|
|
|
+ * NOTE: Corresponding bit = 0 in ebx indicates event present.
|
|
|
+ */
|
|
|
+ cpuid(10, &(eax.full), &ebx, &unused, &unused);
|
|
|
+ if ((eax.split.mask_length < (ARCH_PERFMON_UNHALTED_CORE_CYCLES_INDEX+1)) ||
|
|
|
+ (ebx & ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT))
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ perfctr_msr = MSR_ARCH_PERFMON_PERFCTR0;
|
|
|
+ evntsel_msr = MSR_ARCH_PERFMON_EVENTSEL0;
|
|
|
+
|
|
|
+ if (!reserve_perfctr_nmi(perfctr_msr))
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ if (!reserve_evntsel_nmi(evntsel_msr))
|
|
|
+ goto fail1;
|
|
|
+
|
|
|
+ wrmsrl(perfctr_msr, 0UL);
|
|
|
+
|
|
|
+ evntsel = ARCH_PERFMON_EVENTSEL_INT
|
|
|
+ | ARCH_PERFMON_EVENTSEL_OS
|
|
|
+ | ARCH_PERFMON_EVENTSEL_USR
|
|
|
+ | ARCH_PERFMON_NMI_EVENT_SEL
|
|
|
+ | ARCH_PERFMON_NMI_EVENT_UMASK;
|
|
|
+
|
|
|
+ /* setup the timer */
|
|
|
+ wrmsr(evntsel_msr, evntsel, 0);
|
|
|
+ write_watchdog_counter(perfctr_msr, "INTEL_ARCH_PERFCTR0");
|
|
|
+ apic_write(APIC_LVTPC, APIC_DM_NMI);
|
|
|
+ evntsel |= ARCH_PERFMON_EVENTSEL0_ENABLE;
|
|
|
+ wrmsr(evntsel_msr, evntsel, 0);
|
|
|
+
|
|
|
+ wd->perfctr_msr = perfctr_msr;
|
|
|
+ wd->evntsel_msr = evntsel_msr;
|
|
|
+ wd->cccr_msr = 0; //unused
|
|
|
+ wd->check_bit = 1ULL << (eax.split.bit_width - 1);
|
|
|
+ return 1;
|
|
|
+fail1:
|
|
|
+ release_perfctr_nmi(perfctr_msr);
|
|
|
+fail:
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void stop_intel_arch_watchdog(void)
|
|
|
+{
|
|
|
+ unsigned int ebx;
|
|
|
+ union cpuid10_eax eax;
|
|
|
+ unsigned int unused;
|
|
|
+ struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Check whether the Architectural PerfMon supports
|
|
|
+ * Unhalted Core Cycles Event or not.
|
|
|
+ * NOTE: Corresponding bit = 0 in ebx indicates event present.
|
|
|
+ */
|
|
|
+ cpuid(10, &(eax.full), &ebx, &unused, &unused);
|
|
|
+ if ((eax.split.mask_length < (ARCH_PERFMON_UNHALTED_CORE_CYCLES_INDEX+1)) ||
|
|
|
+ (ebx & ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT))
|
|
|
+ return;
|
|
|
+
|
|
|
+ wrmsr(wd->evntsel_msr, 0, 0);
|
|
|
+ release_evntsel_nmi(wd->evntsel_msr);
|
|
|
+ release_perfctr_nmi(wd->perfctr_msr);
|
|
|
+}
|
|
|
+
|
|
|
void setup_apic_nmi_watchdog (void *unused)
|
|
|
{
|
|
|
struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
|
|
@@ -663,6 +768,11 @@ void setup_apic_nmi_watchdog (void *unused)
|
|
|
return;
|
|
|
break;
|
|
|
case X86_VENDOR_INTEL:
|
|
|
+ if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) {
|
|
|
+ if (!setup_intel_arch_watchdog())
|
|
|
+ return;
|
|
|
+ break;
|
|
|
+ }
|
|
|
switch (boot_cpu_data.x86) {
|
|
|
case 6:
|
|
|
if (boot_cpu_data.x86_model > 0xd)
|
|
@@ -708,6 +818,10 @@ void stop_apic_nmi_watchdog(void *unused)
|
|
|
stop_k7_watchdog();
|
|
|
break;
|
|
|
case X86_VENDOR_INTEL:
|
|
|
+ if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) {
|
|
|
+ stop_intel_arch_watchdog();
|
|
|
+ break;
|
|
|
+ }
|
|
|
switch (boot_cpu_data.x86) {
|
|
|
case 6:
|
|
|
if (boot_cpu_data.x86_model > 0xd)
|
|
@@ -831,10 +945,12 @@ int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
|
|
|
wrmsrl(wd->cccr_msr, dummy);
|
|
|
apic_write(APIC_LVTPC, APIC_DM_NMI);
|
|
|
}
|
|
|
- else if (wd->perfctr_msr == MSR_P6_PERFCTR0) {
|
|
|
- /* Only P6 based Pentium M need to re-unmask
|
|
|
+ else if (wd->perfctr_msr == MSR_P6_PERFCTR0 ||
|
|
|
+ wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0) {
|
|
|
+ /* P6 based Pentium M need to re-unmask
|
|
|
* the apic vector but it doesn't hurt
|
|
|
- * other P6 variant */
|
|
|
+ * other P6 variant.
|
|
|
+ * ArchPerfom/Core Duo also needs this */
|
|
|
apic_write(APIC_LVTPC, APIC_DM_NMI);
|
|
|
}
|
|
|
/* start the cycle over again */
|