mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
Pull nmi/traps changes from Linux 2.6.14.
Signed-off-by: Keir Fraser <keir@xensource.com>
This commit is contained in:
1 parent
b978d1410a
commit
af50dd63cf
4 files changed
+188
-20
No files matched your search
+124
-16
@@ -9,7 +9,8 @@
|
||||
* Mikael Pettersson : AMD K7 support for local APIC NMI watchdog.
|
||||
* Mikael Pettersson : Power Management for local APIC NMI watchdog.
|
||||
* Mikael Pettersson : Pentium 4 support for local APIC NMI watchdog.
|
||||
* Keir Fraser : Pentium 4 Hyperthreading support
|
||||
* Pavel Machek and
|
||||
* Mikael Pettersson : PM converted to driver model. Disable/enable API.
|
||||
*/
|
||||
|
||||
#include <xen/config.h>
|
||||
@@ -27,6 +28,7 @@
|
||||
#include <asm/msr.h>
|
||||
#include <asm/mpspec.h>
|
||||
#include <asm/debugger.h>
|
||||
#include <asm/div64.h>
|
||||
|
||||
unsigned int nmi_watchdog = NMI_NONE;
|
||||
static unsigned int nmi_hz = HZ;
|
||||
@@ -35,6 +37,28 @@ static unsigned int nmi_p4_cccr_val;
|
||||
static struct ac_timer nmi_timer[NR_CPUS];
|
||||
static unsigned int nmi_timer_ticks[NR_CPUS];
|
||||
|
||||
/*
|
||||
* lapic_nmi_owner tracks the ownership of the lapic NMI hardware:
|
||||
* - it may be reserved by some other driver, or not
|
||||
* - when not reserved by some other driver, it may be used for
|
||||
* the NMI watchdog, or not
|
||||
*
|
||||
* This is maintained separately from nmi_active because the NMI
|
||||
* watchdog may also be driven from the I/O APIC timer.
|
||||
*/
|
||||
static DEFINE_SPINLOCK(lapic_nmi_owner_lock);
|
||||
static unsigned int lapic_nmi_owner;
|
||||
#define LAPIC_NMI_WATCHDOG (1<<0)
|
||||
#define LAPIC_NMI_RESERVED (1<<1)
|
||||
|
||||
/* nmi_active:
|
||||
* +1: the lapic NMI watchdog is active, but can be disabled
|
||||
* 0: the lapic NMI watchdog has not been set up, and cannot
|
||||
* be enabled
|
||||
* -1: the lapic NMI watchdog is disabled, but can be enabled
|
||||
*/
|
||||
int nmi_active;
|
||||
|
||||
#define K7_EVNTSEL_ENABLE (1 << 22)
|
||||
#define K7_EVNTSEL_INT (1 << 20)
|
||||
#define K7_EVNTSEL_OS (1 << 17)
|
||||
@@ -111,8 +135,73 @@ static void nmi_timer_fn(void *unused)
|
||||
set_ac_timer(&nmi_timer[cpu], NOW() + MILLISECS(1000));
|
||||
}
|
||||
|
||||
static inline void nmi_pm_init(void) { }
|
||||
#define __pminit __init
|
||||
static void disable_lapic_nmi_watchdog(void)
|
||||
{
|
||||
if (nmi_active <= 0)
|
||||
return;
|
||||
switch (boot_cpu_data.x86_vendor) {
|
||||
case X86_VENDOR_AMD:
|
||||
wrmsr(MSR_K7_EVNTSEL0, 0, 0);
|
||||
break;
|
||||
case X86_VENDOR_INTEL:
|
||||
switch (boot_cpu_data.x86) {
|
||||
case 6:
|
||||
if (boot_cpu_data.x86_model > 0xd)
|
||||
break;
|
||||
|
||||
wrmsr(MSR_P6_EVNTSEL0, 0, 0);
|
||||
break;
|
||||
case 15:
|
||||
if (boot_cpu_data.x86_model > 0x4)
|
||||
break;
|
||||
|
||||
wrmsr(MSR_P4_IQ_CCCR0, 0, 0);
|
||||
wrmsr(MSR_P4_CRU_ESCR0, 0, 0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
nmi_active = -1;
|
||||
/* tell do_nmi() and others that we're not active any more */
|
||||
nmi_watchdog = 0;
|
||||
}
|
||||
|
||||
static void enable_lapic_nmi_watchdog(void)
|
||||
{
|
||||
if (nmi_active < 0) {
|
||||
nmi_watchdog = NMI_LOCAL_APIC;
|
||||
setup_apic_nmi_watchdog();
|
||||
}
|
||||
}
|
||||
|
||||
int reserve_lapic_nmi(void)
|
||||
{
|
||||
unsigned int old_owner;
|
||||
|
||||
spin_lock(&lapic_nmi_owner_lock);
|
||||
old_owner = lapic_nmi_owner;
|
||||
lapic_nmi_owner |= LAPIC_NMI_RESERVED;
|
||||
spin_unlock(&lapic_nmi_owner_lock);
|
||||
if (old_owner & LAPIC_NMI_RESERVED)
|
||||
return -EBUSY;
|
||||
if (old_owner & LAPIC_NMI_WATCHDOG)
|
||||
disable_lapic_nmi_watchdog();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void release_lapic_nmi(void)
|
||||
{
|
||||
unsigned int new_owner;
|
||||
|
||||
spin_lock(&lapic_nmi_owner_lock);
|
||||
new_owner = lapic_nmi_owner & ~LAPIC_NMI_RESERVED;
|
||||
lapic_nmi_owner = new_owner;
|
||||
spin_unlock(&lapic_nmi_owner_lock);
|
||||
if (new_owner & LAPIC_NMI_WATCHDOG)
|
||||
enable_lapic_nmi_watchdog();
|
||||
}
|
||||
|
||||
#define __pminit __init
|
||||
|
||||
/*
|
||||
* Activate the NMI watchdog via the local APIC.
|
||||
@@ -122,10 +211,21 @@ static inline void nmi_pm_init(void) { }
|
||||
static void __pminit clear_msr_range(unsigned int base, unsigned int n)
|
||||
{
|
||||
unsigned int i;
|
||||
for ( i = 0; i < n; i++ )
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
wrmsr(base+i, 0, 0);
|
||||
}
|
||||
|
||||
static inline void write_watchdog_counter(const char *descr)
|
||||
{
|
||||
u64 count = (u64)cpu_khz * 1000;
|
||||
|
||||
do_div(count, nmi_hz);
|
||||
if(descr)
|
||||
Dprintk("setting %s to -0x%08Lx\n", descr, count);
|
||||
wrmsrl(nmi_perfctr_msr, 0 - count);
|
||||
}
|
||||
|
||||
static void __pminit setup_k7_watchdog(void)
|
||||
{
|
||||
unsigned int evntsel;
|
||||
@@ -141,8 +241,7 @@ static void __pminit setup_k7_watchdog(void)
|
||||
| K7_NMI_EVENT;
|
||||
|
||||
wrmsr(MSR_K7_EVNTSEL0, evntsel, 0);
|
||||
Dprintk("setting K7_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000));
|
||||
wrmsr(MSR_K7_PERFCTR0, -(cpu_khz/nmi_hz*1000), -1);
|
||||
write_watchdog_counter("K7_PERFCTR0");
|
||||
apic_write(APIC_LVTPC, APIC_DM_NMI);
|
||||
evntsel |= K7_EVNTSEL_ENABLE;
|
||||
wrmsr(MSR_K7_EVNTSEL0, evntsel, 0);
|
||||
@@ -163,8 +262,7 @@ static void __pminit setup_p6_watchdog(void)
|
||||
| P6_NMI_EVENT;
|
||||
|
||||
wrmsr(MSR_P6_EVNTSEL0, evntsel, 0);
|
||||
Dprintk("setting P6_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000));
|
||||
wrmsr(MSR_P6_PERFCTR0, -(cpu_khz/nmi_hz*1000), 0);
|
||||
write_watchdog_counter("P6_PERFCTR0");
|
||||
apic_write(APIC_LVTPC, APIC_DM_NMI);
|
||||
evntsel |= P6_EVNTSEL0_ENABLE;
|
||||
wrmsr(MSR_P6_EVNTSEL0, evntsel, 0);
|
||||
@@ -187,7 +285,13 @@ static int __pminit setup_p4_watchdog(void)
|
||||
clear_msr_range(0x3F1, 2);
|
||||
/* MSR 0x3F0 seems to have a default value of 0xFC00, but current
|
||||
docs doesn't fully define it, so leave it alone for now. */
|
||||
clear_msr_range(0x3A0, 31);
|
||||
if (boot_cpu_data.x86_model >= 0x3) {
|
||||
/* MSR_P4_IQ_ESCR0/1 (0x3ba/0x3bb) removed */
|
||||
clear_msr_range(0x3A0, 26);
|
||||
clear_msr_range(0x3BC, 3);
|
||||
} else {
|
||||
clear_msr_range(0x3A0, 31);
|
||||
}
|
||||
clear_msr_range(0x3C0, 6);
|
||||
clear_msr_range(0x3C8, 6);
|
||||
clear_msr_range(0x3E0, 2);
|
||||
@@ -196,11 +300,9 @@ static int __pminit setup_p4_watchdog(void)
|
||||
|
||||
wrmsr(MSR_P4_CRU_ESCR0, P4_NMI_CRU_ESCR0, 0);
|
||||
wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0 & ~P4_CCCR_ENABLE, 0);
|
||||
Dprintk("setting P4_IQ_PERFCTR0 to 0x%08lx\n", -(cpu_khz/nmi_hz*1000));
|
||||
wrmsr(MSR_P4_IQ_PERFCTR0, -(cpu_khz/nmi_hz*1000), -1);
|
||||
write_watchdog_counter("P4_IQ_COUNTER0");
|
||||
apic_write(APIC_LVTPC, APIC_DM_NMI);
|
||||
wrmsr(MSR_P4_IQ_CCCR0, nmi_p4_cccr_val, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -220,9 +322,15 @@ void __pminit setup_apic_nmi_watchdog(void)
|
||||
case X86_VENDOR_INTEL:
|
||||
switch (boot_cpu_data.x86) {
|
||||
case 6:
|
||||
if (boot_cpu_data.x86_model > 0xd)
|
||||
return;
|
||||
|
||||
setup_p6_watchdog();
|
||||
break;
|
||||
case 15:
|
||||
if (boot_cpu_data.x86_model > 0x4)
|
||||
return;
|
||||
|
||||
if (!setup_p4_watchdog())
|
||||
return;
|
||||
break;
|
||||
@@ -234,12 +342,12 @@ void __pminit setup_apic_nmi_watchdog(void)
|
||||
return;
|
||||
}
|
||||
|
||||
lapic_nmi_owner = LAPIC_NMI_WATCHDOG;
|
||||
nmi_active = 1;
|
||||
|
||||
init_ac_timer(&nmi_timer[cpu], nmi_timer_fn, NULL, cpu);
|
||||
|
||||
nmi_pm_init();
|
||||
}
|
||||
|
||||
|
||||
static unsigned int
|
||||
last_irq_sums [NR_CPUS],
|
||||
alert_counter [NR_CPUS];
|
||||
@@ -329,6 +437,6 @@ void nmi_watchdog_tick(struct cpu_user_regs * regs)
|
||||
*/
|
||||
apic_write(APIC_LVTPC, APIC_DM_NMI);
|
||||
}
|
||||
wrmsr(nmi_perfctr_msr, -(cpu_khz/nmi_hz*1000), -1);
|
||||
write_watchdog_counter(NULL);
|
||||
}
|
||||
}
|
||||
+29
-3
@@ -54,6 +54,7 @@
|
||||
#include <asm/debugger.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/x86_emulate.h>
|
||||
#include <asm/nmi.h>
|
||||
|
||||
/*
|
||||
* opt_nmi: one of 'ignore', 'dom0', or 'fatal'.
|
||||
@@ -1131,10 +1132,8 @@ static void unknown_nmi_error(unsigned char reason)
|
||||
printk("Do you have a strange power saving mode enabled?\n");
|
||||
}
|
||||
|
||||
asmlinkage void do_nmi(struct cpu_user_regs *regs, unsigned long reason)
|
||||
static void default_do_nmi(struct cpu_user_regs *regs, unsigned long reason)
|
||||
{
|
||||
++nmi_count(smp_processor_id());
|
||||
|
||||
if ( nmi_watchdog )
|
||||
nmi_watchdog_tick(regs);
|
||||
|
||||
@@ -1146,6 +1145,33 @@ asmlinkage void do_nmi(struct cpu_user_regs *regs, unsigned long reason)
|
||||
unknown_nmi_error((unsigned char)(reason&0xff));
|
||||
}
|
||||
|
||||
static int dummy_nmi_callback(struct cpu_user_regs *regs, int cpu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static nmi_callback_t nmi_callback = dummy_nmi_callback;
|
||||
|
||||
asmlinkage void do_nmi(struct cpu_user_regs *regs, unsigned long reason)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
|
||||
++nmi_count(cpu);
|
||||
|
||||
if ( !nmi_callback(regs, cpu) )
|
||||
default_do_nmi(regs, reason);
|
||||
}
|
||||
|
||||
void set_nmi_callback(nmi_callback_t callback)
|
||||
{
|
||||
nmi_callback = callback;
|
||||
}
|
||||
|
||||
void unset_nmi_callback(void)
|
||||
{
|
||||
nmi_callback = dummy_nmi_callback;
|
||||
}
|
||||
|
||||
asmlinkage int math_state_restore(struct cpu_user_regs *regs)
|
||||
{
|
||||
struct trap_bounce *tb;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef __ASM_MSR_H
|
||||
#define __ASM_MSR_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#define rdmsr(msr,val1,val2) \
|
||||
__asm__ __volatile__("rdmsr" \
|
||||
: "=a" (val1), "=d" (val2) \
|
||||
@@ -18,7 +20,13 @@
|
||||
: /* no outputs */ \
|
||||
: "c" (msr), "a" (val1), "d" (val2))
|
||||
|
||||
#define wrmsrl(msr,val) wrmsr(msr,(__u32)((__u64)(val)),((__u64)(val))>>32)
|
||||
static inline void wrmsrl(unsigned int msr, __u64 val)
|
||||
{
|
||||
__u32 lo, hi;
|
||||
lo = (__u32)val;
|
||||
hi = (__u32)(val >> 32);
|
||||
wrmsr(msr, lo, hi);
|
||||
}
|
||||
|
||||
#define rdmsr_user(msr,val1,val2) ({\
|
||||
int _rc; \
|
||||
@@ -74,6 +82,8 @@
|
||||
: "=a" (low), "=d" (high) \
|
||||
: "c" (counter))
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
||||
/* symbolic names for some interesting MSRs */
|
||||
/* Intel defined MSRs. */
|
||||
#define MSR_IA32_P5_MC_ADDR 0
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
#ifndef ASM_NMI_H
|
||||
#define ASM_NMI_H
|
||||
|
||||
struct cpu_user_regs;
|
||||
|
||||
typedef int (*nmi_callback_t)(struct cpu_user_regs *regs, int cpu);
|
||||
|
||||
/**
|
||||
* set_nmi_callback
|
||||
*
|
||||
* Set a handler for an NMI. Only one handler may be
|
||||
* set. Return 1 if the NMI was handled.
|
||||
*/
|
||||
void set_nmi_callback(nmi_callback_t callback);
|
||||
|
||||
/**
|
||||
* unset_nmi_callback
|
||||
*
|
||||
* Remove the handler previously set.
|
||||
*/
|
||||
void unset_nmi_callback(void);
|
||||
|
||||
#endif /* ASM_NMI_H */
|
||||