diff options
| author | Don Zickus <dzickus@redhat.com> | 2006-09-26 04:52:27 -0400 |
|---|---|---|
| committer | Andi Kleen <andi@basil.nowhere.org> | 2006-09-26 04:52:27 -0400 |
| commit | 2fbe7b25c8edaf2d10e6c1a4cc9f8afe714c4764 (patch) | |
| tree | e4012ae3cd4519cba1836668237f077c60fb1086 | |
| parent | 957dc87c1bd849440f0eef27e2ade67387001e13 (diff) | |
[PATCH] i386/x86-64: Remove un/set_nmi_callback and reserve/release_lapic_nmi functions
Removes the un/set_nmi_callback and reserve/release_lapic_nmi functions as
they are no longer needed. The various subsystems are modified to register
with the die_notifier instead.
Also includes compile fixes by Andrew Morton.
Signed-off-by: Don Zickus <dzickus@redhat.com>
Signed-off-by: Andi Kleen <ak@suse.de>
| -rw-r--r-- | arch/i386/kernel/crash.c | 20 | ||||
| -rw-r--r-- | arch/i386/kernel/nmi.c | 85 | ||||
| -rw-r--r-- | arch/i386/kernel/traps.c | 23 | ||||
| -rw-r--r-- | arch/i386/oprofile/nmi_int.c | 47 | ||||
| -rw-r--r-- | arch/i386/oprofile/nmi_timer_int.c | 33 | ||||
| -rw-r--r-- | arch/x86_64/kernel/crash.c | 20 | ||||
| -rw-r--r-- | arch/x86_64/kernel/nmi.c | 102 | ||||
| -rw-r--r-- | include/asm-i386/nmi.h | 21 | ||||
| -rw-r--r-- | include/asm-x86_64/nmi.h | 21 | ||||
| -rw-r--r-- | kernel/sysctl.c | 4 |
10 files changed, 116 insertions, 260 deletions
diff --git a/arch/i386/kernel/crash.c b/arch/i386/kernel/crash.c index 5b96f038367f..736c76d6b31d 100644 --- a/arch/i386/kernel/crash.c +++ b/arch/i386/kernel/crash.c | |||
| @@ -22,6 +22,8 @@ | |||
| 22 | #include <asm/nmi.h> | 22 | #include <asm/nmi.h> |
| 23 | #include <asm/hw_irq.h> | 23 | #include <asm/hw_irq.h> |
| 24 | #include <asm/apic.h> | 24 | #include <asm/apic.h> |
| 25 | #include <asm/kdebug.h> | ||
| 26 | |||
| 25 | #include <mach_ipi.h> | 27 | #include <mach_ipi.h> |
| 26 | 28 | ||
| 27 | 29 | ||
| @@ -93,9 +95,18 @@ static void crash_save_self(struct pt_regs *regs) | |||
| 93 | #if defined(CONFIG_SMP) && defined(CONFIG_X86_LOCAL_APIC) | 95 | #if defined(CONFIG_SMP) && defined(CONFIG_X86_LOCAL_APIC) |
| 94 | static atomic_t waiting_for_crash_ipi; | 96 | static atomic_t waiting_for_crash_ipi; |
| 95 | 97 | ||
| 96 | static int crash_nmi_callback(struct pt_regs *regs, int cpu) | 98 | static int crash_nmi_callback(struct notifier_block *self, |
| 99 | unsigned long val, void *data) | ||
| 97 | { | 100 | { |
| 101 | struct pt_regs *regs; | ||
| 98 | struct pt_regs fixed_regs; | 102 | struct pt_regs fixed_regs; |
| 103 | int cpu; | ||
| 104 | |||
| 105 | if (val != DIE_NMI) | ||
| 106 | return NOTIFY_OK; | ||
| 107 | |||
| 108 | regs = ((struct die_args *)data)->regs; | ||
| 109 | cpu = raw_smp_processor_id(); | ||
| 99 | 110 | ||
| 100 | /* Don't do anything if this handler is invoked on crashing cpu. | 111 | /* Don't do anything if this handler is invoked on crashing cpu. |
| 101 | * Otherwise, system will completely hang. Crashing cpu can get | 112 | * Otherwise, system will completely hang. Crashing cpu can get |
| @@ -125,13 +136,18 @@ static void smp_send_nmi_allbutself(void) | |||
| 125 | send_IPI_allbutself(NMI_VECTOR); | 136 | send_IPI_allbutself(NMI_VECTOR); |
| 126 | } | 137 | } |
| 127 | 138 | ||
| 139 | static struct notifier_block crash_nmi_nb = { | ||
| 140 | .notifier_call = crash_nmi_callback, | ||
| 141 | }; | ||
| 142 | |||
| 128 | static void nmi_shootdown_cpus(void) | 143 | static void nmi_shootdown_cpus(void) |
| 129 | { | 144 | { |
| 130 | unsigned long msecs; | 145 | unsigned long msecs; |
| 131 | 146 | ||
| 132 | atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); | 147 | atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); |
| 133 | /* Would it be better to replace the trap vector here? */ | 148 | /* Would it be better to replace the trap vector here? */ |
| 134 | set_nmi_callback(crash_nmi_callback); | 149 | if (register_die_notifier(&crash_nmi_nb)) |
| 150 | return; /* return what? */ | ||
| 135 | /* Ensure the new callback function is set before sending | 151 | /* Ensure the new callback function is set before sending |
| 136 | * out the NMI | 152 | * out the NMI |
| 137 | */ | 153 | */ |
diff --git a/arch/i386/kernel/nmi.c b/arch/i386/kernel/nmi.c index bd96ea4f2942..acd3fdea2a21 100644 --- a/arch/i386/kernel/nmi.c +++ b/arch/i386/kernel/nmi.c | |||
| @@ -42,20 +42,6 @@ static DEFINE_PER_CPU(unsigned long, evntsel_nmi_owner[3]); | |||
| 42 | */ | 42 | */ |
| 43 | #define NMI_MAX_COUNTER_BITS 66 | 43 | #define NMI_MAX_COUNTER_BITS 66 |
| 44 | 44 | ||
| 45 | /* | ||
| 46 | * lapic_nmi_owner tracks the ownership of the lapic NMI hardware: | ||
| 47 | * - it may be reserved by some other driver, or not | ||
| 48 | * - when not reserved by some other driver, it may be used for | ||
| 49 | * the NMI watchdog, or not | ||
| 50 | * | ||
| 51 | * This is maintained separately from nmi_active because the NMI | ||
| 52 | * watchdog may also be driven from the I/O APIC timer. | ||
| 53 | */ | ||
| 54 | static DEFINE_SPINLOCK(lapic_nmi_owner_lock); | ||
| 55 | static unsigned int lapic_nmi_owner; | ||
| 56 | #define LAPIC_NMI_WATCHDOG (1<<0) | ||
| 57 | #define LAPIC_NMI_RESERVED (1<<1) | ||
| 58 | |||
| 59 | /* nmi_active: | 45 | /* nmi_active: |
| 60 | * >0: the lapic NMI watchdog is active, but can be disabled | 46 | * >0: the lapic NMI watchdog is active, but can be disabled |
| 61 | * <0: the lapic NMI watchdog has not been set up, and cannot | 47 | * <0: the lapic NMI watchdog has not been set up, and cannot |
| @@ -325,33 +311,6 @@ static void enable_lapic_nmi_watchdog(void) | |||
| 325 | touch_nmi_watchdog(); | 311 | touch_nmi_watchdog(); |
| 326 | } | 312 | } |
| 327 | 313 | ||
| 328 | int reserve_lapic_nmi(void) | ||
| 329 | { | ||
| 330 | unsigned int old_owner; | ||
| 331 | |||
| 332 | spin_lock(&lapic_nmi_owner_lock); | ||
| 333 | old_owner = lapic_nmi_owner; | ||
| 334 | lapic_nmi_owner |= LAPIC_NMI_RESERVED; | ||
| 335 | spin_unlock(&lapic_nmi_owner_lock); | ||
| 336 | if (old_owner & LAPIC_NMI_RESERVED) | ||
| 337 | return -EBUSY; | ||
| 338 | if (old_owner & LAPIC_NMI_WATCHDOG) | ||
| 339 | disable_lapic_nmi_watchdog(); | ||
| 340 | return 0; | ||
| 341 | } | ||
| 342 | |||
| 343 | void release_lapic_nmi(void) | ||
| 344 | { | ||
| 345 | unsigned int new_owner; | ||
| 346 | |||
| 347 | spin_lock(&lapic_nmi_owner_lock); | ||
| 348 | new_owner = lapic_nmi_owner & ~LAPIC_NMI_RESERVED; | ||
| 349 | lapic_nmi_owner = new_owner; | ||
| 350 | spin_unlock(&lapic_nmi_owner_lock); | ||
| 351 | if (new_owner & LAPIC_NMI_WATCHDOG) | ||
| 352 | enable_lapic_nmi_watchdog(); | ||
| 353 | } | ||
| 354 | |||
| 355 | void disable_timer_nmi_watchdog(void) | 314 | void disable_timer_nmi_watchdog(void) |
| 356 | { | 315 | { |
| 357 | BUG_ON(nmi_watchdog != NMI_IO_APIC); | 316 | BUG_ON(nmi_watchdog != NMI_IO_APIC); |
| @@ -866,6 +825,15 @@ done: | |||
| 866 | return rc; | 825 | return rc; |
| 867 | } | 826 | } |
| 868 | 827 | ||
| 828 | int do_nmi_callback(struct pt_regs * regs, int cpu) | ||
| 829 | { | ||
| 830 | #ifdef CONFIG_SYSCTL | ||
| 831 | if (unknown_nmi_panic) | ||
| 832 | return unknown_nmi_panic_callback(regs, cpu); | ||
| 833 | #endif | ||
| 834 | return 0; | ||
| 835 | } | ||
| 836 | |||
| 869 | #ifdef CONFIG_SYSCTL | 837 | #ifdef CONFIG_SYSCTL |
| 870 | 838 | ||
| 871 | static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu) | 839 | static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu) |
| @@ -873,37 +841,8 @@ static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu) | |||
| 873 | unsigned char reason = get_nmi_reason(); | 841 | unsigned char reason = get_nmi_reason(); |
| 874 | char buf[64]; | 842 | char buf[64]; |
| 875 | 843 | ||
| 876 | if (!(reason & 0xc0)) { | 844 | sprintf(buf, "NMI received for unknown reason %02x\n", reason); |
| 877 | sprintf(buf, "NMI received for unknown reason %02x\n", reason); | 845 | die_nmi(regs, buf); |
| 878 | die_nmi(regs, buf); | ||
| 879 | } | ||
| 880 | return 0; | ||
| 881 | } | ||
| 882 | |||
| 883 | /* | ||
| 884 | * proc handler for /proc/sys/kernel/unknown_nmi_panic | ||
| 885 | */ | ||
| 886 | int proc_unknown_nmi_panic(ctl_table *table, int write, struct file *file, | ||
| 887 | void __user *buffer, size_t *length, loff_t *ppos) | ||
| 888 | { | ||
| 889 | int old_state; | ||
| 890 | |||
| 891 | old_state = unknown_nmi_panic; | ||
| 892 | proc_dointvec(table, write, file, buffer, length, ppos); | ||
| 893 | if (!!old_state == !!unknown_nmi_panic) | ||
| 894 | return 0; | ||
| 895 | |||
| 896 | if (unknown_nmi_panic) { | ||
| 897 | if (reserve_lapic_nmi() < 0) { | ||
| 898 | unknown_nmi_panic = 0; | ||
| 899 | return -EBUSY; | ||
| 900 | } else { | ||
| 901 | set_nmi_callback(unknown_nmi_panic_callback); | ||
| 902 | } | ||
| 903 | } else { | ||
| 904 | release_lapic_nmi(); | ||
| 905 | unset_nmi_callback(); | ||
| 906 | } | ||
| 907 | return 0; | 846 | return 0; |
| 908 | } | 847 | } |
| 909 | 848 | ||
| @@ -917,7 +856,5 @@ EXPORT_SYMBOL(reserve_perfctr_nmi); | |||
| 917 | EXPORT_SYMBOL(release_perfctr_nmi); | 856 | EXPORT_SYMBOL(release_perfctr_nmi); |
| 918 | EXPORT_SYMBOL(reserve_evntsel_nmi); | 857 | EXPORT_SYMBOL(reserve_evntsel_nmi); |
| 919 | EXPORT_SYMBOL(release_evntsel_nmi); | 858 | EXPORT_SYMBOL(release_evntsel_nmi); |
| 920 | EXPORT_SYMBOL(reserve_lapic_nmi); | ||
| 921 | EXPORT_SYMBOL(release_lapic_nmi); | ||
| 922 | EXPORT_SYMBOL(disable_timer_nmi_watchdog); | 859 | EXPORT_SYMBOL(disable_timer_nmi_watchdog); |
| 923 | EXPORT_SYMBOL(enable_timer_nmi_watchdog); | 860 | EXPORT_SYMBOL(enable_timer_nmi_watchdog); |
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 282f0bd40dfd..7db664d0b25c 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c | |||
| @@ -706,13 +706,6 @@ 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 | |||
| 716 | static void default_do_nmi(struct pt_regs * regs) | 709 | static void default_do_nmi(struct pt_regs * regs) |
| 717 | { | 710 | { |
| 718 | unsigned char reason = 0; | 711 | unsigned char reason = 0; |
| @@ -732,9 +725,10 @@ static void default_do_nmi(struct pt_regs * regs) | |||
| 732 | */ | 725 | */ |
| 733 | if (nmi_watchdog_tick(regs, reason)) | 726 | if (nmi_watchdog_tick(regs, reason)) |
| 734 | return; | 727 | return; |
| 728 | if (!do_nmi_callback(regs, smp_processor_id())) | ||
| 735 | #endif | 729 | #endif |
| 736 | if (!rcu_dereference(nmi_callback)(regs, smp_processor_id())) | ||
| 737 | unknown_nmi_error(reason, regs); | 730 | unknown_nmi_error(reason, regs); |
| 731 | |||
| 738 | return; | 732 | return; |
| 739 | } | 733 | } |
| 740 | if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP) | 734 | if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP) |
| @@ -765,19 +759,6 @@ fastcall void do_nmi(struct pt_regs * regs, long error_code) | |||
| 765 | nmi_exit(); | 759 | nmi_exit(); |
| 766 | } | 760 | } |
| 767 | 761 | ||
| 768 | void set_nmi_callback(nmi_callback_t callback) | ||
| 769 | { | ||
| 770 | vmalloc_sync_all(); | ||
| 771 | rcu_assign_pointer(nmi_callback, callback); | ||
| 772 | } | ||
| 773 | EXPORT_SYMBOL_GPL(set_nmi_callback); | ||
| 774 | |||
| 775 | void unset_nmi_callback(void) | ||
| 776 | { | ||
| 777 | nmi_callback = dummy_nmi_callback; | ||
| 778 | } | ||
| 779 | EXPORT_SYMBOL_GPL(unset_nmi_callback); | ||
| 780 | |||
| 781 | #ifdef CONFIG_KPROBES | 762 | #ifdef CONFIG_KPROBES |
| 782 | fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code) | 763 | fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code) |
| 783 | { | 764 | { |
diff --git a/arch/i386/oprofile/nmi_int.c b/arch/i386/oprofile/nmi_int.c index 8710ca081b1e..b3610188bcf0 100644 --- a/arch/i386/oprofile/nmi_int.c +++ b/arch/i386/oprofile/nmi_int.c | |||
| @@ -17,14 +17,15 @@ | |||
| 17 | #include <asm/nmi.h> | 17 | #include <asm/nmi.h> |
| 18 | #include <asm/msr.h> | 18 | #include <asm/msr.h> |
| 19 | #include <asm/apic.h> | 19 | #include <asm/apic.h> |
| 20 | #include <asm/kdebug.h> | ||
| 20 | 21 | ||
| 21 | #include "op_counter.h" | 22 | #include "op_counter.h" |
| 22 | #include "op_x86_model.h" | 23 | #include "op_x86_model.h" |
| 23 | 24 | ||
| 24 | static struct op_x86_model_spec const * model; | 25 | static struct op_x86_model_spec const * model; |
| 25 | static struct op_msrs cpu_msrs[NR_CPUS]; | 26 | static struct op_msrs cpu_msrs[NR_CPUS]; |
| 26 | static unsigned long saved_lvtpc[NR_CPUS]; | 27 | static unsigned long saved_lvtpc[NR_CPUS]; |
| 27 | 28 | ||
| 28 | static int nmi_start(void); | 29 | static int nmi_start(void); |
| 29 | static void nmi_stop(void); | 30 | static void nmi_stop(void); |
| 30 | 31 | ||
| @@ -82,13 +83,24 @@ static void exit_driverfs(void) | |||
| 82 | #define exit_driverfs() do { } while (0) | 83 | #define exit_driverfs() do { } while (0) |
| 83 | #endif /* CONFIG_PM */ | 84 | #endif /* CONFIG_PM */ |
| 84 | 85 | ||
| 85 | 86 | int profile_exceptions_notify(struct notifier_block *self, | |
| 86 | static int nmi_callback(struct pt_regs * regs, int cpu) | 87 | unsigned long val, void *data) |
| 87 | { | 88 | { |
| 88 | return model->check_ctrs(regs, &cpu_msrs[cpu]); | 89 | struct die_args *args = (struct die_args *)data; |
| 90 | int ret = NOTIFY_DONE; | ||
| 91 | int cpu = smp_processor_id(); | ||
| 92 | |||
| 93 | switch(val) { | ||
| 94 | case DIE_NMI: | ||
| 95 | if (model->check_ctrs(args->regs, &cpu_msrs[cpu])) | ||
| 96 | ret = NOTIFY_STOP; | ||
| 97 | break; | ||
| 98 | default: | ||
| 99 | break; | ||
| 100 | } | ||
| 101 | return ret; | ||
| 89 | } | 102 | } |
| 90 | 103 | ||
| 91 | |||
| 92 | static void nmi_cpu_save_registers(struct op_msrs * msrs) | 104 | static void nmi_cpu_save_registers(struct op_msrs * msrs) |
| 93 | { | 105 | { |
| 94 | unsigned int const nr_ctrs = model->num_counters; | 106 | unsigned int const nr_ctrs = model->num_counters; |
| @@ -174,27 +186,29 @@ static void nmi_cpu_setup(void * dummy) | |||
| 174 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 186 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
| 175 | } | 187 | } |
| 176 | 188 | ||
| 189 | static struct notifier_block profile_exceptions_nb = { | ||
| 190 | .notifier_call = profile_exceptions_notify, | ||
| 191 | .next = NULL, | ||
| 192 | .priority = 0 | ||
| 193 | }; | ||
| 177 | 194 | ||
| 178 | static int nmi_setup(void) | 195 | static int nmi_setup(void) |
| 179 | { | 196 | { |
| 197 | int err=0; | ||
| 198 | |||
| 180 | if (!allocate_msrs()) | 199 | if (!allocate_msrs()) |
| 181 | return -ENOMEM; | 200 | return -ENOMEM; |
| 182 | 201 | ||
| 183 | /* We walk a thin line between law and rape here. | 202 | if ((err = register_die_notifier(&profile_exceptions_nb))){ |
| 184 | * We need to be careful to install our NMI handler | ||
| 185 | * without actually triggering any NMIs as this will | ||
| 186 | * break the core code horrifically. | ||
| 187 | */ | ||
| 188 | if (reserve_lapic_nmi() < 0) { | ||
| 189 | free_msrs(); | 203 | free_msrs(); |
| 190 | return -EBUSY; | 204 | return err; |
| 191 | } | 205 | } |
| 206 | |||
| 192 | /* We need to serialize save and setup for HT because the subset | 207 | /* We need to serialize save and setup for HT because the subset |
| 193 | * of msrs are distinct for save and setup operations | 208 | * of msrs are distinct for save and setup operations |
| 194 | */ | 209 | */ |
| 195 | on_each_cpu(nmi_save_registers, NULL, 0, 1); | 210 | on_each_cpu(nmi_save_registers, NULL, 0, 1); |
| 196 | on_each_cpu(nmi_cpu_setup, NULL, 0, 1); | 211 | on_each_cpu(nmi_cpu_setup, NULL, 0, 1); |
| 197 | set_nmi_callback(nmi_callback); | ||
| 198 | nmi_enabled = 1; | 212 | nmi_enabled = 1; |
| 199 | return 0; | 213 | return 0; |
| 200 | } | 214 | } |
| @@ -250,8 +264,7 @@ static void nmi_shutdown(void) | |||
| 250 | { | 264 | { |
| 251 | nmi_enabled = 0; | 265 | nmi_enabled = 0; |
| 252 | on_each_cpu(nmi_cpu_shutdown, NULL, 0, 1); | 266 | on_each_cpu(nmi_cpu_shutdown, NULL, 0, 1); |
| 253 | unset_nmi_callback(); | 267 | unregister_die_notifier(&profile_exceptions_nb); |
| 254 | release_lapic_nmi(); | ||
| 255 | free_msrs(); | 268 | free_msrs(); |
| 256 | } | 269 | } |
| 257 | 270 | ||
diff --git a/arch/i386/oprofile/nmi_timer_int.c b/arch/i386/oprofile/nmi_timer_int.c index a33a73bb502d..93ca48f804ac 100644 --- a/arch/i386/oprofile/nmi_timer_int.c +++ b/arch/i386/oprofile/nmi_timer_int.c | |||
| @@ -17,32 +17,49 @@ | |||
| 17 | #include <asm/nmi.h> | 17 | #include <asm/nmi.h> |
| 18 | #include <asm/apic.h> | 18 | #include <asm/apic.h> |
| 19 | #include <asm/ptrace.h> | 19 | #include <asm/ptrace.h> |
| 20 | #include <asm/kdebug.h> | ||
| 20 | 21 | ||
| 21 | static int nmi_timer_callback(struct pt_regs * regs, int cpu) | 22 | int profile_timer_exceptions_notify(struct notifier_block *self, |
| 23 | unsigned long val, void *data) | ||
| 22 | { | 24 | { |
| 23 | oprofile_add_sample(regs, 0); | 25 | struct die_args *args = (struct die_args *)data; |
| 24 | return 1; | 26 | int ret = NOTIFY_DONE; |
| 27 | |||
| 28 | switch(val) { | ||
| 29 | case DIE_NMI: | ||
| 30 | oprofile_add_sample(args->regs, 0); | ||
| 31 | ret = NOTIFY_STOP; | ||
| 32 | break; | ||
| 33 | default: | ||
| 34 | break; | ||
| 35 | } | ||
| 36 | return ret; | ||
| 25 | } | 37 | } |
| 26 | 38 | ||
| 39 | static struct notifier_block profile_timer_exceptions_nb = { | ||
| 40 | .notifier_call = profile_timer_exceptions_notify, | ||
| 41 | .next = NULL, | ||
| 42 | .priority = 0 | ||
| 43 | }; | ||
| 44 | |||
| 27 | static int timer_start(void) | 45 | static int timer_start(void) |
| 28 | { | 46 | { |
| 29 | disable_timer_nmi_watchdog(); | 47 | if (register_die_notifier(&profile_timer_exceptions_nb)) |
| 30 | set_nmi_callback(nmi_timer_callback); | 48 | return 1; |
| 31 | return 0; | 49 | return 0; |
| 32 | } | 50 | } |
| 33 | 51 | ||
| 34 | 52 | ||
| 35 | static void timer_stop(void) | 53 | static void timer_stop(void) |
| 36 | { | 54 | { |
| 37 | enable_timer_nmi_watchdog(); | 55 | unregister_die_notifier(&profile_timer_exceptions_nb); |
| 38 | unset_nmi_callback(); | ||
| 39 | synchronize_sched(); /* Allow already-started NMIs to complete. */ | 56 | synchronize_sched(); /* Allow already-started NMIs to complete. */ |
| 40 | } | 57 | } |
| 41 | 58 | ||
| 42 | 59 | ||
| 43 | int __init op_nmi_timer_init(struct oprofile_operations * ops) | 60 | int __init op_nmi_timer_init(struct oprofile_operations * ops) |
| 44 | { | 61 | { |
| 45 | if (atomic_read(&nmi_active) <= 0) | 62 | if ((nmi_watchdog != NMI_IO_APIC) || (atomic_read(&nmi_active) <= 0)) |
| 46 | return -ENODEV; | 63 | return -ENODEV; |
| 47 | 64 | ||
| 48 | ops->start = timer_start; | 65 | ops->start = timer_start; |
diff --git a/arch/x86_64/kernel/crash.c b/arch/x86_64/kernel/crash.c index d8d5750d6106..44c8af65325e 100644 --- a/arch/x86_64/kernel/crash.c +++ b/arch/x86_64/kernel/crash.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <asm/nmi.h> | 23 | #include <asm/nmi.h> |
| 24 | #include <asm/hw_irq.h> | 24 | #include <asm/hw_irq.h> |
| 25 | #include <asm/mach_apic.h> | 25 | #include <asm/mach_apic.h> |
| 26 | #include <asm/kdebug.h> | ||
| 26 | 27 | ||
| 27 | /* This keeps a track of which one is crashing cpu. */ | 28 | /* This keeps a track of which one is crashing cpu. */ |
| 28 | static int crashing_cpu; | 29 | static int crashing_cpu; |
| @@ -95,8 +96,18 @@ static void crash_save_self(struct pt_regs *regs) | |||
| 95 | #ifdef CONFIG_SMP | 96 | #ifdef CONFIG_SMP |
| 96 | static atomic_t waiting_for_crash_ipi; | 97 | static atomic_t waiting_for_crash_ipi; |
| 97 | 98 | ||
| 98 | static int crash_nmi_callback(struct pt_regs *regs, int cpu) | 99 | static int crash_nmi_callback(struct notifier_block *self, |
| 100 | unsigned long val, void *data) | ||
| 99 | { | 101 | { |
| 102 | struct pt_regs *regs; | ||
| 103 | int cpu; | ||
| 104 | |||
| 105 | if (val != DIE_NMI) | ||
| 106 | return NOTIFY_OK; | ||
| 107 | |||
| 108 | regs = ((struct die_args *)data)->regs; | ||
| 109 | cpu = raw_smp_processor_id(); | ||
| 110 | |||
| 100 | /* | 111 | /* |
| 101 | * Don't do anything if this handler is invoked on crashing cpu. | 112 | * Don't do anything if this handler is invoked on crashing cpu. |
| 102 | * Otherwise, system will completely hang. Crashing cpu can get | 113 | * Otherwise, system will completely hang. Crashing cpu can get |
| @@ -127,12 +138,17 @@ static void smp_send_nmi_allbutself(void) | |||
| 127 | * cpu hotplug shouldn't matter. | 138 | * cpu hotplug shouldn't matter. |
| 128 | */ | 139 | */ |
| 129 | 140 | ||
| 141 | static struct notifier_block crash_nmi_nb = { | ||
| 142 | .notifier_call = crash_nmi_callback, | ||
| 143 | }; | ||
| 144 | |||
| 130 | static void nmi_shootdown_cpus(void) | 145 | static void nmi_shootdown_cpus(void) |
| 131 | { | 146 | { |
| 132 | unsigned long msecs; | 147 | unsigned long msecs; |
| 133 | 148 | ||
| 134 | atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); | 149 | atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); |
| 135 | set_nmi_callback(crash_nmi_callback); | 150 | if (register_die_notifier(&crash_nmi_nb)) |
| 151 | return; /* return what? */ | ||
| 136 | 152 | ||
| 137 | /* | 153 | /* |
| 138 | * Ensure the new callback function is set before sending | 154 | * Ensure the new callback function is set before sending |
diff --git a/arch/x86_64/kernel/nmi.c b/arch/x86_64/kernel/nmi.c index f6b881b23a70..9d175dcf3a2d 100644 --- a/arch/x86_64/kernel/nmi.c +++ b/arch/x86_64/kernel/nmi.c | |||
| @@ -41,20 +41,6 @@ static DEFINE_PER_CPU(unsigned, evntsel_nmi_owner[2]); | |||
| 41 | */ | 41 | */ |
| 42 | #define NMI_MAX_COUNTER_BITS 66 | 42 | #define NMI_MAX_COUNTER_BITS 66 |
| 43 | 43 | ||
| 44 | /* | ||
| 45 | * lapic_nmi_owner tracks the ownership of the lapic NMI hardware: | ||
| 46 | * - it may be reserved by some other driver, or not | ||
| 47 | * - when not reserved by some other driver, it may be used for | ||
| 48 | * the NMI watchdog, or not | ||
| 49 | * | ||
| 50 | * This is maintained separately from nmi_active because the NMI | ||
| 51 | * watchdog may also be driven from the I/O APIC timer. | ||
| 52 | */ | ||
| 53 | static DEFINE_SPINLOCK(lapic_nmi_owner_lock); | ||
| 54 | static unsigned int lapic_nmi_owner; | ||
| 55 | #define LAPIC_NMI_WATCHDOG (1<<0) | ||
| 56 | #define LAPIC_NMI_RESERVED (1<<1) | ||
| 57 | |||
| 58 | /* nmi_active: | 44 | /* nmi_active: |
| 59 | * >0: the lapic NMI watchdog is active, but can be disabled | 45 | * >0: the lapic NMI watchdog is active, but can be disabled |
| 60 | * <0: the lapic NMI watchdog has not been set up, and cannot | 46 | * <0: the lapic NMI watchdog has not been set up, and cannot |
| @@ -321,33 +307,6 @@ static void enable_lapic_nmi_watchdog(void) | |||
| 321 | touch_nmi_watchdog(); | 307 | touch_nmi_watchdog(); |
| 322 | } | 308 | } |
| 323 | 309 | ||
| 324 | int reserve_lapic_nmi(void) | ||
| 325 | { | ||
| 326 | unsigned int old_owner; | ||
| 327 | |||
| 328 | spin_lock(&lapic_nmi_owner_lock); | ||
| 329 | old_owner = lapic_nmi_owner; | ||
| 330 | lapic_nmi_owner |= LAPIC_NMI_RESERVED; | ||
| 331 | spin_unlock(&lapic_nmi_owner_lock); | ||
| 332 | if (old_owner & LAPIC_NMI_RESERVED) | ||
| 333 | return -EBUSY; | ||
| 334 | if (old_owner & LAPIC_NMI_WATCHDOG) | ||
| 335 | disable_lapic_nmi_watchdog(); | ||
| 336 | return 0; | ||
| 337 | } | ||
| 338 | |||
| 339 | void release_lapic_nmi(void) | ||
| 340 | { | ||
| 341 | unsigned int new_owner; | ||
| 342 | |||
| 343 | spin_lock(&lapic_nmi_owner_lock); | ||
| 344 | new_owner = lapic_nmi_owner & ~LAPIC_NMI_RESERVED; | ||
| 345 | lapic_nmi_owner = new_owner; | ||
| 346 | spin_unlock(&lapic_nmi_owner_lock); | ||
| 347 | if (new_owner & LAPIC_NMI_WATCHDOG) | ||
| 348 | enable_lapic_nmi_watchdog(); | ||
| 349 | } | ||
| 350 | |||
| 351 | void disable_timer_nmi_watchdog(void) | 310 | void disable_timer_nmi_watchdog(void) |
| 352 | { | 311 | { |
| 353 | BUG_ON(nmi_watchdog != NMI_IO_APIC); | 312 | BUG_ON(nmi_watchdog != NMI_IO_APIC); |
| @@ -762,13 +721,6 @@ done: | |||
| 762 | return rc; | 721 | return rc; |
| 763 | } | 722 | } |
| 764 | 723 | ||
| 765 | static __kprobes int dummy_nmi_callback(struct pt_regs * regs, int cpu) | ||
| 766 | { | ||
| 767 | return 0; | ||
| 768 | } | ||
| 769 | |||
| 770 | static nmi_callback_t nmi_callback = dummy_nmi_callback; | ||
| 771 | |||
| 772 | asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code) | 724 | asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code) |
| 773 | { | 725 | { |
| 774 | nmi_enter(); | 726 | nmi_enter(); |
| @@ -779,21 +731,12 @@ asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code) | |||
| 779 | 731 | ||
| 780 | int do_nmi_callback(struct pt_regs * regs, int cpu) | 732 | int do_nmi_callback(struct pt_regs * regs, int cpu) |
| 781 | { | 733 | { |
| 782 | return rcu_dereference(nmi_callback)(regs, cpu); | 734 | #ifdef CONFIG_SYSCTL |
| 783 | } | 735 | if (unknown_nmi_panic) |
| 784 | 736 | return unknown_nmi_panic_callback(regs, cpu); | |
| 785 | void set_nmi_callback(nmi_callback_t callback) | 737 | #endif |
| 786 | { | 738 | return 0; |
| 787 | vmalloc_sync_all(); | ||
| 788 | rcu_assign_pointer(nmi_callback, callback); | ||
| 789 | } | ||
| 790 | EXPORT_SYMBOL_GPL(set_nmi_callback); | ||
| 791 | |||
| 792 | void unset_nmi_callback(void) | ||
| 793 | { | ||
| 794 | nmi_callback = dummy_nmi_callback; | ||
| 795 | } | 739 | } |
| 796 | EXPORT_SYMBOL_GPL(unset_nmi_callback); | ||
| 797 | 740 | ||
| 798 | #ifdef CONFIG_SYSCTL | 741 | #ifdef CONFIG_SYSCTL |
| 799 | 742 | ||
| @@ -802,37 +745,8 @@ static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu) | |||
| 802 | unsigned char reason = get_nmi_reason(); | 745 | unsigned char reason = get_nmi_reason(); |
| 803 | char buf[64]; | 746 | char buf[64]; |
| 804 | 747 | ||
| 805 | if (!(reason & 0xc0)) { | 748 | sprintf(buf, "NMI received for unknown reason %02x\n", reason); |
| 806 | sprintf(buf, "NMI received for unknown reason %02x\n", reason); | 749 | die_nmi(buf,regs); |
| 807 | die_nmi(buf,regs); | ||
| 808 | } | ||
| 809 | return 0; | ||
| 810 | } | ||
| 811 | |||
| 812 | /* | ||
| 813 | * proc handler for /proc/sys/kernel/unknown_nmi_panic | ||
| 814 | */ | ||
| 815 | int proc_unknown_nmi_panic(struct ctl_table *table, int write, struct file *file, | ||
| 816 | void __user *buffer, size_t *length, loff_t *ppos) | ||
| 817 | { | ||
| 818 | int old_state; | ||
| 819 | |||
| 820 | old_state = unknown_nmi_panic; | ||
| 821 | proc_dointvec(table, write, file, buffer, length, ppos); | ||
| 822 | if (!!old_state == !!unknown_nmi_panic) | ||
| 823 | return 0; | ||
| 824 | |||
| 825 | if (unknown_nmi_panic) { | ||
| 826 | if (reserve_lapic_nmi() < 0) { | ||
| 827 | unknown_nmi_panic = 0; | ||
| 828 | return -EBUSY; | ||
| 829 | } else { | ||
| 830 | set_nmi_callback(unknown_nmi_panic_callback); | ||
| 831 | } | ||
| 832 | } else { | ||
| 833 | release_lapic_nmi(); | ||
| 834 | unset_nmi_callback(); | ||
| 835 | } | ||
| 836 | return 0; | 750 | return 0; |
| 837 | } | 751 | } |
| 838 | 752 | ||
| @@ -846,8 +760,6 @@ EXPORT_SYMBOL(reserve_perfctr_nmi); | |||
| 846 | EXPORT_SYMBOL(release_perfctr_nmi); | 760 | EXPORT_SYMBOL(release_perfctr_nmi); |
| 847 | EXPORT_SYMBOL(reserve_evntsel_nmi); | 761 | EXPORT_SYMBOL(reserve_evntsel_nmi); |
| 848 | EXPORT_SYMBOL(release_evntsel_nmi); | 762 | EXPORT_SYMBOL(release_evntsel_nmi); |
| 849 | EXPORT_SYMBOL(reserve_lapic_nmi); | ||
| 850 | EXPORT_SYMBOL(release_lapic_nmi); | ||
| 851 | EXPORT_SYMBOL(disable_timer_nmi_watchdog); | 763 | EXPORT_SYMBOL(disable_timer_nmi_watchdog); |
| 852 | EXPORT_SYMBOL(enable_timer_nmi_watchdog); | 764 | EXPORT_SYMBOL(enable_timer_nmi_watchdog); |
| 853 | EXPORT_SYMBOL(touch_nmi_watchdog); | 765 | EXPORT_SYMBOL(touch_nmi_watchdog); |
diff --git a/include/asm-i386/nmi.h b/include/asm-i386/nmi.h index da0e0b4e9139..34d6bf063b6e 100644 --- a/include/asm-i386/nmi.h +++ b/include/asm-i386/nmi.h | |||
| @@ -6,24 +6,13 @@ | |||
| 6 | 6 | ||
| 7 | #include <linux/pm.h> | 7 | #include <linux/pm.h> |
| 8 | 8 | ||
| 9 | struct pt_regs; | ||
| 10 | |||
| 11 | typedef int (*nmi_callback_t)(struct pt_regs * regs, int cpu); | ||
| 12 | |||
| 13 | /** | ||
| 14 | * set_nmi_callback | ||
| 15 | * | ||
| 16 | * Set a handler for an NMI. Only one handler may be | ||
| 17 | * set. Return 1 if the NMI was handled. | ||
| 18 | */ | ||
| 19 | void set_nmi_callback(nmi_callback_t callback); | ||
| 20 | |||
| 21 | /** | 9 | /** |
| 22 | * unset_nmi_callback | 10 | * do_nmi_callback |
| 23 | * | 11 | * |
| 24 | * Remove the handler previously set. | 12 | * Check to see if a callback exists and execute it. Return 1 |
| 13 | * if the handler exists and was handled successfully. | ||
| 25 | */ | 14 | */ |
| 26 | void unset_nmi_callback(void); | 15 | int do_nmi_callback(struct pt_regs *regs, int cpu); |
| 27 | 16 | ||
| 28 | extern int avail_to_resrv_perfctr_nmi_bit(unsigned int); | 17 | extern int avail_to_resrv_perfctr_nmi_bit(unsigned int); |
| 29 | extern int avail_to_resrv_perfctr_nmi(unsigned int); | 18 | extern int avail_to_resrv_perfctr_nmi(unsigned int); |
| @@ -33,8 +22,6 @@ extern int reserve_evntsel_nmi(unsigned int); | |||
| 33 | extern void release_evntsel_nmi(unsigned int); | 22 | extern void release_evntsel_nmi(unsigned int); |
| 34 | 23 | ||
| 35 | extern void setup_apic_nmi_watchdog (void *); | 24 | extern void setup_apic_nmi_watchdog (void *); |
| 36 | extern int reserve_lapic_nmi(void); | ||
| 37 | extern void release_lapic_nmi(void); | ||
| 38 | extern void disable_timer_nmi_watchdog(void); | 25 | extern void disable_timer_nmi_watchdog(void); |
| 39 | extern void enable_timer_nmi_watchdog(void); | 26 | extern void enable_timer_nmi_watchdog(void); |
| 40 | extern int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason); | 27 | extern int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason); |
diff --git a/include/asm-x86_64/nmi.h b/include/asm-x86_64/nmi.h index 8f02a2a416e6..8818c39d34e0 100644 --- a/include/asm-x86_64/nmi.h +++ b/include/asm-x86_64/nmi.h | |||
| @@ -7,25 +7,6 @@ | |||
| 7 | #include <linux/pm.h> | 7 | #include <linux/pm.h> |
| 8 | #include <asm/io.h> | 8 | #include <asm/io.h> |
| 9 | 9 | ||
| 10 | struct pt_regs; | ||
| 11 | |||
| 12 | typedef int (*nmi_callback_t)(struct pt_regs * regs, int cpu); | ||
| 13 | |||
| 14 | /** | ||
| 15 | * set_nmi_callback | ||
| 16 | * | ||
| 17 | * Set a handler for an NMI. Only one handler may be | ||
| 18 | * set. Return 1 if the NMI was handled. | ||
| 19 | */ | ||
| 20 | void set_nmi_callback(nmi_callback_t callback); | ||
| 21 | |||
| 22 | /** | ||
| 23 | * unset_nmi_callback | ||
| 24 | * | ||
| 25 | * Remove the handler previously set. | ||
| 26 | */ | ||
| 27 | void unset_nmi_callback(void); | ||
| 28 | |||
| 29 | /** | 10 | /** |
| 30 | * do_nmi_callback | 11 | * do_nmi_callback |
| 31 | * | 12 | * |
| @@ -72,8 +53,6 @@ extern int reserve_evntsel_nmi(unsigned int); | |||
| 72 | extern void release_evntsel_nmi(unsigned int); | 53 | extern void release_evntsel_nmi(unsigned int); |
| 73 | 54 | ||
| 74 | extern void setup_apic_nmi_watchdog (void *); | 55 | extern void setup_apic_nmi_watchdog (void *); |
| 75 | extern int reserve_lapic_nmi(void); | ||
| 76 | extern void release_lapic_nmi(void); | ||
| 77 | extern void disable_timer_nmi_watchdog(void); | 56 | extern void disable_timer_nmi_watchdog(void); |
| 78 | extern void enable_timer_nmi_watchdog(void); | 57 | extern void enable_timer_nmi_watchdog(void); |
| 79 | extern int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason); | 58 | extern int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason); |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 362a0cc37138..83f168361624 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
| @@ -76,8 +76,6 @@ extern int compat_log; | |||
| 76 | 76 | ||
| 77 | #if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86) | 77 | #if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86) |
| 78 | int unknown_nmi_panic; | 78 | int unknown_nmi_panic; |
| 79 | extern int proc_unknown_nmi_panic(ctl_table *, int, struct file *, | ||
| 80 | void __user *, size_t *, loff_t *); | ||
| 81 | #endif | 79 | #endif |
| 82 | 80 | ||
| 83 | /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */ | 81 | /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */ |
| @@ -628,7 +626,7 @@ static ctl_table kern_table[] = { | |||
| 628 | .data = &unknown_nmi_panic, | 626 | .data = &unknown_nmi_panic, |
| 629 | .maxlen = sizeof (int), | 627 | .maxlen = sizeof (int), |
| 630 | .mode = 0644, | 628 | .mode = 0644, |
| 631 | .proc_handler = &proc_unknown_nmi_panic, | 629 | .proc_handler = &proc_dointvec, |
| 632 | }, | 630 | }, |
| 633 | #endif | 631 | #endif |
| 634 | #if defined(CONFIG_X86) | 632 | #if defined(CONFIG_X86) |
