aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorStephane Eranian <eranian@hpl.hp.com>2007-09-06 10:59:51 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-09-10 21:57:47 -0400
commit23d5ea5d3edcfe899cd91fca87a4af799bcc5794 (patch)
treeae68487fea0c1c2642260e54b24c11135edfa622 /arch
parentb8da0d1c27f144bce999c653467106f3f0d5a308 (diff)
i386: Fix perfctr watchdog on core duo
Fix the NMI watchdog on Intel CoreDuo processor where the kernel would get stuck during boot. The issue is related to errata AE49, where the PERFEVTSEL1 counter does not have a working enable bit. Thus it is not possible to use it for NMI. The patch creates a dedicated wd_ops for CoreDuo which falls back to using PERFEVTSEL0. The other Intel processors supporting the architectural PMU will keep on using PERFEVTSEL1 as this allows other subsystems, such as perfmon, to use PERFEVTSEL0 for PEBS monitoring in particular. Bug initially reported by Daniel Walker. AK: Added comments Signed-off-by: Stephane Eranian <eranian@hpl.hp.com> Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/i386/kernel/cpu/perfctr-watchdog.c28
1 files changed, 22 insertions, 6 deletions
diff --git a/arch/i386/kernel/cpu/perfctr-watchdog.c b/arch/i386/kernel/cpu/perfctr-watchdog.c
index 4be488e73bee..93fecd4b03de 100644
--- a/arch/i386/kernel/cpu/perfctr-watchdog.c
+++ b/arch/i386/kernel/cpu/perfctr-watchdog.c
@@ -263,8 +263,8 @@ static int setup_k7_watchdog(unsigned nmi_hz)
263 unsigned int evntsel; 263 unsigned int evntsel;
264 struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); 264 struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
265 265
266 perfctr_msr = MSR_K7_PERFCTR0; 266 perfctr_msr = wd_ops->perfctr;
267 evntsel_msr = MSR_K7_EVNTSEL0; 267 evntsel_msr = wd_ops->evntsel;
268 268
269 wrmsrl(perfctr_msr, 0UL); 269 wrmsrl(perfctr_msr, 0UL);
270 270
@@ -343,8 +343,8 @@ static int setup_p6_watchdog(unsigned nmi_hz)
343 unsigned int evntsel; 343 unsigned int evntsel;
344 struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); 344 struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
345 345
346 perfctr_msr = MSR_P6_PERFCTR0; 346 perfctr_msr = wd_ops->perfctr;
347 evntsel_msr = MSR_P6_EVNTSEL0; 347 evntsel_msr = wd_ops->evntsel;
348 348
349 /* KVM doesn't implement this MSR */ 349 /* KVM doesn't implement this MSR */
350 if (wrmsr_safe(perfctr_msr, 0, 0) < 0) 350 if (wrmsr_safe(perfctr_msr, 0, 0) < 0)
@@ -569,8 +569,8 @@ static int setup_intel_arch_watchdog(unsigned nmi_hz)
569 (ebx & ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT)) 569 (ebx & ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT))
570 return 0; 570 return 0;
571 571
572 perfctr_msr = MSR_ARCH_PERFMON_PERFCTR1; 572 perfctr_msr = wd_ops->perfctr;
573 evntsel_msr = MSR_ARCH_PERFMON_EVENTSEL1; 573 evntsel_msr = wd_ops->evntsel;
574 574
575 wrmsrl(perfctr_msr, 0UL); 575 wrmsrl(perfctr_msr, 0UL);
576 576
@@ -605,6 +605,16 @@ static struct wd_ops intel_arch_wd_ops = {
605 .evntsel = MSR_ARCH_PERFMON_EVENTSEL1, 605 .evntsel = MSR_ARCH_PERFMON_EVENTSEL1,
606}; 606};
607 607
608static struct wd_ops coreduo_wd_ops = {
609 .reserve = single_msr_reserve,
610 .unreserve = single_msr_unreserve,
611 .setup = setup_intel_arch_watchdog,
612 .rearm = p6_rearm,
613 .stop = single_msr_stop_watchdog,
614 .perfctr = MSR_ARCH_PERFMON_PERFCTR0,
615 .evntsel = MSR_ARCH_PERFMON_EVENTSEL0,
616};
617
608static void probe_nmi_watchdog(void) 618static void probe_nmi_watchdog(void)
609{ 619{
610 switch (boot_cpu_data.x86_vendor) { 620 switch (boot_cpu_data.x86_vendor) {
@@ -615,6 +625,12 @@ static void probe_nmi_watchdog(void)
615 wd_ops = &k7_wd_ops; 625 wd_ops = &k7_wd_ops;
616 break; 626 break;
617 case X86_VENDOR_INTEL: 627 case X86_VENDOR_INTEL:
628 /* Work around Core Duo (Yonah) errata AE49 where perfctr1
629 doesn't have a working enable bit. */
630 if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 14) {
631 wd_ops = &coreduo_wd_ops;
632 break;
633 }
618 if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) { 634 if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) {
619 wd_ops = &intel_arch_wd_ops; 635 wd_ops = &intel_arch_wd_ops;
620 break; 636 break;