diff options
Diffstat (limited to 'arch/x86/kernel/cpu/mcheck/non-fatal.c')
-rw-r--r-- | arch/x86/kernel/cpu/mcheck/non-fatal.c | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/arch/x86/kernel/cpu/mcheck/non-fatal.c b/arch/x86/kernel/cpu/mcheck/non-fatal.c new file mode 100644 index 000000000000..bf39409b3838 --- /dev/null +++ b/arch/x86/kernel/cpu/mcheck/non-fatal.c | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * Non Fatal Machine Check Exception Reporting | ||
3 | * | ||
4 | * (C) Copyright 2002 Dave Jones. <davej@codemonkey.org.uk> | ||
5 | * | ||
6 | * This file contains routines to check for non-fatal MCEs every 15s | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/init.h> | ||
11 | #include <linux/types.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/jiffies.h> | ||
14 | #include <linux/workqueue.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/smp.h> | ||
17 | #include <linux/module.h> | ||
18 | |||
19 | #include <asm/processor.h> | ||
20 | #include <asm/system.h> | ||
21 | #include <asm/msr.h> | ||
22 | |||
23 | #include "mce.h" | ||
24 | |||
25 | static int firstbank; | ||
26 | |||
27 | #define MCE_RATE 15*HZ /* timer rate is 15s */ | ||
28 | |||
29 | static void mce_checkregs (void *info) | ||
30 | { | ||
31 | u32 low, high; | ||
32 | int i; | ||
33 | |||
34 | for (i=firstbank; i<nr_mce_banks; i++) { | ||
35 | rdmsr (MSR_IA32_MC0_STATUS+i*4, low, high); | ||
36 | |||
37 | if (high & (1<<31)) { | ||
38 | printk(KERN_INFO "MCE: The hardware reports a non " | ||
39 | "fatal, correctable incident occurred on " | ||
40 | "CPU %d.\n", | ||
41 | smp_processor_id()); | ||
42 | printk (KERN_INFO "Bank %d: %08x%08x\n", i, high, low); | ||
43 | |||
44 | /* Scrub the error so we don't pick it up in MCE_RATE seconds time. */ | ||
45 | wrmsr (MSR_IA32_MC0_STATUS+i*4, 0UL, 0UL); | ||
46 | |||
47 | /* Serialize */ | ||
48 | wmb(); | ||
49 | add_taint(TAINT_MACHINE_CHECK); | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | |||
54 | static void mce_work_fn(struct work_struct *work); | ||
55 | static DECLARE_DELAYED_WORK(mce_work, mce_work_fn); | ||
56 | |||
57 | static void mce_work_fn(struct work_struct *work) | ||
58 | { | ||
59 | on_each_cpu(mce_checkregs, NULL, 1, 1); | ||
60 | schedule_delayed_work(&mce_work, round_jiffies_relative(MCE_RATE)); | ||
61 | } | ||
62 | |||
63 | static int __init init_nonfatal_mce_checker(void) | ||
64 | { | ||
65 | struct cpuinfo_x86 *c = &boot_cpu_data; | ||
66 | |||
67 | /* Check for MCE support */ | ||
68 | if (!cpu_has(c, X86_FEATURE_MCE)) | ||
69 | return -ENODEV; | ||
70 | |||
71 | /* Check for PPro style MCA */ | ||
72 | if (!cpu_has(c, X86_FEATURE_MCA)) | ||
73 | return -ENODEV; | ||
74 | |||
75 | /* Some Athlons misbehave when we frob bank 0 */ | ||
76 | if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && | ||
77 | boot_cpu_data.x86 == 6) | ||
78 | firstbank = 1; | ||
79 | else | ||
80 | firstbank = 0; | ||
81 | |||
82 | /* | ||
83 | * Check for non-fatal errors every MCE_RATE s | ||
84 | */ | ||
85 | schedule_delayed_work(&mce_work, round_jiffies_relative(MCE_RATE)); | ||
86 | printk(KERN_INFO "Machine check exception polling timer started.\n"); | ||
87 | return 0; | ||
88 | } | ||
89 | module_init(init_nonfatal_mce_checker); | ||
90 | |||
91 | MODULE_LICENSE("GPL"); | ||