diff options
Diffstat (limited to 'arch/x86_64/kernel/nmi.c')
-rw-r--r-- | arch/x86_64/kernel/nmi.c | 75 |
1 files changed, 60 insertions, 15 deletions
diff --git a/arch/x86_64/kernel/nmi.c b/arch/x86_64/kernel/nmi.c index 9cb42ecb7f89..486f4c61a948 100644 --- a/arch/x86_64/kernel/nmi.c +++ b/arch/x86_64/kernel/nmi.c | |||
@@ -172,7 +172,7 @@ static __cpuinit inline int nmi_known_cpu(void) | |||
172 | { | 172 | { |
173 | switch (boot_cpu_data.x86_vendor) { | 173 | switch (boot_cpu_data.x86_vendor) { |
174 | case X86_VENDOR_AMD: | 174 | case X86_VENDOR_AMD: |
175 | return boot_cpu_data.x86 == 15; | 175 | return boot_cpu_data.x86 == 15 || boot_cpu_data.x86 == 16; |
176 | case X86_VENDOR_INTEL: | 176 | case X86_VENDOR_INTEL: |
177 | if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) | 177 | if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) |
178 | return 1; | 178 | return 1; |
@@ -214,6 +214,23 @@ static __init void nmi_cpu_busy(void *data) | |||
214 | } | 214 | } |
215 | #endif | 215 | #endif |
216 | 216 | ||
217 | static unsigned int adjust_for_32bit_ctr(unsigned int hz) | ||
218 | { | ||
219 | unsigned int retval = hz; | ||
220 | |||
221 | /* | ||
222 | * On Intel CPUs with ARCH_PERFMON only 32 bits in the counter | ||
223 | * are writable, with higher bits sign extending from bit 31. | ||
224 | * So, we can only program the counter with 31 bit values and | ||
225 | * 32nd bit should be 1, for 33.. to be 1. | ||
226 | * Find the appropriate nmi_hz | ||
227 | */ | ||
228 | if ((((u64)cpu_khz * 1000) / retval) > 0x7fffffffULL) { | ||
229 | retval = ((u64)cpu_khz * 1000) / 0x7fffffffUL + 1; | ||
230 | } | ||
231 | return retval; | ||
232 | } | ||
233 | |||
217 | int __init check_nmi_watchdog (void) | 234 | int __init check_nmi_watchdog (void) |
218 | { | 235 | { |
219 | int *counts; | 236 | int *counts; |
@@ -268,17 +285,8 @@ int __init check_nmi_watchdog (void) | |||
268 | struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); | 285 | struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); |
269 | 286 | ||
270 | nmi_hz = 1; | 287 | nmi_hz = 1; |
271 | /* | 288 | if (wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0) |
272 | * On Intel CPUs with ARCH_PERFMON only 32 bits in the counter | 289 | nmi_hz = adjust_for_32bit_ctr(nmi_hz); |
273 | * are writable, with higher bits sign extending from bit 31. | ||
274 | * So, we can only program the counter with 31 bit values and | ||
275 | * 32nd bit should be 1, for 33.. to be 1. | ||
276 | * Find the appropriate nmi_hz | ||
277 | */ | ||
278 | if (wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0 && | ||
279 | ((u64)cpu_khz * 1000) > 0x7fffffffULL) { | ||
280 | nmi_hz = ((u64)cpu_khz * 1000) / 0x7fffffffUL + 1; | ||
281 | } | ||
282 | } | 290 | } |
283 | 291 | ||
284 | kfree(counts); | 292 | kfree(counts); |
@@ -360,6 +368,33 @@ void enable_timer_nmi_watchdog(void) | |||
360 | } | 368 | } |
361 | } | 369 | } |
362 | 370 | ||
371 | static void __acpi_nmi_disable(void *__unused) | ||
372 | { | ||
373 | apic_write(APIC_LVT0, APIC_DM_NMI | APIC_LVT_MASKED); | ||
374 | } | ||
375 | |||
376 | /* | ||
377 | * Disable timer based NMIs on all CPUs: | ||
378 | */ | ||
379 | void acpi_nmi_disable(void) | ||
380 | { | ||
381 | if (atomic_read(&nmi_active) && nmi_watchdog == NMI_IO_APIC) | ||
382 | on_each_cpu(__acpi_nmi_disable, NULL, 0, 1); | ||
383 | } | ||
384 | |||
385 | static void __acpi_nmi_enable(void *__unused) | ||
386 | { | ||
387 | apic_write(APIC_LVT0, APIC_DM_NMI); | ||
388 | } | ||
389 | |||
390 | /* | ||
391 | * Enable timer based NMIs on all CPUs: | ||
392 | */ | ||
393 | void acpi_nmi_enable(void) | ||
394 | { | ||
395 | if (atomic_read(&nmi_active) && nmi_watchdog == NMI_IO_APIC) | ||
396 | on_each_cpu(__acpi_nmi_enable, NULL, 0, 1); | ||
397 | } | ||
363 | #ifdef CONFIG_PM | 398 | #ifdef CONFIG_PM |
364 | 399 | ||
365 | static int nmi_pm_active; /* nmi_active before suspend */ | 400 | static int nmi_pm_active; /* nmi_active before suspend */ |
@@ -634,7 +669,9 @@ static int setup_intel_arch_watchdog(void) | |||
634 | 669 | ||
635 | /* setup the timer */ | 670 | /* setup the timer */ |
636 | wrmsr(evntsel_msr, evntsel, 0); | 671 | wrmsr(evntsel_msr, evntsel, 0); |
637 | wrmsrl(perfctr_msr, -((u64)cpu_khz * 1000 / nmi_hz)); | 672 | |
673 | nmi_hz = adjust_for_32bit_ctr(nmi_hz); | ||
674 | wrmsr(perfctr_msr, (u32)(-((u64)cpu_khz * 1000 / nmi_hz)), 0); | ||
638 | 675 | ||
639 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 676 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
640 | evntsel |= ARCH_PERFMON_EVENTSEL0_ENABLE; | 677 | evntsel |= ARCH_PERFMON_EVENTSEL0_ENABLE; |
@@ -855,15 +892,23 @@ int __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason) | |||
855 | dummy &= ~P4_CCCR_OVF; | 892 | dummy &= ~P4_CCCR_OVF; |
856 | wrmsrl(wd->cccr_msr, dummy); | 893 | wrmsrl(wd->cccr_msr, dummy); |
857 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 894 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
895 | /* start the cycle over again */ | ||
896 | wrmsrl(wd->perfctr_msr, | ||
897 | -((u64)cpu_khz * 1000 / nmi_hz)); | ||
858 | } else if (wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0) { | 898 | } else if (wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0) { |
859 | /* | 899 | /* |
860 | * ArchPerfom/Core Duo needs to re-unmask | 900 | * ArchPerfom/Core Duo needs to re-unmask |
861 | * the apic vector | 901 | * the apic vector |
862 | */ | 902 | */ |
863 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 903 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
904 | /* ARCH_PERFMON has 32 bit counter writes */ | ||
905 | wrmsr(wd->perfctr_msr, | ||
906 | (u32)(-((u64)cpu_khz * 1000 / nmi_hz)), 0); | ||
907 | } else { | ||
908 | /* start the cycle over again */ | ||
909 | wrmsrl(wd->perfctr_msr, | ||
910 | -((u64)cpu_khz * 1000 / nmi_hz)); | ||
864 | } | 911 | } |
865 | /* start the cycle over again */ | ||
866 | wrmsrl(wd->perfctr_msr, -((u64)cpu_khz * 1000 / nmi_hz)); | ||
867 | rc = 1; | 912 | rc = 1; |
868 | } else if (nmi_watchdog == NMI_IO_APIC) { | 913 | } else if (nmi_watchdog == NMI_IO_APIC) { |
869 | /* don't know how to accurately check for this. | 914 | /* don't know how to accurately check for this. |