diff options
Diffstat (limited to 'arch/i386')
-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 |
5 files changed, 86 insertions, 122 deletions
diff --git a/arch/i386/kernel/crash.c b/arch/i386/kernel/crash.c index 5b96f038367..736c76d6b31 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 bd96ea4f294..acd3fdea2a2 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 282f0bd40df..7db664d0b25 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 8710ca081b1..b3610188bcf 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 a33a73bb502..93ca48f804a 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; |