Commit 7574828b authored by Andi Kleen's avatar Andi Kleen Committed by Linus Torvalds

[PATCH] x86_64: add nmi button support

Ported from i386

Support a sysctl to raise an oops with an NMI
Signed-off-by: default avatarAndi Kleen <ak@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 8d62810b
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/sysdev.h> #include <linux/sysdev.h>
#include <linux/nmi.h> #include <linux/nmi.h>
#include <linux/sysctl.h>
#include <asm/smp.h> #include <asm/smp.h>
#include <asm/mtrr.h> #include <asm/mtrr.h>
...@@ -54,7 +55,7 @@ static unsigned int lapic_nmi_owner; ...@@ -54,7 +55,7 @@ static unsigned int lapic_nmi_owner;
* -1: the lapic NMI watchdog is disabled, but can be enabled * -1: the lapic NMI watchdog is disabled, but can be enabled
*/ */
int nmi_active; /* oprofile uses this */ int nmi_active; /* oprofile uses this */
static int panic_on_timeout; int panic_on_timeout;
unsigned int nmi_watchdog = NMI_DEFAULT; unsigned int nmi_watchdog = NMI_DEFAULT;
static unsigned int nmi_hz = HZ; static unsigned int nmi_hz = HZ;
...@@ -344,8 +345,6 @@ void setup_apic_nmi_watchdog(void) ...@@ -344,8 +345,6 @@ void setup_apic_nmi_watchdog(void)
nmi_active = 1; nmi_active = 1;
} }
static spinlock_t nmi_print_lock = SPIN_LOCK_UNLOCKED;
/* /*
* the best way to detect whether a CPU has a 'hard lockup' problem * the best way to detect whether a CPU has a 'hard lockup' problem
* is to check it's local APIC timer IRQ counts. If they are not * is to check it's local APIC timer IRQ counts. If they are not
...@@ -395,21 +394,7 @@ void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason) ...@@ -395,21 +394,7 @@ void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
alert_counter[cpu] = 0; alert_counter[cpu] = 0;
return; return;
} }
spin_lock(&nmi_print_lock); die_nmi("NMI Watchdog detected LOCKUP on CPU%d", regs);
/*
* We are in trouble anyway, lets at least try
* to get a message out.
*/
bust_spinlocks(1);
printk("NMI Watchdog detected LOCKUP on CPU%d, registers:\n", cpu);
show_registers(regs);
if (panic_on_timeout || panic_on_oops)
panic("nmi watchdog");
printk("console shuts up ...\n");
console_silent();
spin_unlock(&nmi_print_lock);
bust_spinlocks(0);
do_exit(SIGSEGV);
} }
} else { } else {
last_irq_sums[cpu] = sum; last_irq_sums[cpu] = sum;
...@@ -447,6 +432,49 @@ void unset_nmi_callback(void) ...@@ -447,6 +432,49 @@ void unset_nmi_callback(void)
nmi_callback = dummy_nmi_callback; nmi_callback = dummy_nmi_callback;
} }
#ifdef CONFIG_SYSCTL
static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
{
unsigned char reason = get_nmi_reason();
char buf[64];
if (!(reason & 0xc0)) {
sprintf(buf, "NMI received for unknown reason %02x\n", reason);
die_nmi(buf,regs);
}
return 0;
}
/*
* proc handler for /proc/sys/kernel/unknown_nmi_panic
*/
int proc_unknown_nmi_panic(struct ctl_table *table, int write, struct file *file,
void __user *buffer, size_t *length, loff_t *ppos)
{
int old_state;
old_state = unknown_nmi_panic;
proc_dointvec(table, write, file, buffer, length, ppos);
if (!!old_state == !!unknown_nmi_panic)
return 0;
if (unknown_nmi_panic) {
if (reserve_lapic_nmi() < 0) {
unknown_nmi_panic = 0;
return -EBUSY;
} else {
set_nmi_callback(unknown_nmi_panic_callback);
}
} else {
release_lapic_nmi();
unset_nmi_callback();
}
return 0;
}
#endif
EXPORT_SYMBOL(nmi_active); EXPORT_SYMBOL(nmi_active);
EXPORT_SYMBOL(nmi_watchdog); EXPORT_SYMBOL(nmi_watchdog);
EXPORT_SYMBOL(reserve_lapic_nmi); EXPORT_SYMBOL(reserve_lapic_nmi);
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
#include <asm/pda.h> #include <asm/pda.h>
#include <asm/proto.h> #include <asm/proto.h>
#include <asm/nmi.h>
#include <linux/irq.h> #include <linux/irq.h>
...@@ -376,6 +377,22 @@ static inline void die_if_kernel(const char * str, struct pt_regs * regs, long e ...@@ -376,6 +377,22 @@ static inline void die_if_kernel(const char * str, struct pt_regs * regs, long e
die(str, regs, err); die(str, regs, err);
} }
void die_nmi(char *str, struct pt_regs *regs)
{
oops_begin();
/*
* We are in trouble anyway, lets at least try
* to get a message out.
*/
printk(str, safe_smp_processor_id());
show_registers(regs);
if (panic_on_timeout || panic_on_oops)
panic("nmi watchdog");
printk("console shuts up ...\n");
oops_end();
do_exit(SIGSEGV);
}
static inline unsigned long get_cr2(void) static inline unsigned long get_cr2(void)
{ {
unsigned long address; unsigned long address;
...@@ -565,7 +582,7 @@ static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs) ...@@ -565,7 +582,7 @@ static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
asmlinkage void default_do_nmi(struct pt_regs * regs) asmlinkage void default_do_nmi(struct pt_regs * regs)
{ {
unsigned char reason = inb(0x61); unsigned char reason = get_nmi_reason();
if (!(reason & 0xc0)) { if (!(reason & 0xc0)) {
if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 0, SIGINT) if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 0, SIGINT)
...@@ -586,6 +603,9 @@ asmlinkage void default_do_nmi(struct pt_regs * regs) ...@@ -586,6 +603,9 @@ asmlinkage void default_do_nmi(struct pt_regs * regs)
} }
if (notify_die(DIE_NMI, "nmi", regs, reason, 0, SIGINT) == NOTIFY_STOP) if (notify_die(DIE_NMI, "nmi", regs, reason, 0, SIGINT) == NOTIFY_STOP)
return; return;
/* AK: following checks seem to be broken on modern chipsets. FIXME */
if (reason & 0x80) if (reason & 0x80)
mem_parity_error(reason, regs); mem_parity_error(reason, regs);
if (reason & 0x40) if (reason & 0x40)
......
...@@ -47,5 +47,11 @@ static inline void unset_nmi_pm_callback(struct pm_dev * dev) ...@@ -47,5 +47,11 @@ static inline void unset_nmi_pm_callback(struct pm_dev * dev)
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
extern void default_do_nmi(struct pt_regs *); extern void default_do_nmi(struct pt_regs *);
extern void die_nmi(char *str, struct pt_regs *regs);
#define get_nmi_reason() inb(0x61)
extern int panic_on_timeout;
extern int unknown_nmi_panic;
#endif /* ASM_NMI_H */ #endif /* ASM_NMI_H */
...@@ -68,7 +68,7 @@ extern int printk_ratelimit_jiffies; ...@@ -68,7 +68,7 @@ extern int printk_ratelimit_jiffies;
extern int printk_ratelimit_burst; extern int printk_ratelimit_burst;
extern int pid_max_min, pid_max_max; extern int pid_max_min, pid_max_max;
#if defined(CONFIG_X86_LOCAL_APIC) && defined(__i386__) #if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86)
int unknown_nmi_panic; int unknown_nmi_panic;
extern int proc_unknown_nmi_panic(ctl_table *, int, struct file *, extern int proc_unknown_nmi_panic(ctl_table *, int, struct file *,
void __user *, size_t *, loff_t *); void __user *, size_t *, loff_t *);
...@@ -614,7 +614,7 @@ static ctl_table kern_table[] = { ...@@ -614,7 +614,7 @@ static ctl_table kern_table[] = {
.mode = 0444, .mode = 0444,
.proc_handler = &proc_dointvec, .proc_handler = &proc_dointvec,
}, },
#if defined(CONFIG_X86_LOCAL_APIC) && defined(__i386__) #if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86)
{ {
.ctl_name = KERN_UNKNOWN_NMI_PANIC, .ctl_name = KERN_UNKNOWN_NMI_PANIC,
.procname = "unknown_nmi_panic", .procname = "unknown_nmi_panic",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment