aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDon Zickus <dzickus@redhat.com>2006-09-26 04:52:26 -0400
committerAndi Kleen <andi@basil.nowhere.org>2006-09-26 04:52:26 -0400
commit3adbbcce9a49b900d4cc118cdccfdefa78bf1afb (patch)
tree3163758ed23d973a54b396593957c63470f39a0d
parentb7471c6da94d30d3deadc55986cc38d1ff57f9ca (diff)
[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: Don Zickus <dzickus@redhat.com> Signed-off-by: Andi Kleen <ak@suse.de>
-rw-r--r--arch/i386/kernel/nmi.c16
-rw-r--r--arch/i386/kernel/traps.c24
-rw-r--r--arch/x86_64/kernel/nmi.c26
-rw-r--r--arch/x86_64/kernel/traps.c8
-rw-r--r--include/asm-i386/nmi.h2
-rw-r--r--include/asm-x86_64/nmi.h10
6 files changed, 57 insertions, 29 deletions
diff --git a/arch/i386/kernel/nmi.c b/arch/i386/kernel/nmi.c
index d88004343034..bd96ea4f2942 100644
--- a/arch/i386/kernel/nmi.c
+++ b/arch/i386/kernel/nmi.c
@@ -781,7 +781,7 @@ EXPORT_SYMBOL(touch_nmi_watchdog);
781 781
782extern void die_nmi(struct pt_regs *, const char *msg); 782extern void die_nmi(struct pt_regs *, const char *msg);
783 783
784void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason) 784int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
785{ 785{
786 786
787 /* 787 /*
@@ -794,10 +794,12 @@ void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
794 int cpu = smp_processor_id(); 794 int cpu = smp_processor_id();
795 struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); 795 struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
796 u64 dummy; 796 u64 dummy;
797 int rc=0;
797 798
798 /* check for other users first */ 799 /* check for other users first */
799 if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) 800 if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT)
800 == NOTIFY_STOP) { 801 == NOTIFY_STOP) {
802 rc = 1;
801 touched = 1; 803 touched = 1;
802 } 804 }
803 805
@@ -850,10 +852,18 @@ void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
850 } 852 }
851 /* start the cycle over again */ 853 /* start the cycle over again */
852 write_watchdog_counter(wd->perfctr_msr, NULL); 854 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");
854 } 864 }
855done: 865done:
856 return; 866 return rc;
857} 867}
858 868
859#ifdef CONFIG_SYSCTL 869#ifdef CONFIG_SYSCTL
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
index 3a07b2677e2a..282f0bd40dfd 100644
--- a/arch/i386/kernel/traps.c
+++ b/arch/i386/kernel/traps.c
@@ -706,6 +706,13 @@ void die_nmi (struct pt_regs *regs, const char *msg)
706 do_exit(SIGSEGV); 706 do_exit(SIGSEGV);
707} 707}
708 708
709static int dummy_nmi_callback(struct pt_regs * regs, int cpu)
710{
711 return 0;
712}
713
714static nmi_callback_t nmi_callback = dummy_nmi_callback;
715
709static void default_do_nmi(struct pt_regs * regs) 716static void default_do_nmi(struct pt_regs * regs)
710{ 717{
711 unsigned char reason = 0; 718 unsigned char reason = 0;
@@ -723,12 +730,11 @@ static void default_do_nmi(struct pt_regs * regs)
723 * Ok, so this is none of the documented NMI sources, 730 * Ok, so this is none of the documented NMI sources,
724 * so it must be the NMI watchdog. 731 * so it must be the NMI watchdog.
725 */ 732 */
726 if (nmi_watchdog) { 733 if (nmi_watchdog_tick(regs, reason))
727 nmi_watchdog_tick(regs, reason);
728 return; 734 return;
729 }
730#endif 735#endif
731 unknown_nmi_error(reason, regs); 736 if (!rcu_dereference(nmi_callback)(regs, smp_processor_id()))
737 unknown_nmi_error(reason, regs);
732 return; 738 return;
733 } 739 }
734 if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP) 740 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 reassert_nmi(); 750 reassert_nmi();
745} 751}
746 752
747static int dummy_nmi_callback(struct pt_regs * regs, int cpu)
748{
749 return 0;
750}
751
752static nmi_callback_t nmi_callback = dummy_nmi_callback;
753
754fastcall void do_nmi(struct pt_regs * regs, long error_code) 753fastcall void do_nmi(struct pt_regs * regs, long error_code)
755{ 754{
756 int cpu; 755 int cpu;
@@ -761,8 +760,7 @@ fastcall void do_nmi(struct pt_regs * regs, long error_code)
761 760
762 ++nmi_count(cpu); 761 ++nmi_count(cpu);
763 762
764 if (!rcu_dereference(nmi_callback)(regs, cpu)) 763 default_do_nmi(regs);
765 default_do_nmi(regs);
766 764
767 nmi_exit(); 765 nmi_exit();
768} 766}
diff --git a/arch/x86_64/kernel/nmi.c b/arch/x86_64/kernel/nmi.c
index d42374a952d7..f6b881b23a70 100644
--- a/arch/x86_64/kernel/nmi.c
+++ b/arch/x86_64/kernel/nmi.c
@@ -682,16 +682,18 @@ void touch_nmi_watchdog (void)
682 touch_softlockup_watchdog(); 682 touch_softlockup_watchdog();
683} 683}
684 684
685void __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason) 685int __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
686{ 686{
687 int sum; 687 int sum;
688 int touched = 0; 688 int touched = 0;
689 struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); 689 struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk);
690 u64 dummy; 690 u64 dummy;
691 int rc=0;
691 692
692 /* check for other users first */ 693 /* check for other users first */
693 if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) 694 if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT)
694 == NOTIFY_STOP) { 695 == NOTIFY_STOP) {
696 rc = 1;
695 touched = 1; 697 touched = 1;
696 } 698 }
697 699
@@ -746,10 +748,18 @@ void __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
746 } 748 }
747 /* start the cycle over again */ 749 /* start the cycle over again */
748 wrmsrl(wd->perfctr_msr, -((u64)cpu_khz * 1000 / nmi_hz)); 750 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");
750 } 760 }
751done: 761done:
752 return; 762 return rc;
753} 763}
754 764
755static __kprobes int dummy_nmi_callback(struct pt_regs * regs, int cpu) 765static __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 771
762asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code) 772asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code)
763{ 773{
764 int cpu = safe_smp_processor_id();
765
766 nmi_enter(); 774 nmi_enter();
767 add_pda(__nmi_count,1); 775 add_pda(__nmi_count,1);
768 if (!rcu_dereference(nmi_callback)(regs, cpu)) 776 default_do_nmi(regs);
769 default_do_nmi(regs);
770 nmi_exit(); 777 nmi_exit();
771} 778}
772 779
780int do_nmi_callback(struct pt_regs * regs, int cpu)
781{
782 return rcu_dereference(nmi_callback)(regs, cpu);
783}
784
773void set_nmi_callback(nmi_callback_t callback) 785void set_nmi_callback(nmi_callback_t callback)
774{ 786{
775 vmalloc_sync_all(); 787 vmalloc_sync_all();
diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c
index b1249774d1e8..42bc070fdf11 100644
--- a/arch/x86_64/kernel/traps.c
+++ b/arch/x86_64/kernel/traps.c
@@ -781,12 +781,12 @@ asmlinkage __kprobes void default_do_nmi(struct pt_regs *regs)
781 * Ok, so this is none of the documented NMI sources, 781 * Ok, so this is none of the documented NMI sources,
782 * so it must be the NMI watchdog. 782 * so it must be the NMI watchdog.
783 */ 783 */
784 if (nmi_watchdog > 0) { 784 if (nmi_watchdog_tick(regs,reason))
785 nmi_watchdog_tick(regs,reason);
786 return; 785 return;
787 } 786 if (!do_nmi_callback(regs,cpu))
788#endif 787#endif
789 unknown_nmi_error(reason, regs); 788 unknown_nmi_error(reason, regs);
789
790 return; 790 return;
791 } 791 }
792 if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP) 792 if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
diff --git a/include/asm-i386/nmi.h b/include/asm-i386/nmi.h
index 4cda6801ecb8..da0e0b4e9139 100644
--- a/include/asm-i386/nmi.h
+++ b/include/asm-i386/nmi.h
@@ -37,7 +37,7 @@ extern int reserve_lapic_nmi(void);
37extern void release_lapic_nmi(void); 37extern void release_lapic_nmi(void);
38extern void disable_timer_nmi_watchdog(void); 38extern void disable_timer_nmi_watchdog(void);
39extern void enable_timer_nmi_watchdog(void); 39extern void enable_timer_nmi_watchdog(void);
40extern void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason); 40extern int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason);
41 41
42extern atomic_t nmi_active; 42extern atomic_t nmi_active;
43extern unsigned int nmi_watchdog; 43extern unsigned int nmi_watchdog;
diff --git a/include/asm-x86_64/nmi.h b/include/asm-x86_64/nmi.h
index 5918136fd853..8f02a2a416e6 100644
--- a/include/asm-x86_64/nmi.h
+++ b/include/asm-x86_64/nmi.h
@@ -26,6 +26,14 @@ void set_nmi_callback(nmi_callback_t callback);
26 */ 26 */
27void unset_nmi_callback(void); 27void unset_nmi_callback(void);
28 28
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 */
35int do_nmi_callback(struct pt_regs *regs, int cpu);
36
29#ifdef CONFIG_PM 37#ifdef CONFIG_PM
30 38
31/** Replace the PM callback routine for NMI. */ 39/** Replace the PM callback routine for NMI. */
@@ -68,7 +76,7 @@ extern int reserve_lapic_nmi(void);
68extern void release_lapic_nmi(void); 76extern void release_lapic_nmi(void);
69extern void disable_timer_nmi_watchdog(void); 77extern void disable_timer_nmi_watchdog(void);
70extern void enable_timer_nmi_watchdog(void); 78extern void enable_timer_nmi_watchdog(void);
71extern void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason); 79extern int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason);
72 80
73extern void nmi_watchdog_default(void); 81extern void nmi_watchdog_default(void);
74extern int setup_nmi_watchdog(char *); 82extern int setup_nmi_watchdog(char *);