diff options
-rw-r--r-- | arch/x86/include/asm/entry_arch.h | 4 | ||||
-rw-r--r-- | arch/x86/include/asm/hw_irq.h | 1 | ||||
-rw-r--r-- | arch/x86/include/asm/irq_vectors.h | 5 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce.c | 54 | ||||
-rw-r--r-- | arch/x86/kernel/entry_64.S | 5 | ||||
-rw-r--r-- | arch/x86/kernel/irqinit.c | 3 |
6 files changed, 72 insertions, 0 deletions
diff --git a/arch/x86/include/asm/entry_arch.h b/arch/x86/include/asm/entry_arch.h index b2eb9c066843..4cdcf5a3c96b 100644 --- a/arch/x86/include/asm/entry_arch.h +++ b/arch/x86/include/asm/entry_arch.h | |||
@@ -60,4 +60,8 @@ BUILD_INTERRUPT(thermal_interrupt,THERMAL_APIC_VECTOR) | |||
60 | BUILD_INTERRUPT(threshold_interrupt,THRESHOLD_APIC_VECTOR) | 60 | BUILD_INTERRUPT(threshold_interrupt,THRESHOLD_APIC_VECTOR) |
61 | #endif | 61 | #endif |
62 | 62 | ||
63 | #ifdef CONFIG_X86_NEW_MCE | ||
64 | BUILD_INTERRUPT(mce_self_interrupt,MCE_SELF_VECTOR) | ||
65 | #endif | ||
66 | |||
63 | #endif | 67 | #endif |
diff --git a/arch/x86/include/asm/hw_irq.h b/arch/x86/include/asm/hw_irq.h index a7d14bbae110..4e59197e29ba 100644 --- a/arch/x86/include/asm/hw_irq.h +++ b/arch/x86/include/asm/hw_irq.h | |||
@@ -32,6 +32,7 @@ extern void error_interrupt(void); | |||
32 | extern void spurious_interrupt(void); | 32 | extern void spurious_interrupt(void); |
33 | extern void thermal_interrupt(void); | 33 | extern void thermal_interrupt(void); |
34 | extern void reschedule_interrupt(void); | 34 | extern void reschedule_interrupt(void); |
35 | extern void mce_self_interrupt(void); | ||
35 | 36 | ||
36 | extern void invalidate_interrupt(void); | 37 | extern void invalidate_interrupt(void); |
37 | extern void invalidate_interrupt0(void); | 38 | extern void invalidate_interrupt0(void); |
diff --git a/arch/x86/include/asm/irq_vectors.h b/arch/x86/include/asm/irq_vectors.h index 8c46b851296a..68f7cf84a333 100644 --- a/arch/x86/include/asm/irq_vectors.h +++ b/arch/x86/include/asm/irq_vectors.h | |||
@@ -118,6 +118,11 @@ | |||
118 | #define GENERIC_INTERRUPT_VECTOR 0xed | 118 | #define GENERIC_INTERRUPT_VECTOR 0xed |
119 | 119 | ||
120 | /* | 120 | /* |
121 | * Self IPI vector for machine checks | ||
122 | */ | ||
123 | #define MCE_SELF_VECTOR 0xeb | ||
124 | |||
125 | /* | ||
121 | * First APIC vector available to drivers: (vectors 0x30-0xee) we | 126 | * First APIC vector available to drivers: (vectors 0x30-0xee) we |
122 | * start at 0x31(0x41) to spread out vectors evenly between priority | 127 | * start at 0x31(0x41) to spread out vectors evenly between priority |
123 | * levels. (0x80 is the syscall vector) | 128 | * levels. (0x80 is the syscall vector) |
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index 5031814ac943..121781627858 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <linux/thread_info.h> | 10 | #include <linux/thread_info.h> |
11 | #include <linux/capability.h> | 11 | #include <linux/capability.h> |
12 | #include <linux/miscdevice.h> | 12 | #include <linux/miscdevice.h> |
13 | #include <linux/interrupt.h> | ||
13 | #include <linux/ratelimit.h> | 14 | #include <linux/ratelimit.h> |
14 | #include <linux/kallsyms.h> | 15 | #include <linux/kallsyms.h> |
15 | #include <linux/rcupdate.h> | 16 | #include <linux/rcupdate.h> |
@@ -32,7 +33,10 @@ | |||
32 | #include <linux/fs.h> | 33 | #include <linux/fs.h> |
33 | 34 | ||
34 | #include <asm/processor.h> | 35 | #include <asm/processor.h> |
36 | #include <asm/hw_irq.h> | ||
37 | #include <asm/apic.h> | ||
35 | #include <asm/idle.h> | 38 | #include <asm/idle.h> |
39 | #include <asm/ipi.h> | ||
36 | #include <asm/mce.h> | 40 | #include <asm/mce.h> |
37 | #include <asm/msr.h> | 41 | #include <asm/msr.h> |
38 | 42 | ||
@@ -287,6 +291,54 @@ static inline void mce_get_rip(struct mce *m, struct pt_regs *regs) | |||
287 | } | 291 | } |
288 | } | 292 | } |
289 | 293 | ||
294 | #ifdef CONFIG_X86_LOCAL_APIC | ||
295 | /* | ||
296 | * Called after interrupts have been reenabled again | ||
297 | * when a MCE happened during an interrupts off region | ||
298 | * in the kernel. | ||
299 | */ | ||
300 | asmlinkage void smp_mce_self_interrupt(struct pt_regs *regs) | ||
301 | { | ||
302 | ack_APIC_irq(); | ||
303 | exit_idle(); | ||
304 | irq_enter(); | ||
305 | mce_notify_user(); | ||
306 | irq_exit(); | ||
307 | } | ||
308 | #endif | ||
309 | |||
310 | static void mce_report_event(struct pt_regs *regs) | ||
311 | { | ||
312 | if (regs->flags & (X86_VM_MASK|X86_EFLAGS_IF)) { | ||
313 | mce_notify_user(); | ||
314 | return; | ||
315 | } | ||
316 | |||
317 | #ifdef CONFIG_X86_LOCAL_APIC | ||
318 | /* | ||
319 | * Without APIC do not notify. The event will be picked | ||
320 | * up eventually. | ||
321 | */ | ||
322 | if (!cpu_has_apic) | ||
323 | return; | ||
324 | |||
325 | /* | ||
326 | * When interrupts are disabled we cannot use | ||
327 | * kernel services safely. Trigger an self interrupt | ||
328 | * through the APIC to instead do the notification | ||
329 | * after interrupts are reenabled again. | ||
330 | */ | ||
331 | apic->send_IPI_self(MCE_SELF_VECTOR); | ||
332 | |||
333 | /* | ||
334 | * Wait for idle afterwards again so that we don't leave the | ||
335 | * APIC in a non idle state because the normal APIC writes | ||
336 | * cannot exclude us. | ||
337 | */ | ||
338 | apic_wait_icr_idle(); | ||
339 | #endif | ||
340 | } | ||
341 | |||
290 | DEFINE_PER_CPU(unsigned, mce_poll_count); | 342 | DEFINE_PER_CPU(unsigned, mce_poll_count); |
291 | 343 | ||
292 | /* | 344 | /* |
@@ -530,6 +582,8 @@ void do_machine_check(struct pt_regs *regs, long error_code) | |||
530 | /* notify userspace ASAP */ | 582 | /* notify userspace ASAP */ |
531 | set_thread_flag(TIF_MCE_NOTIFY); | 583 | set_thread_flag(TIF_MCE_NOTIFY); |
532 | 584 | ||
585 | mce_report_event(regs); | ||
586 | |||
533 | /* the last thing we do is clear state */ | 587 | /* the last thing we do is clear state */ |
534 | for (i = 0; i < banks; i++) { | 588 | for (i = 0; i < banks; i++) { |
535 | if (test_bit(i, toclear)) | 589 | if (test_bit(i, toclear)) |
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index a31a7f29cffe..711c130a8411 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S | |||
@@ -1011,6 +1011,11 @@ apicinterrupt THRESHOLD_APIC_VECTOR \ | |||
1011 | apicinterrupt THERMAL_APIC_VECTOR \ | 1011 | apicinterrupt THERMAL_APIC_VECTOR \ |
1012 | thermal_interrupt smp_thermal_interrupt | 1012 | thermal_interrupt smp_thermal_interrupt |
1013 | 1013 | ||
1014 | #ifdef CONFIG_X86_MCE | ||
1015 | apicinterrupt MCE_SELF_VECTOR \ | ||
1016 | mce_self_interrupt smp_mce_self_interrupt | ||
1017 | #endif | ||
1018 | |||
1014 | #ifdef CONFIG_SMP | 1019 | #ifdef CONFIG_SMP |
1015 | apicinterrupt CALL_FUNCTION_SINGLE_VECTOR \ | 1020 | apicinterrupt CALL_FUNCTION_SINGLE_VECTOR \ |
1016 | call_function_single_interrupt smp_call_function_single_interrupt | 1021 | call_function_single_interrupt smp_call_function_single_interrupt |
diff --git a/arch/x86/kernel/irqinit.c b/arch/x86/kernel/irqinit.c index aab3d277766c..441f6ec6e9d4 100644 --- a/arch/x86/kernel/irqinit.c +++ b/arch/x86/kernel/irqinit.c | |||
@@ -187,6 +187,9 @@ static void __init apic_intr_init(void) | |||
187 | #ifdef CONFIG_X86_THRESHOLD | 187 | #ifdef CONFIG_X86_THRESHOLD |
188 | alloc_intr_gate(THRESHOLD_APIC_VECTOR, threshold_interrupt); | 188 | alloc_intr_gate(THRESHOLD_APIC_VECTOR, threshold_interrupt); |
189 | #endif | 189 | #endif |
190 | #if defined(CONFIG_X86_NEW_MCE) && defined(CONFIG_X86_LOCAL_APIC) | ||
191 | alloc_intr_gate(MCE_SELF_VECTOR, mce_self_interrupt); | ||
192 | #endif | ||
190 | 193 | ||
191 | #if defined(CONFIG_X86_64) || defined(CONFIG_X86_LOCAL_APIC) | 194 | #if defined(CONFIG_X86_64) || defined(CONFIG_X86_LOCAL_APIC) |
192 | /* self generated IPI for local APIC timer */ | 195 | /* self generated IPI for local APIC timer */ |