diff options
Diffstat (limited to 'arch/i386/kernel')
-rw-r--r-- | arch/i386/kernel/nmi.c | 188 |
1 files changed, 156 insertions, 32 deletions
diff --git a/arch/i386/kernel/nmi.c b/arch/i386/kernel/nmi.c index 1282d70ff971..5d58dfeacd59 100644 --- a/arch/i386/kernel/nmi.c +++ b/arch/i386/kernel/nmi.c | |||
@@ -34,6 +34,20 @@ static unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */ | |||
34 | static unsigned int nmi_p4_cccr_val; | 34 | static unsigned int nmi_p4_cccr_val; |
35 | extern void show_registers(struct pt_regs *regs); | 35 | extern void show_registers(struct pt_regs *regs); |
36 | 36 | ||
37 | /* perfctr_nmi_owner tracks the ownership of the perfctr registers: | ||
38 | * evtsel_nmi_owner tracks the ownership of the event selection | ||
39 | * - different performance counters/ event selection may be reserved for | ||
40 | * different subsystems this reservation system just tries to coordinate | ||
41 | * things a little | ||
42 | */ | ||
43 | static DEFINE_PER_CPU(unsigned long, perfctr_nmi_owner); | ||
44 | static DEFINE_PER_CPU(unsigned long, evntsel_nmi_owner[3]); | ||
45 | |||
46 | /* this number is calculated from Intel's MSR_P4_CRU_ESCR5 register and it's | ||
47 | * offset from MSR_P4_BSU_ESCR0. It will be the max for all platforms (for now) | ||
48 | */ | ||
49 | #define NMI_MAX_COUNTER_BITS 66 | ||
50 | |||
37 | /* | 51 | /* |
38 | * lapic_nmi_owner tracks the ownership of the lapic NMI hardware: | 52 | * lapic_nmi_owner tracks the ownership of the lapic NMI hardware: |
39 | * - it may be reserved by some other driver, or not | 53 | * - it may be reserved by some other driver, or not |
@@ -95,6 +109,105 @@ int nmi_active; | |||
95 | (P4_CCCR_OVF_PMI0|P4_CCCR_THRESHOLD(15)|P4_CCCR_COMPLEMENT| \ | 109 | (P4_CCCR_OVF_PMI0|P4_CCCR_THRESHOLD(15)|P4_CCCR_COMPLEMENT| \ |
96 | P4_CCCR_COMPARE|P4_CCCR_REQUIRED|P4_CCCR_ESCR_SELECT(4)|P4_CCCR_ENABLE) | 110 | P4_CCCR_COMPARE|P4_CCCR_REQUIRED|P4_CCCR_ESCR_SELECT(4)|P4_CCCR_ENABLE) |
97 | 111 | ||
112 | /* converts an msr to an appropriate reservation bit */ | ||
113 | static inline unsigned int nmi_perfctr_msr_to_bit(unsigned int msr) | ||
114 | { | ||
115 | /* returns the bit offset of the performance counter register */ | ||
116 | switch (boot_cpu_data.x86_vendor) { | ||
117 | case X86_VENDOR_AMD: | ||
118 | return (msr - MSR_K7_PERFCTR0); | ||
119 | case X86_VENDOR_INTEL: | ||
120 | switch (boot_cpu_data.x86) { | ||
121 | case 6: | ||
122 | return (msr - MSR_P6_PERFCTR0); | ||
123 | case 15: | ||
124 | return (msr - MSR_P4_BPU_PERFCTR0); | ||
125 | } | ||
126 | } | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | /* converts an msr to an appropriate reservation bit */ | ||
131 | static inline unsigned int nmi_evntsel_msr_to_bit(unsigned int msr) | ||
132 | { | ||
133 | /* returns the bit offset of the event selection register */ | ||
134 | switch (boot_cpu_data.x86_vendor) { | ||
135 | case X86_VENDOR_AMD: | ||
136 | return (msr - MSR_K7_EVNTSEL0); | ||
137 | case X86_VENDOR_INTEL: | ||
138 | switch (boot_cpu_data.x86) { | ||
139 | case 6: | ||
140 | return (msr - MSR_P6_EVNTSEL0); | ||
141 | case 15: | ||
142 | return (msr - MSR_P4_BSU_ESCR0); | ||
143 | } | ||
144 | } | ||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | /* checks for a bit availability (hack for oprofile) */ | ||
149 | int avail_to_resrv_perfctr_nmi_bit(unsigned int counter) | ||
150 | { | ||
151 | BUG_ON(counter > NMI_MAX_COUNTER_BITS); | ||
152 | |||
153 | return (!test_bit(counter, &__get_cpu_var(perfctr_nmi_owner))); | ||
154 | } | ||
155 | |||
156 | /* checks the an msr for availability */ | ||
157 | int avail_to_resrv_perfctr_nmi(unsigned int msr) | ||
158 | { | ||
159 | unsigned int counter; | ||
160 | |||
161 | counter = nmi_perfctr_msr_to_bit(msr); | ||
162 | BUG_ON(counter > NMI_MAX_COUNTER_BITS); | ||
163 | |||
164 | return (!test_bit(counter, &__get_cpu_var(perfctr_nmi_owner))); | ||
165 | } | ||
166 | |||
167 | int reserve_perfctr_nmi(unsigned int msr) | ||
168 | { | ||
169 | unsigned int counter; | ||
170 | |||
171 | counter = nmi_perfctr_msr_to_bit(msr); | ||
172 | BUG_ON(counter > NMI_MAX_COUNTER_BITS); | ||
173 | |||
174 | if (!test_and_set_bit(counter, &__get_cpu_var(perfctr_nmi_owner))) | ||
175 | return 1; | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | void release_perfctr_nmi(unsigned int msr) | ||
180 | { | ||
181 | unsigned int counter; | ||
182 | |||
183 | counter = nmi_perfctr_msr_to_bit(msr); | ||
184 | BUG_ON(counter > NMI_MAX_COUNTER_BITS); | ||
185 | |||
186 | clear_bit(counter, &__get_cpu_var(perfctr_nmi_owner)); | ||
187 | } | ||
188 | |||
189 | int reserve_evntsel_nmi(unsigned int msr) | ||
190 | { | ||
191 | unsigned int counter; | ||
192 | |||
193 | counter = nmi_evntsel_msr_to_bit(msr); | ||
194 | BUG_ON(counter > NMI_MAX_COUNTER_BITS); | ||
195 | |||
196 | if (!test_and_set_bit(counter, &__get_cpu_var(evntsel_nmi_owner)[0])) | ||
197 | return 1; | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | void release_evntsel_nmi(unsigned int msr) | ||
202 | { | ||
203 | unsigned int counter; | ||
204 | |||
205 | counter = nmi_evntsel_msr_to_bit(msr); | ||
206 | BUG_ON(counter > NMI_MAX_COUNTER_BITS); | ||
207 | |||
208 | clear_bit(counter, &__get_cpu_var(evntsel_nmi_owner)[0]); | ||
209 | } | ||
210 | |||
98 | #ifdef CONFIG_SMP | 211 | #ifdef CONFIG_SMP |
99 | /* The performance counters used by NMI_LOCAL_APIC don't trigger when | 212 | /* The performance counters used by NMI_LOCAL_APIC don't trigger when |
100 | * the CPU is idle. To make sure the NMI watchdog really ticks on all | 213 | * the CPU is idle. To make sure the NMI watchdog really ticks on all |
@@ -344,14 +457,6 @@ late_initcall(init_lapic_nmi_sysfs); | |||
344 | * Original code written by Keith Owens. | 457 | * Original code written by Keith Owens. |
345 | */ | 458 | */ |
346 | 459 | ||
347 | static void clear_msr_range(unsigned int base, unsigned int n) | ||
348 | { | ||
349 | unsigned int i; | ||
350 | |||
351 | for(i = 0; i < n; ++i) | ||
352 | wrmsr(base+i, 0, 0); | ||
353 | } | ||
354 | |||
355 | static void write_watchdog_counter(const char *descr) | 460 | static void write_watchdog_counter(const char *descr) |
356 | { | 461 | { |
357 | u64 count = (u64)cpu_khz * 1000; | 462 | u64 count = (u64)cpu_khz * 1000; |
@@ -362,14 +467,19 @@ static void write_watchdog_counter(const char *descr) | |||
362 | wrmsrl(nmi_perfctr_msr, 0 - count); | 467 | wrmsrl(nmi_perfctr_msr, 0 - count); |
363 | } | 468 | } |
364 | 469 | ||
365 | static void setup_k7_watchdog(void) | 470 | static int setup_k7_watchdog(void) |
366 | { | 471 | { |
367 | unsigned int evntsel; | 472 | unsigned int evntsel; |
368 | 473 | ||
369 | nmi_perfctr_msr = MSR_K7_PERFCTR0; | 474 | nmi_perfctr_msr = MSR_K7_PERFCTR0; |
370 | 475 | ||
371 | clear_msr_range(MSR_K7_EVNTSEL0, 4); | 476 | if (!reserve_perfctr_nmi(nmi_perfctr_msr)) |
372 | clear_msr_range(MSR_K7_PERFCTR0, 4); | 477 | goto fail; |
478 | |||
479 | if (!reserve_evntsel_nmi(MSR_K7_EVNTSEL0)) | ||
480 | goto fail1; | ||
481 | |||
482 | wrmsrl(MSR_K7_PERFCTR0, 0UL); | ||
373 | 483 | ||
374 | evntsel = K7_EVNTSEL_INT | 484 | evntsel = K7_EVNTSEL_INT |
375 | | K7_EVNTSEL_OS | 485 | | K7_EVNTSEL_OS |
@@ -381,16 +491,24 @@ static void setup_k7_watchdog(void) | |||
381 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 491 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
382 | evntsel |= K7_EVNTSEL_ENABLE; | 492 | evntsel |= K7_EVNTSEL_ENABLE; |
383 | wrmsr(MSR_K7_EVNTSEL0, evntsel, 0); | 493 | wrmsr(MSR_K7_EVNTSEL0, evntsel, 0); |
494 | return 1; | ||
495 | fail1: | ||
496 | release_perfctr_nmi(nmi_perfctr_msr); | ||
497 | fail: | ||
498 | return 0; | ||
384 | } | 499 | } |
385 | 500 | ||
386 | static void setup_p6_watchdog(void) | 501 | static int setup_p6_watchdog(void) |
387 | { | 502 | { |
388 | unsigned int evntsel; | 503 | unsigned int evntsel; |
389 | 504 | ||
390 | nmi_perfctr_msr = MSR_P6_PERFCTR0; | 505 | nmi_perfctr_msr = MSR_P6_PERFCTR0; |
391 | 506 | ||
392 | clear_msr_range(MSR_P6_EVNTSEL0, 2); | 507 | if (!reserve_perfctr_nmi(nmi_perfctr_msr)) |
393 | clear_msr_range(MSR_P6_PERFCTR0, 2); | 508 | goto fail; |
509 | |||
510 | if (!reserve_evntsel_nmi(MSR_P6_EVNTSEL0)) | ||
511 | goto fail1; | ||
394 | 512 | ||
395 | evntsel = P6_EVNTSEL_INT | 513 | evntsel = P6_EVNTSEL_INT |
396 | | P6_EVNTSEL_OS | 514 | | P6_EVNTSEL_OS |
@@ -402,6 +520,11 @@ static void setup_p6_watchdog(void) | |||
402 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 520 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
403 | evntsel |= P6_EVNTSEL0_ENABLE; | 521 | evntsel |= P6_EVNTSEL0_ENABLE; |
404 | wrmsr(MSR_P6_EVNTSEL0, evntsel, 0); | 522 | wrmsr(MSR_P6_EVNTSEL0, evntsel, 0); |
523 | return 1; | ||
524 | fail1: | ||
525 | release_perfctr_nmi(nmi_perfctr_msr); | ||
526 | fail: | ||
527 | return 0; | ||
405 | } | 528 | } |
406 | 529 | ||
407 | static int setup_p4_watchdog(void) | 530 | static int setup_p4_watchdog(void) |
@@ -419,22 +542,11 @@ static int setup_p4_watchdog(void) | |||
419 | nmi_p4_cccr_val |= P4_CCCR_OVF_PMI1; | 542 | nmi_p4_cccr_val |= P4_CCCR_OVF_PMI1; |
420 | #endif | 543 | #endif |
421 | 544 | ||
422 | if (!(misc_enable & MSR_P4_MISC_ENABLE_PEBS_UNAVAIL)) | 545 | if (!reserve_perfctr_nmi(nmi_perfctr_msr)) |
423 | clear_msr_range(0x3F1, 2); | 546 | goto fail; |
424 | /* MSR 0x3F0 seems to have a default value of 0xFC00, but current | 547 | |
425 | docs doesn't fully define it, so leave it alone for now. */ | 548 | if (!reserve_evntsel_nmi(MSR_P4_CRU_ESCR0)) |
426 | if (boot_cpu_data.x86_model >= 0x3) { | 549 | goto fail1; |
427 | /* MSR_P4_IQ_ESCR0/1 (0x3ba/0x3bb) removed */ | ||
428 | clear_msr_range(0x3A0, 26); | ||
429 | clear_msr_range(0x3BC, 3); | ||
430 | } else { | ||
431 | clear_msr_range(0x3A0, 31); | ||
432 | } | ||
433 | clear_msr_range(0x3C0, 6); | ||
434 | clear_msr_range(0x3C8, 6); | ||
435 | clear_msr_range(0x3E0, 2); | ||
436 | clear_msr_range(MSR_P4_CCCR0, 18); | ||
437 | clear_msr_range(MSR_P4_PERFCTR0, 18); | ||
438 | 550 | ||
439 | wrmsr(MSR_P4_CRU_ESCR0, P4_NMI_CRU_ESCR0, 0); | 551 | wrmsr(MSR_P4_CRU_ESCR0, P4_NMI_CRU_ESCR0, 0); |
440 | wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0 & ~P4_CCCR_ENABLE, 0); | 552 | wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0 & ~P4_CCCR_ENABLE, 0); |
@@ -442,6 +554,10 @@ static int setup_p4_watchdog(void) | |||
442 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 554 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
443 | wrmsr(MSR_P4_IQ_CCCR0, nmi_p4_cccr_val, 0); | 555 | wrmsr(MSR_P4_IQ_CCCR0, nmi_p4_cccr_val, 0); |
444 | return 1; | 556 | return 1; |
557 | fail1: | ||
558 | release_perfctr_nmi(nmi_perfctr_msr); | ||
559 | fail: | ||
560 | return 0; | ||
445 | } | 561 | } |
446 | 562 | ||
447 | void setup_apic_nmi_watchdog (void) | 563 | void setup_apic_nmi_watchdog (void) |
@@ -450,7 +566,8 @@ void setup_apic_nmi_watchdog (void) | |||
450 | case X86_VENDOR_AMD: | 566 | case X86_VENDOR_AMD: |
451 | if (boot_cpu_data.x86 != 6 && boot_cpu_data.x86 != 15) | 567 | if (boot_cpu_data.x86 != 6 && boot_cpu_data.x86 != 15) |
452 | return; | 568 | return; |
453 | setup_k7_watchdog(); | 569 | if (!setup_k7_watchdog()) |
570 | return; | ||
454 | break; | 571 | break; |
455 | case X86_VENDOR_INTEL: | 572 | case X86_VENDOR_INTEL: |
456 | switch (boot_cpu_data.x86) { | 573 | switch (boot_cpu_data.x86) { |
@@ -458,7 +575,8 @@ void setup_apic_nmi_watchdog (void) | |||
458 | if (boot_cpu_data.x86_model > 0xd) | 575 | if (boot_cpu_data.x86_model > 0xd) |
459 | return; | 576 | return; |
460 | 577 | ||
461 | setup_p6_watchdog(); | 578 | if(!setup_p6_watchdog()) |
579 | return; | ||
462 | break; | 580 | break; |
463 | case 15: | 581 | case 15: |
464 | if (boot_cpu_data.x86_model > 0x4) | 582 | if (boot_cpu_data.x86_model > 0x4) |
@@ -612,6 +730,12 @@ int proc_unknown_nmi_panic(ctl_table *table, int write, struct file *file, | |||
612 | 730 | ||
613 | EXPORT_SYMBOL(nmi_active); | 731 | EXPORT_SYMBOL(nmi_active); |
614 | EXPORT_SYMBOL(nmi_watchdog); | 732 | EXPORT_SYMBOL(nmi_watchdog); |
733 | EXPORT_SYMBOL(avail_to_resrv_perfctr_nmi); | ||
734 | EXPORT_SYMBOL(avail_to_resrv_perfctr_nmi_bit); | ||
735 | EXPORT_SYMBOL(reserve_perfctr_nmi); | ||
736 | EXPORT_SYMBOL(release_perfctr_nmi); | ||
737 | EXPORT_SYMBOL(reserve_evntsel_nmi); | ||
738 | EXPORT_SYMBOL(release_evntsel_nmi); | ||
615 | EXPORT_SYMBOL(reserve_lapic_nmi); | 739 | EXPORT_SYMBOL(reserve_lapic_nmi); |
616 | EXPORT_SYMBOL(release_lapic_nmi); | 740 | EXPORT_SYMBOL(release_lapic_nmi); |
617 | EXPORT_SYMBOL(disable_timer_nmi_watchdog); | 741 | EXPORT_SYMBOL(disable_timer_nmi_watchdog); |