History

[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).

Don Zickus committed 20 years ago

Commit 3adbbcc1 parent b7471c66 files · +57 −29Browse files

Unified · Split

6 files
  1. arch/i386/kernel/nmi.c +13 −3
  2. arch/i386/kernel/traps.c +11 −13
  3. arch/x86_64/kernel/nmi.c +19 −7
  4. arch/x86_64/kernel/traps.c +4 −4
  5. include/asm-i386/nmi.h +1 −1
  6. include/asm-x86_64/nmi.h +9 −1
arch/i386/kernel/nmi.c+13−3
@@ -781,7 +781,7 @@ EXPORT_SYMBOL(touch_nmi_watchdog); · 780 unmodified lines
781781
782782 extern void die_nmi(struct pt_regs *, const char *msg);
783783
784−void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
784+int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
785785 {
786786
787787 /*
@@ -794,10 +794,12 @@ void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason) · 6 unmodified lines
794794 int cpu = smp_processor_id();
795795 struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
796796 u64 dummy;
797+ int rc=0;
797798
798799 /* check for other users first */
799800 if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT)
800801 == NOTIFY_STOP) {
802+ rc = 1;
801803 touched = 1;
802804 }
803805
@@ -850,10 +852,18 @@ void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason) · 46 unmodified lines
850852 }
851853 /* start the cycle over again */
852854 write_watchdog_counter(wd->perfctr_msr, NULL);
853− }
855+ rc = 1;
856+ } else if (nmi_watchdog == NMI_IO_APIC) {
857+ /* don't know how to accurately check for this.
858+ * just assume it was a watchdog timer interrupt
859+ * This matches the old behaviour.
860+ */
861+ rc = 1;
862+ } else
863+ printk(KERN_WARNING "Unknown enabled NMI hardware?!\n");
854864 }
855865 done:
856− return;
866+ return rc;
857867 }
858868
859869 #ifdef CONFIG_SYSCTL
arch/i386/kernel/traps.c+11−13
@@ -706,6 +706,13 @@ void die_nmi (struct pt_regs *regs, const char *msg) · 705 unmodified lines
706706 do_exit(SIGSEGV);
707707 }
708708
709+static int dummy_nmi_callback(struct pt_regs * regs, int cpu)
710+{
711+ return 0;
712+}
713+
714+static nmi_callback_t nmi_callback = dummy_nmi_callback;
715+
709716 static void default_do_nmi(struct pt_regs * regs)
710717 {
711718 unsigned char reason = 0;
@@ -723,12 +730,11 @@ static void default_do_nmi(struct pt_regs * regs) · 11 unmodified lines
723730 * Ok, so this is none of the documented NMI sources,
724731 * so it must be the NMI watchdog.
725732 */
726− if (nmi_watchdog) {
727− nmi_watchdog_tick(regs, reason);
733+ if (nmi_watchdog_tick(regs, reason))
728734 return;
729− }
730735 #endif
731− unknown_nmi_error(reason, regs);
736+ if (!rcu_dereference(nmi_callback)(regs, smp_processor_id()))
737+ unknown_nmi_error(reason, regs);
732738 return;
733739 }
734740 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) · 9 unmodified lines
744750 reassert_nmi();
745751 }
746752
747−static int dummy_nmi_callback(struct pt_regs * regs, int cpu)
748−{
749− return 0;
750−}
751−
752−static nmi_callback_t nmi_callback = dummy_nmi_callback;
753−
754753 fastcall void do_nmi(struct pt_regs * regs, long error_code)
755754 {
756755 int cpu;
@@ -761,8 +760,7 @@ fastcall void do_nmi(struct pt_regs * regs, long error_code) · 4 unmodified lines
761760
762761 ++nmi_count(cpu);
763762
764− if (!rcu_dereference(nmi_callback)(regs, cpu))
765− default_do_nmi(regs);
763+ default_do_nmi(regs);
766764
767765 nmi_exit();
768766 }
arch/x86_64/kernel/nmi.c+19−7
@@ -682,16 +682,18 @@ void touch_nmi_watchdog (void) · 681 unmodified lines
682682 touch_softlockup_watchdog();
683683 }
684684
685−void __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
685+int __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
686686 {
687687 int sum;
688688 int touched = 0;
689689 struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
690690 u64 dummy;
691+ int rc=0;
691692
692693 /* check for other users first */
693694 if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT)
694695 == NOTIFY_STOP) {
696+ rc = 1;
695697 touched = 1;
696698 }
697699
@@ -746,10 +748,18 @@ void __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason) · 48 unmodified lines
746748 }
747749 /* start the cycle over again */
748750 wrmsrl(wd->perfctr_msr, -((u64)cpu_khz * 1000 / nmi_hz));
749− }
751+ rc = 1;
752+ } else if (nmi_watchdog == NMI_IO_APIC) {
753+ /* don't know how to accurately check for this.
754+ * just assume it was a watchdog timer interrupt
755+ * This matches the old behaviour.
756+ */
757+ rc = 1;
758+ } else
759+ printk(KERN_WARNING "Unknown enabled NMI hardware?!\n");
750760 }
751761 done:
752− return;
762+ return rc;
753763 }
754764
755765 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; · 5 unmodified lines
761771
762772 asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code)
763773 {
764− int cpu = safe_smp_processor_id();
765−
766774 nmi_enter();
767775 add_pda(__nmi_count,1);
768− if (!rcu_dereference(nmi_callback)(regs, cpu))
769− default_do_nmi(regs);
776+ default_do_nmi(regs);
770777 nmi_exit();
771778 }
772779
780+int do_nmi_callback(struct pt_regs * regs, int cpu)
781+{
782+ return rcu_dereference(nmi_callback)(regs, cpu);
783+}
784+
773785 void set_nmi_callback(nmi_callback_t callback)
774786 {
775787 vmalloc_sync_all();
arch/x86_64/kernel/traps.c+4−4
@@ -781,12 +781,12 @@ asmlinkage __kprobes void default_do_nmi(struct pt_regs *regs) · 780 unmodified lines
781781 * Ok, so this is none of the documented NMI sources,
782782 * so it must be the NMI watchdog.
783783 */
784− if (nmi_watchdog > 0) {
785− nmi_watchdog_tick(regs,reason);
784+ if (nmi_watchdog_tick(regs,reason))
786785 return;
787− }
786+ if (!do_nmi_callback(regs,cpu))
788787 #endif
789− unknown_nmi_error(reason, regs);
788+ unknown_nmi_error(reason, regs);
789+
790790 return;
791791 }
792792 if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
include/asm-i386/nmi.h+1−1
@@ -37,7 +37,7 @@ extern int reserve_lapic_nmi(void); · 36 unmodified lines
3737 extern void release_lapic_nmi(void);
3838 extern void disable_timer_nmi_watchdog(void);
3939 extern void enable_timer_nmi_watchdog(void);
40−extern void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason);
40+extern int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason);
4141
4242 extern atomic_t nmi_active;
4343 extern unsigned int nmi_watchdog;
include/asm-x86_64/nmi.h+9−1
@@ -26,6 +26,14 @@ void set_nmi_callback(nmi_callback_t callback); · 25 unmodified lines
2626 */
2727 void unset_nmi_callback(void);
2828
29+/**
30+ * do_nmi_callback
31+ *
32+ * Check to see if a callback exists and execute it. Return 1
33+ * if the handler exists and was handled successfully.
34+ */
35+int do_nmi_callback(struct pt_regs *regs, int cpu);
36+
2937 #ifdef CONFIG_PM
3038
3139 /** Replace the PM callback routine for NMI. */
@@ -68,7 +76,7 @@ extern int reserve_lapic_nmi(void); · 36 unmodified lines
6876 extern void release_lapic_nmi(void);
6977 extern void disable_timer_nmi_watchdog(void);
7078 extern void enable_timer_nmi_watchdog(void);
71−extern void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason);
79+extern int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason);
7280
7381 extern void nmi_watchdog_default(void);
7482 extern int setup_nmi_watchdog(char *);