aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/entry_arch.h4
-rw-r--r--arch/x86/include/asm/hw_irq.h1
-rw-r--r--arch/x86/include/asm/irq_vectors.h5
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce.c54
-rw-r--r--arch/x86/kernel/entry_64.S5
-rw-r--r--arch/x86/kernel/irqinit.c3
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)
60BUILD_INTERRUPT(threshold_interrupt,THRESHOLD_APIC_VECTOR) 60BUILD_INTERRUPT(threshold_interrupt,THRESHOLD_APIC_VECTOR)
61#endif 61#endif
62 62
63#ifdef CONFIG_X86_NEW_MCE
64BUILD_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);
32extern void spurious_interrupt(void); 32extern void spurious_interrupt(void);
33extern void thermal_interrupt(void); 33extern void thermal_interrupt(void);
34extern void reschedule_interrupt(void); 34extern void reschedule_interrupt(void);
35extern void mce_self_interrupt(void);
35 36
36extern void invalidate_interrupt(void); 37extern void invalidate_interrupt(void);
37extern void invalidate_interrupt0(void); 38extern 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 */
300asmlinkage 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
310static 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
290DEFINE_PER_CPU(unsigned, mce_poll_count); 342DEFINE_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 \
1011apicinterrupt THERMAL_APIC_VECTOR \ 1011apicinterrupt THERMAL_APIC_VECTOR \
1012 thermal_interrupt smp_thermal_interrupt 1012 thermal_interrupt smp_thermal_interrupt
1013 1013
1014#ifdef CONFIG_X86_MCE
1015apicinterrupt MCE_SELF_VECTOR \
1016 mce_self_interrupt smp_mce_self_interrupt
1017#endif
1018
1014#ifdef CONFIG_SMP 1019#ifdef CONFIG_SMP
1015apicinterrupt CALL_FUNCTION_SINGLE_VECTOR \ 1020apicinterrupt 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 */