diff options
| -rw-r--r-- | arch/x86/include/asm/mce.h | 10 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce_64.c | 16 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce_intel_64.c | 205 |
3 files changed, 228 insertions, 3 deletions
diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h index 6fc5e07eca4f..563933e06a35 100644 --- a/arch/x86/include/asm/mce.h +++ b/arch/x86/include/asm/mce.h | |||
| @@ -105,8 +105,16 @@ extern void (*threshold_cpu_callback)(unsigned long action, unsigned int cpu); | |||
| 105 | 105 | ||
| 106 | #ifdef CONFIG_X86_MCE_INTEL | 106 | #ifdef CONFIG_X86_MCE_INTEL |
| 107 | void mce_intel_feature_init(struct cpuinfo_x86 *c); | 107 | void mce_intel_feature_init(struct cpuinfo_x86 *c); |
| 108 | void cmci_clear(void); | ||
| 109 | void cmci_reenable(void); | ||
| 110 | void cmci_rediscover(int dying); | ||
| 111 | void cmci_recheck(void); | ||
| 108 | #else | 112 | #else |
| 109 | static inline void mce_intel_feature_init(struct cpuinfo_x86 *c) { } | 113 | static inline void mce_intel_feature_init(struct cpuinfo_x86 *c) { } |
| 114 | static inline void cmci_clear(void) {} | ||
| 115 | static inline void cmci_reenable(void) {} | ||
| 116 | static inline void cmci_rediscover(int dying) {} | ||
| 117 | static inline void cmci_recheck(void) {} | ||
| 110 | #endif | 118 | #endif |
| 111 | 119 | ||
| 112 | #ifdef CONFIG_X86_MCE_AMD | 120 | #ifdef CONFIG_X86_MCE_AMD |
| @@ -115,6 +123,8 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c); | |||
| 115 | static inline void mce_amd_feature_init(struct cpuinfo_x86 *c) { } | 123 | static inline void mce_amd_feature_init(struct cpuinfo_x86 *c) { } |
| 116 | #endif | 124 | #endif |
| 117 | 125 | ||
| 126 | extern int mce_available(struct cpuinfo_x86 *c); | ||
| 127 | |||
| 118 | void mce_log_therm_throt_event(__u64 status); | 128 | void mce_log_therm_throt_event(__u64 status); |
| 119 | 129 | ||
| 120 | extern atomic_t mce_entry; | 130 | extern atomic_t mce_entry; |
diff --git a/arch/x86/kernel/cpu/mcheck/mce_64.c b/arch/x86/kernel/cpu/mcheck/mce_64.c index a8ff38bfa6ed..bfbd5323a635 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_64.c +++ b/arch/x86/kernel/cpu/mcheck/mce_64.c | |||
| @@ -166,7 +166,7 @@ static void mce_panic(char *msg, struct mce *backup, unsigned long start) | |||
| 166 | panic(msg); | 166 | panic(msg); |
| 167 | } | 167 | } |
| 168 | 168 | ||
| 169 | static int mce_available(struct cpuinfo_x86 *c) | 169 | int mce_available(struct cpuinfo_x86 *c) |
| 170 | { | 170 | { |
| 171 | if (mce_dont_init) | 171 | if (mce_dont_init) |
| 172 | return 0; | 172 | return 0; |
| @@ -1060,9 +1060,12 @@ static __cpuinit void mce_remove_device(unsigned int cpu) | |||
| 1060 | static void mce_disable_cpu(void *h) | 1060 | static void mce_disable_cpu(void *h) |
| 1061 | { | 1061 | { |
| 1062 | int i; | 1062 | int i; |
| 1063 | unsigned long action = *(unsigned long *)h; | ||
| 1063 | 1064 | ||
| 1064 | if (!mce_available(¤t_cpu_data)) | 1065 | if (!mce_available(¤t_cpu_data)) |
| 1065 | return; | 1066 | return; |
| 1067 | if (!(action & CPU_TASKS_FROZEN)) | ||
| 1068 | cmci_clear(); | ||
| 1066 | for (i = 0; i < banks; i++) | 1069 | for (i = 0; i < banks; i++) |
| 1067 | wrmsrl(MSR_IA32_MC0_CTL + i*4, 0); | 1070 | wrmsrl(MSR_IA32_MC0_CTL + i*4, 0); |
| 1068 | } | 1071 | } |
| @@ -1070,9 +1073,12 @@ static void mce_disable_cpu(void *h) | |||
| 1070 | static void mce_reenable_cpu(void *h) | 1073 | static void mce_reenable_cpu(void *h) |
| 1071 | { | 1074 | { |
| 1072 | int i; | 1075 | int i; |
| 1076 | unsigned long action = *(unsigned long *)h; | ||
| 1073 | 1077 | ||
| 1074 | if (!mce_available(¤t_cpu_data)) | 1078 | if (!mce_available(¤t_cpu_data)) |
| 1075 | return; | 1079 | return; |
| 1080 | if (!(action & CPU_TASKS_FROZEN)) | ||
| 1081 | cmci_reenable(); | ||
| 1076 | for (i = 0; i < banks; i++) | 1082 | for (i = 0; i < banks; i++) |
| 1077 | wrmsrl(MSR_IA32_MC0_CTL + i*4, bank[i]); | 1083 | wrmsrl(MSR_IA32_MC0_CTL + i*4, bank[i]); |
| 1078 | } | 1084 | } |
| @@ -1100,13 +1106,17 @@ static int __cpuinit mce_cpu_callback(struct notifier_block *nfb, | |||
| 1100 | case CPU_DOWN_PREPARE: | 1106 | case CPU_DOWN_PREPARE: |
| 1101 | case CPU_DOWN_PREPARE_FROZEN: | 1107 | case CPU_DOWN_PREPARE_FROZEN: |
| 1102 | del_timer_sync(t); | 1108 | del_timer_sync(t); |
| 1103 | smp_call_function_single(cpu, mce_disable_cpu, NULL, 1); | 1109 | smp_call_function_single(cpu, mce_disable_cpu, &action, 1); |
| 1104 | break; | 1110 | break; |
| 1105 | case CPU_DOWN_FAILED: | 1111 | case CPU_DOWN_FAILED: |
| 1106 | case CPU_DOWN_FAILED_FROZEN: | 1112 | case CPU_DOWN_FAILED_FROZEN: |
| 1107 | t->expires = round_jiffies_relative(jiffies + next_interval); | 1113 | t->expires = round_jiffies_relative(jiffies + next_interval); |
| 1108 | add_timer_on(t, cpu); | 1114 | add_timer_on(t, cpu); |
| 1109 | smp_call_function_single(cpu, mce_reenable_cpu, NULL, 1); | 1115 | smp_call_function_single(cpu, mce_reenable_cpu, &action, 1); |
| 1116 | break; | ||
| 1117 | case CPU_POST_DEAD: | ||
| 1118 | /* intentionally ignoring frozen here */ | ||
| 1119 | cmci_rediscover(cpu); | ||
| 1110 | break; | 1120 | break; |
| 1111 | } | 1121 | } |
| 1112 | return NOTIFY_OK; | 1122 | return NOTIFY_OK; |
diff --git a/arch/x86/kernel/cpu/mcheck/mce_intel_64.c b/arch/x86/kernel/cpu/mcheck/mce_intel_64.c index 1b1491a76b55..a518ec8c6f89 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_intel_64.c +++ b/arch/x86/kernel/cpu/mcheck/mce_intel_64.c | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Intel specific MCE features. | 2 | * Intel specific MCE features. |
| 3 | * Copyright 2004 Zwane Mwaikambo <zwane@linuxpower.ca> | 3 | * Copyright 2004 Zwane Mwaikambo <zwane@linuxpower.ca> |
| 4 | * Copyright (C) 2008, 2009 Intel Corporation | ||
| 5 | * Author: Andi Kleen | ||
| 4 | */ | 6 | */ |
| 5 | 7 | ||
| 6 | #include <linux/init.h> | 8 | #include <linux/init.h> |
| @@ -12,6 +14,7 @@ | |||
| 12 | #include <asm/hw_irq.h> | 14 | #include <asm/hw_irq.h> |
| 13 | #include <asm/idle.h> | 15 | #include <asm/idle.h> |
| 14 | #include <asm/therm_throt.h> | 16 | #include <asm/therm_throt.h> |
| 17 | #include <asm/apic.h> | ||
| 15 | 18 | ||
| 16 | asmlinkage void smp_thermal_interrupt(void) | 19 | asmlinkage void smp_thermal_interrupt(void) |
| 17 | { | 20 | { |
| @@ -84,7 +87,209 @@ static void intel_init_thermal(struct cpuinfo_x86 *c) | |||
| 84 | return; | 87 | return; |
| 85 | } | 88 | } |
| 86 | 89 | ||
| 90 | /* | ||
| 91 | * Support for Intel Correct Machine Check Interrupts. This allows | ||
| 92 | * the CPU to raise an interrupt when a corrected machine check happened. | ||
| 93 | * Normally we pick those up using a regular polling timer. | ||
| 94 | * Also supports reliable discovery of shared banks. | ||
| 95 | */ | ||
| 96 | |||
| 97 | static DEFINE_PER_CPU(mce_banks_t, mce_banks_owned); | ||
| 98 | |||
| 99 | /* | ||
| 100 | * cmci_discover_lock protects against parallel discovery attempts | ||
| 101 | * which could race against each other. | ||
| 102 | */ | ||
| 103 | static DEFINE_SPINLOCK(cmci_discover_lock); | ||
| 104 | |||
| 105 | #define CMCI_THRESHOLD 1 | ||
| 106 | |||
| 107 | static __cpuinit int cmci_supported(int *banks) | ||
| 108 | { | ||
| 109 | u64 cap; | ||
| 110 | |||
| 111 | /* | ||
| 112 | * Vendor check is not strictly needed, but the initial | ||
| 113 | * initialization is vendor keyed and this | ||
| 114 | * makes sure none of the backdoors are entered otherwise. | ||
| 115 | */ | ||
| 116 | if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) | ||
| 117 | return 0; | ||
| 118 | if (!cpu_has_apic || lapic_get_maxlvt() < 6) | ||
| 119 | return 0; | ||
| 120 | rdmsrl(MSR_IA32_MCG_CAP, cap); | ||
| 121 | *banks = min_t(unsigned, MAX_NR_BANKS, cap & 0xff); | ||
| 122 | return !!(cap & MCG_CMCI_P); | ||
| 123 | } | ||
| 124 | |||
| 125 | /* | ||
| 126 | * The interrupt handler. This is called on every event. | ||
| 127 | * Just call the poller directly to log any events. | ||
| 128 | * This could in theory increase the threshold under high load, | ||
| 129 | * but doesn't for now. | ||
| 130 | */ | ||
| 131 | static void intel_threshold_interrupt(void) | ||
| 132 | { | ||
| 133 | machine_check_poll(MCP_TIMESTAMP, &__get_cpu_var(mce_banks_owned)); | ||
| 134 | mce_notify_user(); | ||
| 135 | } | ||
| 136 | |||
| 137 | static void print_update(char *type, int *hdr, int num) | ||
| 138 | { | ||
| 139 | if (*hdr == 0) | ||
| 140 | printk(KERN_INFO "CPU %d MCA banks", smp_processor_id()); | ||
| 141 | *hdr = 1; | ||
| 142 | printk(KERN_CONT " %s:%d", type, num); | ||
| 143 | } | ||
| 144 | |||
| 145 | /* | ||
| 146 | * Enable CMCI (Corrected Machine Check Interrupt) for available MCE banks | ||
| 147 | * on this CPU. Use the algorithm recommended in the SDM to discover shared | ||
| 148 | * banks. | ||
| 149 | */ | ||
| 150 | static __cpuinit void cmci_discover(int banks, int boot) | ||
| 151 | { | ||
| 152 | unsigned long *owned = (void *)&__get_cpu_var(mce_banks_owned); | ||
| 153 | int hdr = 0; | ||
| 154 | int i; | ||
| 155 | |||
| 156 | spin_lock(&cmci_discover_lock); | ||
| 157 | for (i = 0; i < banks; i++) { | ||
| 158 | u64 val; | ||
| 159 | |||
| 160 | if (test_bit(i, owned)) | ||
| 161 | continue; | ||
| 162 | |||
| 163 | rdmsrl(MSR_IA32_MC0_CTL2 + i, val); | ||
| 164 | |||
| 165 | /* Already owned by someone else? */ | ||
| 166 | if (val & CMCI_EN) { | ||
| 167 | if (test_and_clear_bit(i, owned) || boot) | ||
| 168 | print_update("SHD", &hdr, i); | ||
| 169 | __clear_bit(i, __get_cpu_var(mce_poll_banks)); | ||
| 170 | continue; | ||
| 171 | } | ||
| 172 | |||
| 173 | val |= CMCI_EN | CMCI_THRESHOLD; | ||
| 174 | wrmsrl(MSR_IA32_MC0_CTL2 + i, val); | ||
| 175 | rdmsrl(MSR_IA32_MC0_CTL2 + i, val); | ||
| 176 | |||
| 177 | /* Did the enable bit stick? -- the bank supports CMCI */ | ||
| 178 | if (val & CMCI_EN) { | ||
| 179 | if (!test_and_set_bit(i, owned) || boot) | ||
| 180 | print_update("CMCI", &hdr, i); | ||
| 181 | __clear_bit(i, __get_cpu_var(mce_poll_banks)); | ||
| 182 | } else { | ||
| 183 | WARN_ON(!test_bit(i, __get_cpu_var(mce_poll_banks))); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | spin_unlock(&cmci_discover_lock); | ||
| 187 | if (hdr) | ||
| 188 | printk(KERN_CONT "\n"); | ||
| 189 | } | ||
| 190 | |||
| 191 | /* | ||
| 192 | * Just in case we missed an event during initialization check | ||
| 193 | * all the CMCI owned banks. | ||
| 194 | */ | ||
| 195 | __cpuinit void cmci_recheck(void) | ||
| 196 | { | ||
| 197 | unsigned long flags; | ||
| 198 | int banks; | ||
| 199 | |||
| 200 | if (!mce_available(¤t_cpu_data) || !cmci_supported(&banks)) | ||
| 201 | return; | ||
| 202 | local_irq_save(flags); | ||
| 203 | machine_check_poll(MCP_TIMESTAMP, &__get_cpu_var(mce_banks_owned)); | ||
| 204 | local_irq_restore(flags); | ||
| 205 | } | ||
| 206 | |||
| 207 | /* | ||
| 208 | * Disable CMCI on this CPU for all banks it owns when it goes down. | ||
| 209 | * This allows other CPUs to claim the banks on rediscovery. | ||
| 210 | */ | ||
| 211 | void __cpuexit cmci_clear(void) | ||
| 212 | { | ||
| 213 | int i; | ||
| 214 | int banks; | ||
| 215 | u64 val; | ||
| 216 | |||
| 217 | if (!cmci_supported(&banks)) | ||
| 218 | return; | ||
| 219 | spin_lock(&cmci_discover_lock); | ||
| 220 | for (i = 0; i < banks; i++) { | ||
| 221 | if (!test_bit(i, __get_cpu_var(mce_banks_owned))) | ||
| 222 | continue; | ||
| 223 | /* Disable CMCI */ | ||
| 224 | rdmsrl(MSR_IA32_MC0_CTL2 + i, val); | ||
| 225 | val &= ~(CMCI_EN|CMCI_THRESHOLD_MASK); | ||
| 226 | wrmsrl(MSR_IA32_MC0_CTL2 + i, val); | ||
| 227 | __clear_bit(i, __get_cpu_var(mce_banks_owned)); | ||
| 228 | } | ||
| 229 | spin_unlock(&cmci_discover_lock); | ||
| 230 | } | ||
| 231 | |||
| 232 | /* | ||
| 233 | * After a CPU went down cycle through all the others and rediscover | ||
| 234 | * Must run in process context. | ||
| 235 | */ | ||
| 236 | void __cpuexit cmci_rediscover(int dying) | ||
| 237 | { | ||
| 238 | int banks; | ||
| 239 | int cpu; | ||
| 240 | cpumask_var_t old; | ||
| 241 | |||
| 242 | if (!cmci_supported(&banks)) | ||
| 243 | return; | ||
| 244 | if (!alloc_cpumask_var(&old, GFP_KERNEL)) | ||
| 245 | return; | ||
| 246 | cpumask_copy(old, ¤t->cpus_allowed); | ||
| 247 | |||
| 248 | for_each_online_cpu (cpu) { | ||
| 249 | if (cpu == dying) | ||
| 250 | continue; | ||
| 251 | if (set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu))) | ||
| 252 | continue; | ||
| 253 | /* Recheck banks in case CPUs don't all have the same */ | ||
| 254 | if (cmci_supported(&banks)) | ||
| 255 | cmci_discover(banks, 0); | ||
| 256 | } | ||
| 257 | |||
| 258 | set_cpus_allowed_ptr(current, old); | ||
| 259 | free_cpumask_var(old); | ||
| 260 | } | ||
| 261 | |||
| 262 | /* | ||
| 263 | * Reenable CMCI on this CPU in case a CPU down failed. | ||
| 264 | */ | ||
| 265 | void cmci_reenable(void) | ||
| 266 | { | ||
| 267 | int banks; | ||
| 268 | if (cmci_supported(&banks)) | ||
| 269 | cmci_discover(banks, 0); | ||
| 270 | } | ||
| 271 | |||
| 272 | static __cpuinit void intel_init_cmci(void) | ||
| 273 | { | ||
| 274 | int banks; | ||
| 275 | |||
| 276 | if (!cmci_supported(&banks)) | ||
| 277 | return; | ||
| 278 | |||
| 279 | mce_threshold_vector = intel_threshold_interrupt; | ||
| 280 | cmci_discover(banks, 1); | ||
| 281 | /* | ||
| 282 | * For CPU #0 this runs with still disabled APIC, but that's | ||
| 283 | * ok because only the vector is set up. We still do another | ||
| 284 | * check for the banks later for CPU #0 just to make sure | ||
| 285 | * to not miss any events. | ||
| 286 | */ | ||
| 287 | apic_write(APIC_LVTCMCI, THRESHOLD_APIC_VECTOR|APIC_DM_FIXED); | ||
| 288 | cmci_recheck(); | ||
| 289 | } | ||
| 290 | |||
| 87 | void mce_intel_feature_init(struct cpuinfo_x86 *c) | 291 | void mce_intel_feature_init(struct cpuinfo_x86 *c) |
| 88 | { | 292 | { |
| 89 | intel_init_thermal(c); | 293 | intel_init_thermal(c); |
| 294 | intel_init_cmci(); | ||
| 90 | } | 295 | } |
