diff options
Diffstat (limited to 'arch/x86/kernel/cpu/perfctr-watchdog.c')
| -rw-r--r-- | arch/x86/kernel/cpu/perfctr-watchdog.c | 86 |
1 files changed, 74 insertions, 12 deletions
diff --git a/arch/x86/kernel/cpu/perfctr-watchdog.c b/arch/x86/kernel/cpu/perfctr-watchdog.c index 05cc22dbd4ff..6bff382094f5 100644 --- a/arch/x86/kernel/cpu/perfctr-watchdog.c +++ b/arch/x86/kernel/cpu/perfctr-watchdog.c | |||
| @@ -295,13 +295,19 @@ static int setup_k7_watchdog(unsigned nmi_hz) | |||
| 295 | /* setup the timer */ | 295 | /* setup the timer */ |
| 296 | wrmsr(evntsel_msr, evntsel, 0); | 296 | wrmsr(evntsel_msr, evntsel, 0); |
| 297 | write_watchdog_counter(perfctr_msr, "K7_PERFCTR0",nmi_hz); | 297 | 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 | 298 | ||
| 299 | /* initialize the wd struct before enabling */ | ||
| 302 | wd->perfctr_msr = perfctr_msr; | 300 | wd->perfctr_msr = perfctr_msr; |
| 303 | wd->evntsel_msr = evntsel_msr; | 301 | wd->evntsel_msr = evntsel_msr; |
| 304 | wd->cccr_msr = 0; /* unused */ | 302 | wd->cccr_msr = 0; /* unused */ |
| 303 | |||
| 304 | /* ok, everything is initialized, announce that we're set */ | ||
| 305 | cpu_nmi_set_wd_enabled(); | ||
| 306 | |||
| 307 | apic_write(APIC_LVTPC, APIC_DM_NMI); | ||
| 308 | evntsel |= K7_EVNTSEL_ENABLE; | ||
| 309 | wrmsr(evntsel_msr, evntsel, 0); | ||
| 310 | |||
| 305 | return 1; | 311 | return 1; |
| 306 | } | 312 | } |
| 307 | 313 | ||
| @@ -379,13 +385,19 @@ static int setup_p6_watchdog(unsigned nmi_hz) | |||
| 379 | wrmsr(evntsel_msr, evntsel, 0); | 385 | wrmsr(evntsel_msr, evntsel, 0); |
| 380 | nmi_hz = adjust_for_32bit_ctr(nmi_hz); | 386 | nmi_hz = adjust_for_32bit_ctr(nmi_hz); |
| 381 | write_watchdog_counter32(perfctr_msr, "P6_PERFCTR0",nmi_hz); | 387 | 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 | 388 | ||
| 389 | /* initialize the wd struct before enabling */ | ||
| 386 | wd->perfctr_msr = perfctr_msr; | 390 | wd->perfctr_msr = perfctr_msr; |
| 387 | wd->evntsel_msr = evntsel_msr; | 391 | wd->evntsel_msr = evntsel_msr; |
| 388 | wd->cccr_msr = 0; /* unused */ | 392 | wd->cccr_msr = 0; /* unused */ |
| 393 | |||
| 394 | /* ok, everything is initialized, announce that we're set */ | ||
| 395 | cpu_nmi_set_wd_enabled(); | ||
| 396 | |||
| 397 | apic_write(APIC_LVTPC, APIC_DM_NMI); | ||
| 398 | evntsel |= P6_EVNTSEL0_ENABLE; | ||
| 399 | wrmsr(evntsel_msr, evntsel, 0); | ||
| 400 | |||
| 389 | return 1; | 401 | return 1; |
| 390 | } | 402 | } |
| 391 | 403 | ||
| @@ -432,6 +444,27 @@ static const struct wd_ops p6_wd_ops = { | |||
| 432 | #define P4_CCCR_ENABLE (1 << 12) | 444 | #define P4_CCCR_ENABLE (1 << 12) |
| 433 | #define P4_CCCR_OVF (1 << 31) | 445 | #define P4_CCCR_OVF (1 << 31) |
| 434 | 446 | ||
| 447 | #define P4_CONTROLS 18 | ||
| 448 | static unsigned int p4_controls[18] = { | ||
| 449 | MSR_P4_BPU_CCCR0, | ||
| 450 | MSR_P4_BPU_CCCR1, | ||
| 451 | MSR_P4_BPU_CCCR2, | ||
| 452 | MSR_P4_BPU_CCCR3, | ||
| 453 | MSR_P4_MS_CCCR0, | ||
| 454 | MSR_P4_MS_CCCR1, | ||
| 455 | MSR_P4_MS_CCCR2, | ||
| 456 | MSR_P4_MS_CCCR3, | ||
| 457 | MSR_P4_FLAME_CCCR0, | ||
| 458 | MSR_P4_FLAME_CCCR1, | ||
| 459 | MSR_P4_FLAME_CCCR2, | ||
| 460 | MSR_P4_FLAME_CCCR3, | ||
| 461 | MSR_P4_IQ_CCCR0, | ||
| 462 | MSR_P4_IQ_CCCR1, | ||
| 463 | MSR_P4_IQ_CCCR2, | ||
| 464 | MSR_P4_IQ_CCCR3, | ||
| 465 | MSR_P4_IQ_CCCR4, | ||
| 466 | MSR_P4_IQ_CCCR5, | ||
| 467 | }; | ||
| 435 | /* | 468 | /* |
| 436 | * Set up IQ_COUNTER0 to behave like a clock, by having IQ_CCCR0 filter | 469 | * 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 | 470 | * CRU_ESCR0 (with any non-null event selector) through a complemented |
| @@ -473,6 +506,26 @@ static int setup_p4_watchdog(unsigned nmi_hz) | |||
| 473 | evntsel_msr = MSR_P4_CRU_ESCR0; | 506 | evntsel_msr = MSR_P4_CRU_ESCR0; |
| 474 | cccr_msr = MSR_P4_IQ_CCCR0; | 507 | cccr_msr = MSR_P4_IQ_CCCR0; |
| 475 | cccr_val = P4_CCCR_OVF_PMI0 | P4_CCCR_ESCR_SELECT(4); | 508 | cccr_val = P4_CCCR_OVF_PMI0 | P4_CCCR_ESCR_SELECT(4); |
| 509 | |||
| 510 | /* | ||
| 511 | * If we're on the kdump kernel or other situation, we may | ||
| 512 | * still have other performance counter registers set to | ||
| 513 | * interrupt and they'll keep interrupting forever because | ||
| 514 | * of the P4_CCCR_OVF quirk. So we need to ACK all the | ||
| 515 | * pending interrupts and disable all the registers here, | ||
| 516 | * before reenabling the NMI delivery. Refer to p4_rearm() | ||
| 517 | * about the P4_CCCR_OVF quirk. | ||
| 518 | */ | ||
| 519 | if (reset_devices) { | ||
| 520 | unsigned int low, high; | ||
| 521 | int i; | ||
| 522 | |||
| 523 | for (i = 0; i < P4_CONTROLS; i++) { | ||
| 524 | rdmsr(p4_controls[i], low, high); | ||
| 525 | low &= ~(P4_CCCR_ENABLE | P4_CCCR_OVF); | ||
| 526 | wrmsr(p4_controls[i], low, high); | ||
| 527 | } | ||
| 528 | } | ||
| 476 | } else { | 529 | } else { |
| 477 | /* logical cpu 1 */ | 530 | /* logical cpu 1 */ |
| 478 | perfctr_msr = MSR_P4_IQ_PERFCTR1; | 531 | perfctr_msr = MSR_P4_IQ_PERFCTR1; |
| @@ -499,12 +552,17 @@ static int setup_p4_watchdog(unsigned nmi_hz) | |||
| 499 | wrmsr(evntsel_msr, evntsel, 0); | 552 | wrmsr(evntsel_msr, evntsel, 0); |
| 500 | wrmsr(cccr_msr, cccr_val, 0); | 553 | wrmsr(cccr_msr, cccr_val, 0); |
| 501 | write_watchdog_counter(perfctr_msr, "P4_IQ_COUNTER0", nmi_hz); | 554 | write_watchdog_counter(perfctr_msr, "P4_IQ_COUNTER0", nmi_hz); |
| 502 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 555 | |
| 503 | cccr_val |= P4_CCCR_ENABLE; | ||
| 504 | wrmsr(cccr_msr, cccr_val, 0); | ||
| 505 | wd->perfctr_msr = perfctr_msr; | 556 | wd->perfctr_msr = perfctr_msr; |
| 506 | wd->evntsel_msr = evntsel_msr; | 557 | wd->evntsel_msr = evntsel_msr; |
| 507 | wd->cccr_msr = cccr_msr; | 558 | wd->cccr_msr = cccr_msr; |
| 559 | |||
| 560 | /* ok, everything is initialized, announce that we're set */ | ||
| 561 | cpu_nmi_set_wd_enabled(); | ||
| 562 | |||
| 563 | apic_write(APIC_LVTPC, APIC_DM_NMI); | ||
| 564 | cccr_val |= P4_CCCR_ENABLE; | ||
| 565 | wrmsr(cccr_msr, cccr_val, 0); | ||
| 508 | return 1; | 566 | return 1; |
| 509 | } | 567 | } |
| 510 | 568 | ||
| @@ -620,13 +678,17 @@ static int setup_intel_arch_watchdog(unsigned nmi_hz) | |||
| 620 | wrmsr(evntsel_msr, evntsel, 0); | 678 | wrmsr(evntsel_msr, evntsel, 0); |
| 621 | nmi_hz = adjust_for_32bit_ctr(nmi_hz); | 679 | nmi_hz = adjust_for_32bit_ctr(nmi_hz); |
| 622 | write_watchdog_counter32(perfctr_msr, "INTEL_ARCH_PERFCTR0", nmi_hz); | 680 | write_watchdog_counter32(perfctr_msr, "INTEL_ARCH_PERFCTR0", nmi_hz); |
| 623 | apic_write(APIC_LVTPC, APIC_DM_NMI); | ||
| 624 | evntsel |= ARCH_PERFMON_EVENTSEL0_ENABLE; | ||
| 625 | wrmsr(evntsel_msr, evntsel, 0); | ||
| 626 | 681 | ||
| 627 | wd->perfctr_msr = perfctr_msr; | 682 | wd->perfctr_msr = perfctr_msr; |
| 628 | wd->evntsel_msr = evntsel_msr; | 683 | wd->evntsel_msr = evntsel_msr; |
| 629 | wd->cccr_msr = 0; /* unused */ | 684 | wd->cccr_msr = 0; /* unused */ |
| 685 | |||
| 686 | /* ok, everything is initialized, announce that we're set */ | ||
| 687 | cpu_nmi_set_wd_enabled(); | ||
| 688 | |||
| 689 | apic_write(APIC_LVTPC, APIC_DM_NMI); | ||
| 690 | evntsel |= ARCH_PERFMON_EVENTSEL0_ENABLE; | ||
| 691 | wrmsr(evntsel_msr, evntsel, 0); | ||
| 630 | intel_arch_wd_ops.checkbit = 1ULL << (eax.split.bit_width - 1); | 692 | intel_arch_wd_ops.checkbit = 1ULL << (eax.split.bit_width - 1); |
| 631 | return 1; | 693 | return 1; |
| 632 | } | 694 | } |
