[PATCH] x86: Cleanup NMI interrupt path
authorDon Zickus <dzickus@redhat.com>
Tue, 26 Sep 2006 08:52:26 +0000 (10:52 +0200)
committerAndi Kleen <andi@basil.nowhere.org>
Tue, 26 Sep 2006 08:52:26 +0000 (10:52 +0200)
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: Don Zickus <dzickus@redhat.com>
Signed-off-by: Andi Kleen <ak@suse.de>
arch/i386/kernel/nmi.c
arch/i386/kernel/traps.c
arch/x86_64/kernel/nmi.c
arch/x86_64/kernel/traps.c
include/asm-i386/nmi.h
include/asm-x86_64/nmi.h

index d8800434303497b0df822626e4907cae0c52dd1b..bd96ea4f2942e35ba6ab45b850a23b26bc9cb62c 100644 (file)
@@ -781,7 +781,7 @@ EXPORT_SYMBOL(touch_nmi_watchdog);
 
 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)
        int cpu = smp_processor_id();
        struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
        u64 dummy;
+       int rc=0;
 
        /* check for other users first */
        if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT)
                        == NOTIFY_STOP) {
+               rc = 1;
                touched = 1;
        }
 
@@ -850,10 +852,18 @@ void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
                        }
                        /* start the cycle over again */
                        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:
-       return;
+       return rc;
 }
 
 #ifdef CONFIG_SYSCTL
index 3a07b2677e2ae0d4a7ea8f1fd1546b3710aa1167..282f0bd40dfdf19d525ad5233df86160f9c3930c 100644 (file)
@@ -706,6 +706,13 @@ void die_nmi (struct pt_regs *regs, const char *msg)
        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)
 {
        unsigned char reason = 0;
@@ -723,12 +730,11 @@ static void default_do_nmi(struct pt_regs * regs)
                 * Ok, so this is none of the documented NMI sources,
                 * so it must be the NMI watchdog.
                 */
-               if (nmi_watchdog) {
-                       nmi_watchdog_tick(regs, reason);
+               if (nmi_watchdog_tick(regs, reason))
                        return;
-               }
 #endif
-               unknown_nmi_error(reason, regs);
+               if (!rcu_dereference(nmi_callback)(regs, smp_processor_id()))
+                       unknown_nmi_error(reason, regs);
                return;
        }
        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)
        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)
 {
        int cpu;
@@ -761,8 +760,7 @@ fastcall void do_nmi(struct pt_regs * regs, long error_code)
 
        ++nmi_count(cpu);
 
-       if (!rcu_dereference(nmi_callback)(regs, cpu))
-               default_do_nmi(regs);
+       default_do_nmi(regs);
 
        nmi_exit();
 }
index d42374a952d7105804e9da8b4fe46c242ae3808f..f6b881b23a707e425d3495f8bf00e2c3ced5758e 100644 (file)
@@ -682,16 +682,18 @@ void touch_nmi_watchdog (void)
        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 touched = 0;
        struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
        u64 dummy;
+       int rc=0;
 
        /* check for other users first */
        if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT)
                        == NOTIFY_STOP) {
+               rc = 1;
                touched = 1;
        }
 
@@ -746,10 +748,18 @@ void __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
                        }
                        /* 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.
+                        * 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:
-       return;
+       return rc;
 }
 
 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;
  
 asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code)
 {
-       int cpu = safe_smp_processor_id();
-
        nmi_enter();
        add_pda(__nmi_count,1);
-       if (!rcu_dereference(nmi_callback)(regs, cpu))
-               default_do_nmi(regs);
+       default_do_nmi(regs);
        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)
 {
        vmalloc_sync_all();
index b1249774d1e8af251f343357ddb6c627a0515751..42bc070fdf11ef5d27db34cb4fd50e3f0e6bc6cf 100644 (file)
@@ -781,12 +781,12 @@ asmlinkage __kprobes void default_do_nmi(struct pt_regs *regs)
                 * Ok, so this is none of the documented NMI sources,
                 * so it must be the NMI watchdog.
                 */
-               if (nmi_watchdog > 0) {
-                       nmi_watchdog_tick(regs,reason);
+               if (nmi_watchdog_tick(regs,reason))
                        return;
-               }
+               if (!do_nmi_callback(regs,cpu))
 #endif
-               unknown_nmi_error(reason, regs);
+                       unknown_nmi_error(reason, regs);
+
                return;
        }
        if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
index 4cda6801ecb87c74ba6f6dec138a548143c8449f..da0e0b4e9139d1c41fae7be5d92db38409078f57 100644 (file)
@@ -37,7 +37,7 @@ extern int reserve_lapic_nmi(void);
 extern void release_lapic_nmi(void);
 extern void disable_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 unsigned int nmi_watchdog;
index 5918136fd853696cf2bebd151c36ba310673f13d..8f02a2a416e693e424977b4f996932af82aeb44f 100644 (file)
@@ -26,6 +26,14 @@ void set_nmi_callback(nmi_callback_t callback);
  */
 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
  
 /** Replace the PM callback routine for NMI. */
@@ -68,7 +76,7 @@ extern int reserve_lapic_nmi(void);
 extern void release_lapic_nmi(void);
 extern void disable_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 int setup_nmi_watchdog(char *);