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 @@
#include <linux/module.h>
#include <linux/sysdev.h>
#include <linux/nmi.h>
#include <linux/sysctl.h>
#include <asm/smp.h>
#include <asm/mtrr.h>
......@@ -54,7 +55,7 @@ static unsigned int lapic_nmi_owner;
* -1: the lapic NMI watchdog is disabled, but can be enabled
*/
int nmi_active; /* oprofile uses this */
static int panic_on_timeout;
int panic_on_timeout;
unsigned int nmi_watchdog = NMI_DEFAULT;
static unsigned int nmi_hz = HZ;
......@@ -344,8 +345,6 @@ void setup_apic_nmi_watchdog(void)
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
* 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)
alert_counter[cpu] = 0;
return;
}
spin_lock(&nmi_print_lock);
/*
* 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);
die_nmi("NMI Watchdog detected LOCKUP on CPU%d", regs);
}
} else {
last_irq_sums[cpu] = sum;
......@@ -447,6 +432,49 @@ void unset_nmi_callback(void)
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_watchdog);
EXPORT_SYMBOL(reserve_lapic_nmi);
......
......@@ -43,6 +43,7 @@
#include <asm/pgalloc.h>
#include <asm/pda.h>
#include <asm/proto.h>
#include <asm/nmi.h>
#include <linux/irq.h>
......@@ -376,6 +377,22 @@ static inline void die_if_kernel(const char * str, struct pt_regs * regs, long e
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)
{
unsigned long address;
......@@ -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)
{
unsigned char reason = inb(0x61);
unsigned char reason = get_nmi_reason();
if (!(reason & 0xc0)) {
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)
}
if (notify_die(DIE_NMI, "nmi", regs, reason, 0, SIGINT) == NOTIFY_STOP)
return;
/* AK: following checks seem to be broken on modern chipsets. FIXME */
if (reason & 0x80)
mem_parity_error(reason, regs);
if (reason & 0x40)
......
......@@ -47,5 +47,11 @@ static inline void unset_nmi_pm_callback(struct pm_dev * dev)
#endif /* CONFIG_PM */
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 */
......@@ -68,7 +68,7 @@ extern int printk_ratelimit_jiffies;
extern int printk_ratelimit_burst;
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;
extern int proc_unknown_nmi_panic(ctl_table *, int, struct file *,
void __user *, size_t *, loff_t *);
......@@ -614,7 +614,7 @@ static ctl_table kern_table[] = {
.mode = 0444,
.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,
.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