diff options
author | Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> | 2007-02-13 07:26:22 -0500 |
---|---|---|
committer | Andi Kleen <andi@basil.nowhere.org> | 2007-02-13 07:26:22 -0500 |
commit | 90ce4bc4542c10b63dc6482ac920ff1226a6e5ff (patch) | |
tree | 2fe3baa98028ddb28ffca83930d0bc63d50c5792 /arch/i386/kernel/nmi.c | |
parent | 1676193937a538fdb92a2916a86a705093cfd613 (diff) |
[PATCH] i386: Handle 32 bit PerfMon Counter writes cleanly in i386 nmi_watchdog
Change i386 nmi handler to handle 32 bit perfmon counter MSR writes cleanly.
Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Diffstat (limited to 'arch/i386/kernel/nmi.c')
-rw-r--r-- | arch/i386/kernel/nmi.c | 64 |
1 files changed, 48 insertions, 16 deletions
diff --git a/arch/i386/kernel/nmi.c b/arch/i386/kernel/nmi.c index 1a6f8bb8881c..7d3f4e22d6fb 100644 --- a/arch/i386/kernel/nmi.c +++ b/arch/i386/kernel/nmi.c | |||
@@ -216,6 +216,28 @@ static __init void nmi_cpu_busy(void *data) | |||
216 | } | 216 | } |
217 | #endif | 217 | #endif |
218 | 218 | ||
219 | static unsigned int adjust_for_32bit_ctr(unsigned int hz) | ||
220 | { | ||
221 | u64 counter_val; | ||
222 | unsigned int retval = hz; | ||
223 | |||
224 | /* | ||
225 | * On Intel CPUs with P6/ARCH_PERFMON only 32 bits in the counter | ||
226 | * are writable, with higher bits sign extending from bit 31. | ||
227 | * So, we can only program the counter with 31 bit values and | ||
228 | * 32nd bit should be 1, for 33.. to be 1. | ||
229 | * Find the appropriate nmi_hz | ||
230 | */ | ||
231 | counter_val = (u64)cpu_khz * 1000; | ||
232 | do_div(counter_val, retval); | ||
233 | if (counter_val > 0x7fffffffULL) { | ||
234 | u64 count = (u64)cpu_khz * 1000; | ||
235 | do_div(count, 0x7fffffffUL); | ||
236 | retval = count + 1; | ||
237 | } | ||
238 | return retval; | ||
239 | } | ||
240 | |||
219 | static int __init check_nmi_watchdog(void) | 241 | static int __init check_nmi_watchdog(void) |
220 | { | 242 | { |
221 | unsigned int *prev_nmi_count; | 243 | unsigned int *prev_nmi_count; |
@@ -281,18 +303,10 @@ static int __init check_nmi_watchdog(void) | |||
281 | struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); | 303 | struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); |
282 | 304 | ||
283 | nmi_hz = 1; | 305 | nmi_hz = 1; |
284 | /* | 306 | |
285 | * On Intel CPUs with ARCH_PERFMON only 32 bits in the counter | 307 | if (wd->perfctr_msr == MSR_P6_PERFCTR0 || |
286 | * are writable, with higher bits sign extending from bit 31. | 308 | wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0) { |
287 | * So, we can only program the counter with 31 bit values and | 309 | nmi_hz = adjust_for_32bit_ctr(nmi_hz); |
288 | * 32nd bit should be 1, for 33.. to be 1. | ||
289 | * Find the appropriate nmi_hz | ||
290 | */ | ||
291 | if (wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0 && | ||
292 | ((u64)cpu_khz * 1000) > 0x7fffffffULL) { | ||
293 | u64 count = (u64)cpu_khz * 1000; | ||
294 | do_div(count, 0x7fffffffUL); | ||
295 | nmi_hz = count + 1; | ||
296 | } | 310 | } |
297 | } | 311 | } |
298 | 312 | ||
@@ -442,6 +456,17 @@ static void write_watchdog_counter(unsigned int perfctr_msr, const char *descr) | |||
442 | wrmsrl(perfctr_msr, 0 - count); | 456 | wrmsrl(perfctr_msr, 0 - count); |
443 | } | 457 | } |
444 | 458 | ||
459 | static void write_watchdog_counter32(unsigned int perfctr_msr, | ||
460 | const char *descr) | ||
461 | { | ||
462 | u64 count = (u64)cpu_khz * 1000; | ||
463 | |||
464 | do_div(count, nmi_hz); | ||
465 | if(descr) | ||
466 | Dprintk("setting %s to -0x%08Lx\n", descr, count); | ||
467 | wrmsr(perfctr_msr, (u32)(-count), 0); | ||
468 | } | ||
469 | |||
445 | /* Note that these events don't tick when the CPU idles. This means | 470 | /* Note that these events don't tick when the CPU idles. This means |
446 | the frequency varies with CPU load. */ | 471 | the frequency varies with CPU load. */ |
447 | 472 | ||
@@ -531,7 +556,8 @@ static int setup_p6_watchdog(void) | |||
531 | 556 | ||
532 | /* setup the timer */ | 557 | /* setup the timer */ |
533 | wrmsr(evntsel_msr, evntsel, 0); | 558 | wrmsr(evntsel_msr, evntsel, 0); |
534 | write_watchdog_counter(perfctr_msr, "P6_PERFCTR0"); | 559 | nmi_hz = adjust_for_32bit_ctr(nmi_hz); |
560 | write_watchdog_counter32(perfctr_msr, "P6_PERFCTR0"); | ||
535 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 561 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
536 | evntsel |= P6_EVNTSEL0_ENABLE; | 562 | evntsel |= P6_EVNTSEL0_ENABLE; |
537 | wrmsr(evntsel_msr, evntsel, 0); | 563 | wrmsr(evntsel_msr, evntsel, 0); |
@@ -704,7 +730,8 @@ static int setup_intel_arch_watchdog(void) | |||
704 | 730 | ||
705 | /* setup the timer */ | 731 | /* setup the timer */ |
706 | wrmsr(evntsel_msr, evntsel, 0); | 732 | wrmsr(evntsel_msr, evntsel, 0); |
707 | write_watchdog_counter(perfctr_msr, "INTEL_ARCH_PERFCTR0"); | 733 | nmi_hz = adjust_for_32bit_ctr(nmi_hz); |
734 | write_watchdog_counter32(perfctr_msr, "INTEL_ARCH_PERFCTR0"); | ||
708 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 735 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
709 | evntsel |= ARCH_PERFMON_EVENTSEL0_ENABLE; | 736 | evntsel |= ARCH_PERFMON_EVENTSEL0_ENABLE; |
710 | wrmsr(evntsel_msr, evntsel, 0); | 737 | wrmsr(evntsel_msr, evntsel, 0); |
@@ -956,6 +983,8 @@ __kprobes int nmi_watchdog_tick(struct pt_regs * regs, unsigned reason) | |||
956 | dummy &= ~P4_CCCR_OVF; | 983 | dummy &= ~P4_CCCR_OVF; |
957 | wrmsrl(wd->cccr_msr, dummy); | 984 | wrmsrl(wd->cccr_msr, dummy); |
958 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 985 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
986 | /* start the cycle over again */ | ||
987 | write_watchdog_counter(wd->perfctr_msr, NULL); | ||
959 | } | 988 | } |
960 | else if (wd->perfctr_msr == MSR_P6_PERFCTR0 || | 989 | else if (wd->perfctr_msr == MSR_P6_PERFCTR0 || |
961 | wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0) { | 990 | wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0) { |
@@ -964,9 +993,12 @@ __kprobes int nmi_watchdog_tick(struct pt_regs * regs, unsigned reason) | |||
964 | * other P6 variant. | 993 | * other P6 variant. |
965 | * ArchPerfom/Core Duo also needs this */ | 994 | * ArchPerfom/Core Duo also needs this */ |
966 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 995 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
996 | /* P6/ARCH_PERFMON has 32 bit counter write */ | ||
997 | write_watchdog_counter32(wd->perfctr_msr, NULL); | ||
998 | } else { | ||
999 | /* start the cycle over again */ | ||
1000 | write_watchdog_counter(wd->perfctr_msr, NULL); | ||
967 | } | 1001 | } |
968 | /* start the cycle over again */ | ||
969 | write_watchdog_counter(wd->perfctr_msr, NULL); | ||
970 | rc = 1; | 1002 | rc = 1; |
971 | } else if (nmi_watchdog == NMI_IO_APIC) { | 1003 | } else if (nmi_watchdog == NMI_IO_APIC) { |
972 | /* don't know how to accurately check for this. | 1004 | /* don't know how to accurately check for this. |