aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/kernel/nmi.c
diff options
context:
space:
mode:
authorDon Zickus <dzickus@redhat.com>2006-09-26 04:52:26 -0400
committerAndi Kleen <andi@basil.nowhere.org>2006-09-26 04:52:26 -0400
commit828f0afda123a96ff4e8078f057a302f4b4232ae (patch)
treea6f7398e0037f5c8f4cbd95ff11c5e4bf78a4c4d /arch/i386/kernel/nmi.c
parentb07f8915cda3fcd73b8b68075ba1e6cd0673365d (diff)
[PATCH] x86: Add performance counter reservation framework for UP kernels
Adds basic infrastructure to allow subsystems to reserve performance counters on the x86 chips. Only UP kernels are supported in this patch to make reviewing easier. The SMP portion makes a lot more changes. Think of this as a locking mechanism where each bit represents a different counter. In addition, each subsystem should also reserve an appropriate event selection register that will correspond to the performance counter it will be using (this is mainly neccessary for the Pentium 4 chips as they break the 1:1 relationship to performance counters). This will help prevent subsystems like oprofile from interfering with the nmi watchdog. Signed-off-by: Don Zickus <dzickus@redhat.com> Signed-off-by: Andi Kleen <ak@suse.de>
Diffstat (limited to 'arch/i386/kernel/nmi.c')
-rw-r--r--arch/i386/kernel/nmi.c188
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 */
34static unsigned int nmi_p4_cccr_val; 34static unsigned int nmi_p4_cccr_val;
35extern void show_registers(struct pt_regs *regs); 35extern 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 */
43static DEFINE_PER_CPU(unsigned long, perfctr_nmi_owner);
44static 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 */
113static 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 */
131static 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) */
149int 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 */
157int 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
167int 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
179void 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
189int 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
201void 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
347static 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
355static void write_watchdog_counter(const char *descr) 460static 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
365static void setup_k7_watchdog(void) 470static 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;
495fail1:
496 release_perfctr_nmi(nmi_perfctr_msr);
497fail:
498 return 0;
384} 499}
385 500
386static void setup_p6_watchdog(void) 501static 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;
524fail1:
525 release_perfctr_nmi(nmi_perfctr_msr);
526fail:
527 return 0;
405} 528}
406 529
407static int setup_p4_watchdog(void) 530static 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;
557fail1:
558 release_perfctr_nmi(nmi_perfctr_msr);
559fail:
560 return 0;
445} 561}
446 562
447void setup_apic_nmi_watchdog (void) 563void 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
613EXPORT_SYMBOL(nmi_active); 731EXPORT_SYMBOL(nmi_active);
614EXPORT_SYMBOL(nmi_watchdog); 732EXPORT_SYMBOL(nmi_watchdog);
733EXPORT_SYMBOL(avail_to_resrv_perfctr_nmi);
734EXPORT_SYMBOL(avail_to_resrv_perfctr_nmi_bit);
735EXPORT_SYMBOL(reserve_perfctr_nmi);
736EXPORT_SYMBOL(release_perfctr_nmi);
737EXPORT_SYMBOL(reserve_evntsel_nmi);
738EXPORT_SYMBOL(release_evntsel_nmi);
615EXPORT_SYMBOL(reserve_lapic_nmi); 739EXPORT_SYMBOL(reserve_lapic_nmi);
616EXPORT_SYMBOL(release_lapic_nmi); 740EXPORT_SYMBOL(release_lapic_nmi);
617EXPORT_SYMBOL(disable_timer_nmi_watchdog); 741EXPORT_SYMBOL(disable_timer_nmi_watchdog);