Commit 3adbbcce authored by Don Zickus's avatar Don Zickus Committed by Andi Kleen

[PATCH] x86: Cleanup NMI interrupt path

This patch cleans up the NMI interrupt path.  Instead of being gated by if
the 'nmi callback' is set, the interrupt handler now calls everyone who is
registered on the die_chain and additionally checks the nmi watchdog,
reseting it if enabled.  This allows more subsystems to hook into the NMI if
they need to (without being block by set_nmi_callback).
Signed-off-by: default avatarDon Zickus <dzickus@redhat.com>
Signed-off-by: default avatarAndi Kleen <ak@suse.de>
parent b7471c6d
...@@ -781,7 +781,7 @@ EXPORT_SYMBOL(touch_nmi_watchdog); ...@@ -781,7 +781,7 @@ EXPORT_SYMBOL(touch_nmi_watchdog);
extern void die_nmi(struct pt_regs *, const char *msg); extern void die_nmi(struct pt_regs *, const char *msg);
void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason) int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
{ {
/* /*
...@@ -794,10 +794,12 @@ void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason) ...@@ -794,10 +794,12 @@ void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
int cpu = smp_processor_id(); int cpu = smp_processor_id();
struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
u64 dummy; u64 dummy;
int rc=0;
/* check for other users first */ /* check for other users first */
if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT)
== NOTIFY_STOP) { == NOTIFY_STOP) {
rc = 1;
touched = 1; touched = 1;
} }
...@@ -850,10 +852,18 @@ void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason) ...@@ -850,10 +852,18 @@ void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
} }
/* start the cycle over again */ /* start the cycle over again */
write_watchdog_counter(wd->perfctr_msr, NULL); write_watchdog_counter(wd->perfctr_msr, NULL);
} rc = 1;
} else if (nmi_watchdog == NMI_IO_APIC) {
/* don't know how to accurately check for this.
* just assume it was a watchdog timer interrupt
* This matches the old behaviour.
*/
rc = 1;
} else
printk(KERN_WARNING "Unknown enabled NMI hardware?!\n");
} }
done: done:
return; return rc;
} }
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
......
...@@ -706,6 +706,13 @@ void die_nmi (struct pt_regs *regs, const char *msg) ...@@ -706,6 +706,13 @@ void die_nmi (struct pt_regs *regs, const char *msg)
do_exit(SIGSEGV); do_exit(SIGSEGV);
} }
static int dummy_nmi_callback(struct pt_regs * regs, int cpu)
{
return 0;
}
static nmi_callback_t nmi_callback = dummy_nmi_callback;
static void default_do_nmi(struct pt_regs * regs) static void default_do_nmi(struct pt_regs * regs)
{ {
unsigned char reason = 0; unsigned char reason = 0;
...@@ -723,12 +730,11 @@ static void default_do_nmi(struct pt_regs * regs) ...@@ -723,12 +730,11 @@ static void default_do_nmi(struct pt_regs * regs)
* Ok, so this is none of the documented NMI sources, * Ok, so this is none of the documented NMI sources,
* so it must be the NMI watchdog. * so it must be the NMI watchdog.
*/ */
if (nmi_watchdog) { if (nmi_watchdog_tick(regs, reason))
nmi_watchdog_tick(regs, reason);
return; return;
}
#endif #endif
unknown_nmi_error(reason, regs); if (!rcu_dereference(nmi_callback)(regs, smp_processor_id()))
unknown_nmi_error(reason, regs);
return; return;
} }
if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP) if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
...@@ -744,13 +750,6 @@ static void default_do_nmi(struct pt_regs * regs) ...@@ -744,13 +750,6 @@ static void default_do_nmi(struct pt_regs * regs)
reassert_nmi(); reassert_nmi();
} }
static int dummy_nmi_callback(struct pt_regs * regs, int cpu)
{
return 0;
}
static nmi_callback_t nmi_callback = dummy_nmi_callback;
fastcall void do_nmi(struct pt_regs * regs, long error_code) fastcall void do_nmi(struct pt_regs * regs, long error_code)
{ {
int cpu; int cpu;
...@@ -761,8 +760,7 @@ fastcall void do_nmi(struct pt_regs * regs, long error_code) ...@@ -761,8 +760,7 @@ fastcall void do_nmi(struct pt_regs * regs, long error_code)
++nmi_count(cpu); ++nmi_count(cpu);
if (!rcu_dereference(nmi_callback)(regs, cpu)) default_do_nmi(regs);
default_do_nmi(regs);
nmi_exit(); nmi_exit();
} }
......
...@@ -682,16 +682,18 @@ void touch_nmi_watchdog (void) ...@@ -682,16 +682,18 @@ void touch_nmi_watchdog (void)
touch_softlockup_watchdog(); touch_softlockup_watchdog();
} }
void __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason) int __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
{ {
int sum; int sum;
int touched = 0; int touched = 0;
struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
u64 dummy; u64 dummy;
int rc=0;
/* check for other users first */ /* check for other users first */
if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT)
== NOTIFY_STOP) { == NOTIFY_STOP) {
rc = 1;
touched = 1; touched = 1;
} }
...@@ -746,10 +748,18 @@ void __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason) ...@@ -746,10 +748,18 @@ void __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
} }
/* start the cycle over again */ /* start the cycle over again */
wrmsrl(wd->perfctr_msr, -((u64)cpu_khz * 1000 / nmi_hz)); 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.
* just assume it was a watchdog timer interrupt
* This matches the old behaviour.
*/
rc = 1;
} else
printk(KERN_WARNING "Unknown enabled NMI hardware?!\n");
} }
done: done:
return; return rc;
} }
static __kprobes int dummy_nmi_callback(struct pt_regs * regs, int cpu) static __kprobes int dummy_nmi_callback(struct pt_regs * regs, int cpu)
...@@ -761,15 +771,17 @@ static nmi_callback_t nmi_callback = dummy_nmi_callback; ...@@ -761,15 +771,17 @@ static nmi_callback_t nmi_callback = dummy_nmi_callback;
asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code) asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code)
{ {
int cpu = safe_smp_processor_id();
nmi_enter(); nmi_enter();
add_pda(__nmi_count,1); add_pda(__nmi_count,1);
if (!rcu_dereference(nmi_callback)(regs, cpu)) default_do_nmi(regs);
default_do_nmi(regs);
nmi_exit(); nmi_exit();
} }
int do_nmi_callback(struct pt_regs * regs, int cpu)
{
return rcu_dereference(nmi_callback)(regs, cpu);
}
void set_nmi_callback(nmi_callback_t callback) void set_nmi_callback(nmi_callback_t callback)
{ {
vmalloc_sync_all(); vmalloc_sync_all();
......
...@@ -781,12 +781,12 @@ asmlinkage __kprobes void default_do_nmi(struct pt_regs *regs) ...@@ -781,12 +781,12 @@ asmlinkage __kprobes void default_do_nmi(struct pt_regs *regs)
* Ok, so this is none of the documented NMI sources, * Ok, so this is none of the documented NMI sources,
* so it must be the NMI watchdog. * so it must be the NMI watchdog.
*/ */
if (nmi_watchdog > 0) { if (nmi_watchdog_tick(regs,reason))
nmi_watchdog_tick(regs,reason);
return; return;
} if (!do_nmi_callback(regs,cpu))
#endif #endif
unknown_nmi_error(reason, regs); unknown_nmi_error(reason, regs);
return; return;
} }
if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP) if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
......
...@@ -37,7 +37,7 @@ extern int reserve_lapic_nmi(void); ...@@ -37,7 +37,7 @@ extern int reserve_lapic_nmi(void);
extern void release_lapic_nmi(void); extern void release_lapic_nmi(void);
extern void disable_timer_nmi_watchdog(void); extern void disable_timer_nmi_watchdog(void);
extern void enable_timer_nmi_watchdog(void); extern void enable_timer_nmi_watchdog(void);
extern void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason); extern int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason);
extern atomic_t nmi_active; extern atomic_t nmi_active;
extern unsigned int nmi_watchdog; extern unsigned int nmi_watchdog;
......
...@@ -26,6 +26,14 @@ void set_nmi_callback(nmi_callback_t callback); ...@@ -26,6 +26,14 @@ void set_nmi_callback(nmi_callback_t callback);
*/ */
void unset_nmi_callback(void); void unset_nmi_callback(void);
/**
* do_nmi_callback
*
* Check to see if a callback exists and execute it. Return 1
* if the handler exists and was handled successfully.
*/
int do_nmi_callback(struct pt_regs *regs, int cpu);
#ifdef CONFIG_PM #ifdef CONFIG_PM
/** Replace the PM callback routine for NMI. */ /** Replace the PM callback routine for NMI. */
...@@ -68,7 +76,7 @@ extern int reserve_lapic_nmi(void); ...@@ -68,7 +76,7 @@ extern int reserve_lapic_nmi(void);
extern void release_lapic_nmi(void); extern void release_lapic_nmi(void);
extern void disable_timer_nmi_watchdog(void); extern void disable_timer_nmi_watchdog(void);
extern void enable_timer_nmi_watchdog(void); extern void enable_timer_nmi_watchdog(void);
extern void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason); extern int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason);
extern void nmi_watchdog_default(void); extern void nmi_watchdog_default(void);
extern int setup_nmi_watchdog(char *); extern int setup_nmi_watchdog(char *);
......
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