Skip to content

Commit

Permalink
[PATCH] x86-64: Handle 32 bit PerfMon Counter writes cleanly in x86_6…
Browse files Browse the repository at this point in the history
…4 nmi_watchdog

P6 CPUs and Core/Core 2 CPUs which has 'architectural perf mon' feature,
only supports write of low 32 bits in Performance Monitoring Counters.
Bits 32..39 are sign extended based on bit 31 and bits 40..63 are reserved
and should be zero.

This patch:

Change x86_64 nmi handler to handle this case cleanly.

Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: Andi Kleen <ak@suse.de>
  • Loading branch information
Venkatesh Pallipadi authored and Andi Kleen committed Feb 13, 2007
1 parent 4c3cbf7 commit 1676193
Showing 1 changed file with 32 additions and 14 deletions.
46 changes: 32 additions & 14 deletions arch/x86_64/kernel/nmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,23 @@ static __init void nmi_cpu_busy(void *data)
}
#endif

static unsigned int adjust_for_32bit_ctr(unsigned int hz)
{
unsigned int retval = hz;

/*
* On Intel CPUs with ARCH_PERFMON only 32 bits in the counter
* are writable, with higher bits sign extending from bit 31.
* So, we can only program the counter with 31 bit values and
* 32nd bit should be 1, for 33.. to be 1.
* Find the appropriate nmi_hz
*/
if ((((u64)cpu_khz * 1000) / retval) > 0x7fffffffULL) {
retval = ((u64)cpu_khz * 1000) / 0x7fffffffUL + 1;
}
return retval;
}

int __init check_nmi_watchdog (void)
{
int *counts;
Expand Down Expand Up @@ -268,17 +285,8 @@ int __init check_nmi_watchdog (void)
struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);

nmi_hz = 1;
/*
* On Intel CPUs with ARCH_PERFMON only 32 bits in the counter
* are writable, with higher bits sign extending from bit 31.
* So, we can only program the counter with 31 bit values and
* 32nd bit should be 1, for 33.. to be 1.
* Find the appropriate nmi_hz
*/
if (wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0 &&
((u64)cpu_khz * 1000) > 0x7fffffffULL) {
nmi_hz = ((u64)cpu_khz * 1000) / 0x7fffffffUL + 1;
}
if (wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0)
nmi_hz = adjust_for_32bit_ctr(nmi_hz);
}

kfree(counts);
Expand Down Expand Up @@ -634,7 +642,9 @@ static int setup_intel_arch_watchdog(void)

/* setup the timer */
wrmsr(evntsel_msr, evntsel, 0);
wrmsrl(perfctr_msr, -((u64)cpu_khz * 1000 / nmi_hz));

nmi_hz = adjust_for_32bit_ctr(nmi_hz);
wrmsr(perfctr_msr, (u32)(-((u64)cpu_khz * 1000 / nmi_hz)), 0);

apic_write(APIC_LVTPC, APIC_DM_NMI);
evntsel |= ARCH_PERFMON_EVENTSEL0_ENABLE;
Expand Down Expand Up @@ -855,15 +865,23 @@ int __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
dummy &= ~P4_CCCR_OVF;
wrmsrl(wd->cccr_msr, dummy);
apic_write(APIC_LVTPC, APIC_DM_NMI);
/* start the cycle over again */
wrmsrl(wd->perfctr_msr,
-((u64)cpu_khz * 1000 / nmi_hz));
} else if (wd->perfctr_msr == MSR_ARCH_PERFMON_PERFCTR0) {
/*
* ArchPerfom/Core Duo needs to re-unmask
* the apic vector
*/
apic_write(APIC_LVTPC, APIC_DM_NMI);
/* ARCH_PERFMON has 32 bit counter writes */
wrmsr(wd->perfctr_msr,
(u32)(-((u64)cpu_khz * 1000 / nmi_hz)), 0);
} else {
/* start the cycle over again */
wrmsrl(wd->perfctr_msr,
-((u64)cpu_khz * 1000 / nmi_hz));
}
/* start the cycle over again */
wrmsrl(wd->perfctr_msr, -((u64)cpu_khz * 1000 / nmi_hz));
rc = 1;
} else if (nmi_watchdog == NMI_IO_APIC) {
/* don't know how to accurately check for this.
Expand Down

0 comments on commit 1676193

Please sign in to comment.