diff options
Diffstat (limited to 'arch/x86/kernel/cpu/perfctr-watchdog.c')
-rw-r--r-- | arch/x86/kernel/cpu/perfctr-watchdog.c | 109 |
1 files changed, 90 insertions, 19 deletions
diff --git a/arch/x86/kernel/cpu/perfctr-watchdog.c b/arch/x86/kernel/cpu/perfctr-watchdog.c index 6d4bdc02388a..9abd48b22674 100644 --- a/arch/x86/kernel/cpu/perfctr-watchdog.c +++ b/arch/x86/kernel/cpu/perfctr-watchdog.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <linux/bitops.h> | 17 | #include <linux/bitops.h> |
18 | #include <linux/smp.h> | 18 | #include <linux/smp.h> |
19 | #include <linux/nmi.h> | 19 | #include <linux/nmi.h> |
20 | #include <linux/kprobes.h> | ||
21 | |||
20 | #include <asm/apic.h> | 22 | #include <asm/apic.h> |
21 | #include <asm/intel_arch_perfmon.h> | 23 | #include <asm/intel_arch_perfmon.h> |
22 | 24 | ||
@@ -250,7 +252,7 @@ static void write_watchdog_counter(unsigned int perfctr_msr, | |||
250 | 252 | ||
251 | do_div(count, nmi_hz); | 253 | do_div(count, nmi_hz); |
252 | if(descr) | 254 | if(descr) |
253 | Dprintk("setting %s to -0x%08Lx\n", descr, count); | 255 | pr_debug("setting %s to -0x%08Lx\n", descr, count); |
254 | wrmsrl(perfctr_msr, 0 - count); | 256 | wrmsrl(perfctr_msr, 0 - count); |
255 | } | 257 | } |
256 | 258 | ||
@@ -261,7 +263,7 @@ static void write_watchdog_counter32(unsigned int perfctr_msr, | |||
261 | 263 | ||
262 | do_div(count, nmi_hz); | 264 | do_div(count, nmi_hz); |
263 | if(descr) | 265 | if(descr) |
264 | Dprintk("setting %s to -0x%08Lx\n", descr, count); | 266 | pr_debug("setting %s to -0x%08Lx\n", descr, count); |
265 | wrmsr(perfctr_msr, (u32)(-count), 0); | 267 | wrmsr(perfctr_msr, (u32)(-count), 0); |
266 | } | 268 | } |
267 | 269 | ||
@@ -295,13 +297,19 @@ static int setup_k7_watchdog(unsigned nmi_hz) | |||
295 | /* setup the timer */ | 297 | /* setup the timer */ |
296 | wrmsr(evntsel_msr, evntsel, 0); | 298 | wrmsr(evntsel_msr, evntsel, 0); |
297 | write_watchdog_counter(perfctr_msr, "K7_PERFCTR0",nmi_hz); | 299 | write_watchdog_counter(perfctr_msr, "K7_PERFCTR0",nmi_hz); |
298 | apic_write(APIC_LVTPC, APIC_DM_NMI); | ||
299 | evntsel |= K7_EVNTSEL_ENABLE; | ||
300 | wrmsr(evntsel_msr, evntsel, 0); | ||
301 | 300 | ||
301 | /* initialize the wd struct before enabling */ | ||
302 | wd->perfctr_msr = perfctr_msr; | 302 | wd->perfctr_msr = perfctr_msr; |
303 | wd->evntsel_msr = evntsel_msr; | 303 | wd->evntsel_msr = evntsel_msr; |
304 | wd->cccr_msr = 0; /* unused */ | 304 | wd->cccr_msr = 0; /* unused */ |
305 | |||
306 | /* ok, everything is initialized, announce that we're set */ | ||
307 | cpu_nmi_set_wd_enabled(); | ||
308 | |||
309 | apic_write(APIC_LVTPC, APIC_DM_NMI); | ||
310 | evntsel |= K7_EVNTSEL_ENABLE; | ||
311 | wrmsr(evntsel_msr, evntsel, 0); | ||
312 | |||
305 | return 1; | 313 | return 1; |
306 | } | 314 | } |
307 | 315 | ||
@@ -330,7 +338,8 @@ static void single_msr_unreserve(void) | |||
330 | release_perfctr_nmi(wd_ops->perfctr); | 338 | release_perfctr_nmi(wd_ops->perfctr); |
331 | } | 339 | } |
332 | 340 | ||
333 | static void single_msr_rearm(struct nmi_watchdog_ctlblk *wd, unsigned nmi_hz) | 341 | static void __kprobes |
342 | single_msr_rearm(struct nmi_watchdog_ctlblk *wd, unsigned nmi_hz) | ||
334 | { | 343 | { |
335 | /* start the cycle over again */ | 344 | /* start the cycle over again */ |
336 | write_watchdog_counter(wd->perfctr_msr, NULL, nmi_hz); | 345 | write_watchdog_counter(wd->perfctr_msr, NULL, nmi_hz); |
@@ -379,17 +388,23 @@ static int setup_p6_watchdog(unsigned nmi_hz) | |||
379 | wrmsr(evntsel_msr, evntsel, 0); | 388 | wrmsr(evntsel_msr, evntsel, 0); |
380 | nmi_hz = adjust_for_32bit_ctr(nmi_hz); | 389 | nmi_hz = adjust_for_32bit_ctr(nmi_hz); |
381 | write_watchdog_counter32(perfctr_msr, "P6_PERFCTR0",nmi_hz); | 390 | write_watchdog_counter32(perfctr_msr, "P6_PERFCTR0",nmi_hz); |
382 | apic_write(APIC_LVTPC, APIC_DM_NMI); | ||
383 | evntsel |= P6_EVNTSEL0_ENABLE; | ||
384 | wrmsr(evntsel_msr, evntsel, 0); | ||
385 | 391 | ||
392 | /* initialize the wd struct before enabling */ | ||
386 | wd->perfctr_msr = perfctr_msr; | 393 | wd->perfctr_msr = perfctr_msr; |
387 | wd->evntsel_msr = evntsel_msr; | 394 | wd->evntsel_msr = evntsel_msr; |
388 | wd->cccr_msr = 0; /* unused */ | 395 | wd->cccr_msr = 0; /* unused */ |
396 | |||
397 | /* ok, everything is initialized, announce that we're set */ | ||
398 | cpu_nmi_set_wd_enabled(); | ||
399 | |||
400 | apic_write(APIC_LVTPC, APIC_DM_NMI); | ||
401 | evntsel |= P6_EVNTSEL0_ENABLE; | ||
402 | wrmsr(evntsel_msr, evntsel, 0); | ||
403 | |||
389 | return 1; | 404 | return 1; |
390 | } | 405 | } |
391 | 406 | ||
392 | static void p6_rearm(struct nmi_watchdog_ctlblk *wd, unsigned nmi_hz) | 407 | static void __kprobes p6_rearm(struct nmi_watchdog_ctlblk *wd, unsigned nmi_hz) |
393 | { | 408 | { |
394 | /* | 409 | /* |
395 | * P6 based Pentium M need to re-unmask | 410 | * P6 based Pentium M need to re-unmask |
@@ -432,6 +447,27 @@ static const struct wd_ops p6_wd_ops = { | |||
432 | #define P4_CCCR_ENABLE (1 << 12) | 447 | #define P4_CCCR_ENABLE (1 << 12) |
433 | #define P4_CCCR_OVF (1 << 31) | 448 | #define P4_CCCR_OVF (1 << 31) |
434 | 449 | ||
450 | #define P4_CONTROLS 18 | ||
451 | static unsigned int p4_controls[18] = { | ||
452 | MSR_P4_BPU_CCCR0, | ||
453 | MSR_P4_BPU_CCCR1, | ||
454 | MSR_P4_BPU_CCCR2, | ||
455 | MSR_P4_BPU_CCCR3, | ||
456 | MSR_P4_MS_CCCR0, | ||
457 | MSR_P4_MS_CCCR1, | ||
458 | MSR_P4_MS_CCCR2, | ||
459 | MSR_P4_MS_CCCR3, | ||
460 | MSR_P4_FLAME_CCCR0, | ||
461 | MSR_P4_FLAME_CCCR1, | ||
462 | MSR_P4_FLAME_CCCR2, | ||
463 | MSR_P4_FLAME_CCCR3, | ||
464 | MSR_P4_IQ_CCCR0, | ||
465 | MSR_P4_IQ_CCCR1, | ||
466 | MSR_P4_IQ_CCCR2, | ||
467 | MSR_P4_IQ_CCCR3, | ||
468 | MSR_P4_IQ_CCCR4, | ||
469 | MSR_P4_IQ_CCCR5, | ||
470 | }; | ||
435 | /* | 471 | /* |
436 | * Set up IQ_COUNTER0 to behave like a clock, by having IQ_CCCR0 filter | 472 | * Set up IQ_COUNTER0 to behave like a clock, by having IQ_CCCR0 filter |
437 | * CRU_ESCR0 (with any non-null event selector) through a complemented | 473 | * CRU_ESCR0 (with any non-null event selector) through a complemented |
@@ -473,12 +509,38 @@ static int setup_p4_watchdog(unsigned nmi_hz) | |||
473 | evntsel_msr = MSR_P4_CRU_ESCR0; | 509 | evntsel_msr = MSR_P4_CRU_ESCR0; |
474 | cccr_msr = MSR_P4_IQ_CCCR0; | 510 | cccr_msr = MSR_P4_IQ_CCCR0; |
475 | cccr_val = P4_CCCR_OVF_PMI0 | P4_CCCR_ESCR_SELECT(4); | 511 | cccr_val = P4_CCCR_OVF_PMI0 | P4_CCCR_ESCR_SELECT(4); |
512 | |||
513 | /* | ||
514 | * If we're on the kdump kernel or other situation, we may | ||
515 | * still have other performance counter registers set to | ||
516 | * interrupt and they'll keep interrupting forever because | ||
517 | * of the P4_CCCR_OVF quirk. So we need to ACK all the | ||
518 | * pending interrupts and disable all the registers here, | ||
519 | * before reenabling the NMI delivery. Refer to p4_rearm() | ||
520 | * about the P4_CCCR_OVF quirk. | ||
521 | */ | ||
522 | if (reset_devices) { | ||
523 | unsigned int low, high; | ||
524 | int i; | ||
525 | |||
526 | for (i = 0; i < P4_CONTROLS; i++) { | ||
527 | rdmsr(p4_controls[i], low, high); | ||
528 | low &= ~(P4_CCCR_ENABLE | P4_CCCR_OVF); | ||
529 | wrmsr(p4_controls[i], low, high); | ||
530 | } | ||
531 | } | ||
476 | } else { | 532 | } else { |
477 | /* logical cpu 1 */ | 533 | /* logical cpu 1 */ |
478 | perfctr_msr = MSR_P4_IQ_PERFCTR1; | 534 | perfctr_msr = MSR_P4_IQ_PERFCTR1; |
479 | evntsel_msr = MSR_P4_CRU_ESCR0; | 535 | evntsel_msr = MSR_P4_CRU_ESCR0; |
480 | cccr_msr = MSR_P4_IQ_CCCR1; | 536 | cccr_msr = MSR_P4_IQ_CCCR1; |
481 | cccr_val = P4_CCCR_OVF_PMI1 | P4_CCCR_ESCR_SELECT(4); | 537 | |
538 | /* Pentium 4 D processors don't support P4_CCCR_OVF_PMI1 */ | ||
539 | if (boot_cpu_data.x86_model == 4 && boot_cpu_data.x86_mask == 4) | ||
540 | cccr_val = P4_CCCR_OVF_PMI0; | ||
541 | else | ||
542 | cccr_val = P4_CCCR_OVF_PMI1; | ||
543 | cccr_val |= P4_CCCR_ESCR_SELECT(4); | ||
482 | } | 544 | } |
483 | 545 | ||
484 | evntsel = P4_ESCR_EVENT_SELECT(0x3F) | 546 | evntsel = P4_ESCR_EVENT_SELECT(0x3F) |
@@ -493,12 +555,17 @@ static int setup_p4_watchdog(unsigned nmi_hz) | |||
493 | wrmsr(evntsel_msr, evntsel, 0); | 555 | wrmsr(evntsel_msr, evntsel, 0); |
494 | wrmsr(cccr_msr, cccr_val, 0); | 556 | wrmsr(cccr_msr, cccr_val, 0); |
495 | write_watchdog_counter(perfctr_msr, "P4_IQ_COUNTER0", nmi_hz); | 557 | write_watchdog_counter(perfctr_msr, "P4_IQ_COUNTER0", nmi_hz); |
496 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 558 | |
497 | cccr_val |= P4_CCCR_ENABLE; | ||
498 | wrmsr(cccr_msr, cccr_val, 0); | ||
499 | wd->perfctr_msr = perfctr_msr; | 559 | wd->perfctr_msr = perfctr_msr; |
500 | wd->evntsel_msr = evntsel_msr; | 560 | wd->evntsel_msr = evntsel_msr; |
501 | wd->cccr_msr = cccr_msr; | 561 | wd->cccr_msr = cccr_msr; |
562 | |||
563 | /* ok, everything is initialized, announce that we're set */ | ||
564 | cpu_nmi_set_wd_enabled(); | ||
565 | |||
566 | apic_write(APIC_LVTPC, APIC_DM_NMI); | ||
567 | cccr_val |= P4_CCCR_ENABLE; | ||
568 | wrmsr(cccr_msr, cccr_val, 0); | ||
502 | return 1; | 569 | return 1; |
503 | } | 570 | } |
504 | 571 | ||
@@ -541,7 +608,7 @@ static void p4_unreserve(void) | |||
541 | release_perfctr_nmi(MSR_P4_IQ_PERFCTR0); | 608 | release_perfctr_nmi(MSR_P4_IQ_PERFCTR0); |
542 | } | 609 | } |
543 | 610 | ||
544 | static void p4_rearm(struct nmi_watchdog_ctlblk *wd, unsigned nmi_hz) | 611 | static void __kprobes p4_rearm(struct nmi_watchdog_ctlblk *wd, unsigned nmi_hz) |
545 | { | 612 | { |
546 | unsigned dummy; | 613 | unsigned dummy; |
547 | /* | 614 | /* |
@@ -614,13 +681,17 @@ static int setup_intel_arch_watchdog(unsigned nmi_hz) | |||
614 | wrmsr(evntsel_msr, evntsel, 0); | 681 | wrmsr(evntsel_msr, evntsel, 0); |
615 | nmi_hz = adjust_for_32bit_ctr(nmi_hz); | 682 | nmi_hz = adjust_for_32bit_ctr(nmi_hz); |
616 | write_watchdog_counter32(perfctr_msr, "INTEL_ARCH_PERFCTR0", nmi_hz); | 683 | write_watchdog_counter32(perfctr_msr, "INTEL_ARCH_PERFCTR0", nmi_hz); |
617 | apic_write(APIC_LVTPC, APIC_DM_NMI); | ||
618 | evntsel |= ARCH_PERFMON_EVENTSEL0_ENABLE; | ||
619 | wrmsr(evntsel_msr, evntsel, 0); | ||
620 | 684 | ||
621 | wd->perfctr_msr = perfctr_msr; | 685 | wd->perfctr_msr = perfctr_msr; |
622 | wd->evntsel_msr = evntsel_msr; | 686 | wd->evntsel_msr = evntsel_msr; |
623 | wd->cccr_msr = 0; /* unused */ | 687 | wd->cccr_msr = 0; /* unused */ |
688 | |||
689 | /* ok, everything is initialized, announce that we're set */ | ||
690 | cpu_nmi_set_wd_enabled(); | ||
691 | |||
692 | apic_write(APIC_LVTPC, APIC_DM_NMI); | ||
693 | evntsel |= ARCH_PERFMON_EVENTSEL0_ENABLE; | ||
694 | wrmsr(evntsel_msr, evntsel, 0); | ||
624 | intel_arch_wd_ops.checkbit = 1ULL << (eax.split.bit_width - 1); | 695 | intel_arch_wd_ops.checkbit = 1ULL << (eax.split.bit_width - 1); |
625 | return 1; | 696 | return 1; |
626 | } | 697 | } |
@@ -716,7 +787,7 @@ unsigned lapic_adjust_nmi_hz(unsigned hz) | |||
716 | return hz; | 787 | return hz; |
717 | } | 788 | } |
718 | 789 | ||
719 | int lapic_wd_event(unsigned nmi_hz) | 790 | int __kprobes lapic_wd_event(unsigned nmi_hz) |
720 | { | 791 | { |
721 | struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); | 792 | struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); |
722 | u64 ctr; | 793 | u64 ctr; |