diff options
-rw-r--r-- | arch/i386/kernel/nmi.c | 16 | ||||
-rw-r--r-- | arch/i386/kernel/traps.c | 24 | ||||
-rw-r--r-- | arch/x86_64/kernel/nmi.c | 26 | ||||
-rw-r--r-- | arch/x86_64/kernel/traps.c | 8 | ||||
-rw-r--r-- | include/asm-i386/nmi.h | 2 | ||||
-rw-r--r-- | include/asm-x86_64/nmi.h | 10 |
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 | ||
782 | extern void die_nmi(struct pt_regs *, const char *msg); | 782 | extern void die_nmi(struct pt_regs *, const char *msg); |
783 | 783 | ||
784 | void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason) | 784 | int 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 | } |
855 | done: | 865 | done: |
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 | ||
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 | |||
709 | static void default_do_nmi(struct pt_regs * regs) | 716 | static 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 | ||
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 | |||
754 | fastcall void do_nmi(struct pt_regs * regs, long error_code) | 753 | fastcall 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 | ||
685 | void __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason) | 685 | int __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 | } |
751 | done: | 761 | done: |
752 | return; | 762 | return rc; |
753 | } | 763 | } |
754 | 764 | ||
755 | static __kprobes int dummy_nmi_callback(struct pt_regs * regs, int cpu) | 765 | 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 | 771 | ||
762 | asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code) | 772 | asmlinkage __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 | ||
780 | int do_nmi_callback(struct pt_regs * regs, int cpu) | ||
781 | { | ||
782 | return rcu_dereference(nmi_callback)(regs, cpu); | ||
783 | } | ||
784 | |||
773 | void set_nmi_callback(nmi_callback_t callback) | 785 | void 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); | |||
37 | extern void release_lapic_nmi(void); | 37 | extern void release_lapic_nmi(void); |
38 | extern void disable_timer_nmi_watchdog(void); | 38 | extern void disable_timer_nmi_watchdog(void); |
39 | extern void enable_timer_nmi_watchdog(void); | 39 | 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); |
41 | 41 | ||
42 | extern atomic_t nmi_active; | 42 | extern atomic_t nmi_active; |
43 | extern unsigned int nmi_watchdog; | 43 | extern 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 | */ |
27 | void unset_nmi_callback(void); | 27 | void 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 | */ | ||
35 | int 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); | |||
68 | extern void release_lapic_nmi(void); | 76 | extern void release_lapic_nmi(void); |
69 | extern void disable_timer_nmi_watchdog(void); | 77 | extern void disable_timer_nmi_watchdog(void); |
70 | extern void enable_timer_nmi_watchdog(void); | 78 | 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); |
72 | 80 | ||
73 | extern void nmi_watchdog_default(void); | 81 | extern void nmi_watchdog_default(void); |
74 | extern int setup_nmi_watchdog(char *); | 82 | extern int setup_nmi_watchdog(char *); |