Commit | Line | Data |
---|---|---|
89b831ef | 1 | /* |
95268664 | 2 | * (c) 2005, 2006 Advanced Micro Devices, Inc. |
89b831ef JS |
3 | * Your use of this code is subject to the terms and conditions of the |
4 | * GNU general public license version 2. See "COPYING" or | |
5 | * http://www.gnu.org/licenses/gpl.html | |
6 | * | |
7 | * Written by Jacob Shin - AMD, Inc. | |
8 | * | |
9 | * Support : jacob.shin@amd.com | |
10 | * | |
95268664 JS |
11 | * April 2006 |
12 | * - added support for AMD Family 0x10 processors | |
89b831ef | 13 | * |
95268664 | 14 | * All MC4_MISCi registers are shared between multi-cores |
89b831ef JS |
15 | */ |
16 | ||
17 | #include <linux/cpu.h> | |
18 | #include <linux/errno.h> | |
19 | #include <linux/init.h> | |
20 | #include <linux/interrupt.h> | |
21 | #include <linux/kobject.h> | |
22 | #include <linux/notifier.h> | |
23 | #include <linux/sched.h> | |
24 | #include <linux/smp.h> | |
25 | #include <linux/sysdev.h> | |
26 | #include <linux/sysfs.h> | |
27 | #include <asm/apic.h> | |
28 | #include <asm/mce.h> | |
29 | #include <asm/msr.h> | |
30 | #include <asm/percpu.h> | |
95833c83 | 31 | #include <asm/idle.h> |
89b831ef | 32 | |
2903ee85 JS |
33 | #define PFX "mce_threshold: " |
34 | #define VERSION "version 1.1.1" | |
35 | #define NR_BANKS 6 | |
36 | #define NR_BLOCKS 9 | |
37 | #define THRESHOLD_MAX 0xFFF | |
38 | #define INT_TYPE_APIC 0x00020000 | |
39 | #define MASK_VALID_HI 0x80000000 | |
40 | #define MASK_LVTOFF_HI 0x00F00000 | |
41 | #define MASK_COUNT_EN_HI 0x00080000 | |
42 | #define MASK_INT_TYPE_HI 0x00060000 | |
43 | #define MASK_OVERFLOW_HI 0x00010000 | |
89b831ef | 44 | #define MASK_ERR_COUNT_HI 0x00000FFF |
95268664 JS |
45 | #define MASK_BLKPTR_LO 0xFF000000 |
46 | #define MCG_XBLK_ADDR 0xC0000400 | |
89b831ef | 47 | |
95268664 JS |
48 | struct threshold_block { |
49 | unsigned int block; | |
50 | unsigned int bank; | |
89b831ef | 51 | unsigned int cpu; |
95268664 JS |
52 | u32 address; |
53 | u16 interrupt_enable; | |
89b831ef JS |
54 | u16 threshold_limit; |
55 | struct kobject kobj; | |
95268664 | 56 | struct list_head miscj; |
89b831ef JS |
57 | }; |
58 | ||
95268664 JS |
59 | /* defaults used early on boot */ |
60 | static struct threshold_block threshold_defaults = { | |
89b831ef JS |
61 | .interrupt_enable = 0, |
62 | .threshold_limit = THRESHOLD_MAX, | |
63 | }; | |
64 | ||
95268664 JS |
65 | struct threshold_bank { |
66 | struct kobject kobj; | |
67 | struct threshold_block *blocks; | |
68 | cpumask_t cpus; | |
69 | }; | |
70 | static DEFINE_PER_CPU(struct threshold_bank *, threshold_banks[NR_BANKS]); | |
71 | ||
89b831ef JS |
72 | #ifdef CONFIG_SMP |
73 | static unsigned char shared_bank[NR_BANKS] = { | |
74 | 0, 0, 0, 0, 1 | |
75 | }; | |
76 | #endif | |
77 | ||
78 | static DEFINE_PER_CPU(unsigned char, bank_map); /* see which banks are on */ | |
79 | ||
80 | /* | |
81 | * CPU Initialization | |
82 | */ | |
83 | ||
84 | /* must be called with correct cpu affinity */ | |
95268664 | 85 | static void threshold_restart_bank(struct threshold_block *b, |
89b831ef JS |
86 | int reset, u16 old_limit) |
87 | { | |
88 | u32 mci_misc_hi, mci_misc_lo; | |
89 | ||
95268664 | 90 | rdmsr(b->address, mci_misc_lo, mci_misc_hi); |
89b831ef JS |
91 | |
92 | if (b->threshold_limit < (mci_misc_hi & THRESHOLD_MAX)) | |
93 | reset = 1; /* limit cannot be lower than err count */ | |
94 | ||
95 | if (reset) { /* reset err count and overflow bit */ | |
96 | mci_misc_hi = | |
97 | (mci_misc_hi & ~(MASK_ERR_COUNT_HI | MASK_OVERFLOW_HI)) | | |
98 | (THRESHOLD_MAX - b->threshold_limit); | |
99 | } else if (old_limit) { /* change limit w/o reset */ | |
100 | int new_count = (mci_misc_hi & THRESHOLD_MAX) + | |
101 | (old_limit - b->threshold_limit); | |
102 | mci_misc_hi = (mci_misc_hi & ~MASK_ERR_COUNT_HI) | | |
103 | (new_count & THRESHOLD_MAX); | |
104 | } | |
105 | ||
106 | b->interrupt_enable ? | |
107 | (mci_misc_hi = (mci_misc_hi & ~MASK_INT_TYPE_HI) | INT_TYPE_APIC) : | |
108 | (mci_misc_hi &= ~MASK_INT_TYPE_HI); | |
109 | ||
110 | mci_misc_hi |= MASK_COUNT_EN_HI; | |
95268664 | 111 | wrmsr(b->address, mci_misc_lo, mci_misc_hi); |
89b831ef JS |
112 | } |
113 | ||
95268664 | 114 | /* cpu init entry point, called from mce.c with preempt off */ |
89b831ef JS |
115 | void __cpuinit mce_amd_feature_init(struct cpuinfo_x86 *c) |
116 | { | |
95268664 | 117 | unsigned int bank, block; |
89b831ef | 118 | unsigned int cpu = smp_processor_id(); |
95268664 | 119 | u32 low = 0, high = 0, address = 0; |
89b831ef JS |
120 | |
121 | for (bank = 0; bank < NR_BANKS; ++bank) { | |
95268664 JS |
122 | for (block = 0; block < NR_BLOCKS; ++block) { |
123 | if (block == 0) | |
124 | address = MSR_IA32_MC0_MISC + bank * 4; | |
125 | else if (block == 1) | |
126 | address = MCG_XBLK_ADDR | |
127 | + ((low & MASK_BLKPTR_LO) >> 21); | |
128 | else | |
129 | ++address; | |
130 | ||
131 | if (rdmsr_safe(address, &low, &high)) | |
132 | continue; | |
133 | ||
134 | if (!(high & MASK_VALID_HI)) { | |
135 | if (block) | |
136 | continue; | |
137 | else | |
138 | break; | |
139 | } | |
140 | ||
141 | if (!(high & MASK_VALID_HI >> 1) || | |
142 | (high & MASK_VALID_HI >> 2)) | |
143 | continue; | |
144 | ||
145 | if (!block) | |
146 | per_cpu(bank_map, cpu) |= (1 << bank); | |
89b831ef | 147 | #ifdef CONFIG_SMP |
95268664 JS |
148 | if (shared_bank[bank] && c->cpu_core_id) |
149 | break; | |
89b831ef | 150 | #endif |
95268664 JS |
151 | high &= ~MASK_LVTOFF_HI; |
152 | high |= K8_APIC_EXT_LVT_ENTRY_THRESHOLD << 20; | |
153 | wrmsr(address, low, high); | |
154 | ||
155 | setup_APIC_extened_lvt(K8_APIC_EXT_LVT_ENTRY_THRESHOLD, | |
156 | THRESHOLD_APIC_VECTOR, | |
157 | K8_APIC_EXT_INT_MSG_FIX, 0); | |
89b831ef | 158 | |
95268664 JS |
159 | threshold_defaults.address = address; |
160 | threshold_restart_bank(&threshold_defaults, 0, 0); | |
161 | } | |
89b831ef JS |
162 | } |
163 | } | |
164 | ||
165 | /* | |
166 | * APIC Interrupt Handler | |
167 | */ | |
168 | ||
169 | /* | |
170 | * threshold interrupt handler will service THRESHOLD_APIC_VECTOR. | |
171 | * the interrupt goes off when error_count reaches threshold_limit. | |
172 | * the handler will simply log mcelog w/ software defined bank number. | |
173 | */ | |
174 | asmlinkage void mce_threshold_interrupt(void) | |
175 | { | |
95268664 | 176 | unsigned int bank, block; |
89b831ef | 177 | struct mce m; |
95268664 | 178 | u32 low = 0, high = 0, address = 0; |
89b831ef JS |
179 | |
180 | ack_APIC_irq(); | |
95833c83 | 181 | exit_idle(); |
89b831ef JS |
182 | irq_enter(); |
183 | ||
184 | memset(&m, 0, sizeof(m)); | |
185 | rdtscll(m.tsc); | |
186 | m.cpu = smp_processor_id(); | |
187 | ||
188 | /* assume first bank caused it */ | |
189 | for (bank = 0; bank < NR_BANKS; ++bank) { | |
95268664 JS |
190 | for (block = 0; block < NR_BLOCKS; ++block) { |
191 | if (block == 0) | |
192 | address = MSR_IA32_MC0_MISC + bank * 4; | |
193 | else if (block == 1) | |
194 | address = MCG_XBLK_ADDR | |
195 | + ((low & MASK_BLKPTR_LO) >> 21); | |
196 | else | |
197 | ++address; | |
198 | ||
199 | if (rdmsr_safe(address, &low, &high)) | |
200 | continue; | |
201 | ||
202 | if (!(high & MASK_VALID_HI)) { | |
203 | if (block) | |
204 | continue; | |
205 | else | |
206 | break; | |
207 | } | |
208 | ||
209 | if (!(high & MASK_VALID_HI >> 1) || | |
210 | (high & MASK_VALID_HI >> 2)) | |
211 | continue; | |
212 | ||
213 | if (high & MASK_OVERFLOW_HI) { | |
214 | rdmsrl(address, m.misc); | |
215 | rdmsrl(MSR_IA32_MC0_STATUS + bank * 4, | |
216 | m.status); | |
217 | m.bank = K8_MCE_THRESHOLD_BASE | |
218 | + bank * NR_BLOCKS | |
219 | + block; | |
220 | mce_log(&m); | |
221 | goto out; | |
222 | } | |
89b831ef JS |
223 | } |
224 | } | |
2903ee85 | 225 | out: |
89b831ef JS |
226 | irq_exit(); |
227 | } | |
228 | ||
229 | /* | |
230 | * Sysfs Interface | |
231 | */ | |
232 | ||
89b831ef | 233 | struct threshold_attr { |
2903ee85 | 234 | struct attribute attr; |
95268664 JS |
235 | ssize_t(*show) (struct threshold_block *, char *); |
236 | ssize_t(*store) (struct threshold_block *, const char *, size_t count); | |
89b831ef JS |
237 | }; |
238 | ||
89b831ef JS |
239 | static cpumask_t affinity_set(unsigned int cpu) |
240 | { | |
241 | cpumask_t oldmask = current->cpus_allowed; | |
242 | cpumask_t newmask = CPU_MASK_NONE; | |
243 | cpu_set(cpu, newmask); | |
244 | set_cpus_allowed(current, newmask); | |
245 | return oldmask; | |
246 | } | |
247 | ||
248 | static void affinity_restore(cpumask_t oldmask) | |
249 | { | |
250 | set_cpus_allowed(current, oldmask); | |
251 | } | |
252 | ||
2903ee85 JS |
253 | #define SHOW_FIELDS(name) \ |
254 | static ssize_t show_ ## name(struct threshold_block * b, char *buf) \ | |
255 | { \ | |
256 | return sprintf(buf, "%lx\n", (unsigned long) b->name); \ | |
257 | } | |
89b831ef JS |
258 | SHOW_FIELDS(interrupt_enable) |
259 | SHOW_FIELDS(threshold_limit) | |
260 | ||
95268664 | 261 | static ssize_t store_interrupt_enable(struct threshold_block *b, |
89b831ef JS |
262 | const char *buf, size_t count) |
263 | { | |
264 | char *end; | |
265 | cpumask_t oldmask; | |
266 | unsigned long new = simple_strtoul(buf, &end, 0); | |
267 | if (end == buf) | |
268 | return -EINVAL; | |
269 | b->interrupt_enable = !!new; | |
270 | ||
271 | oldmask = affinity_set(b->cpu); | |
272 | threshold_restart_bank(b, 0, 0); | |
273 | affinity_restore(oldmask); | |
274 | ||
275 | return end - buf; | |
276 | } | |
277 | ||
95268664 | 278 | static ssize_t store_threshold_limit(struct threshold_block *b, |
89b831ef JS |
279 | const char *buf, size_t count) |
280 | { | |
281 | char *end; | |
282 | cpumask_t oldmask; | |
283 | u16 old; | |
284 | unsigned long new = simple_strtoul(buf, &end, 0); | |
285 | if (end == buf) | |
286 | return -EINVAL; | |
287 | if (new > THRESHOLD_MAX) | |
288 | new = THRESHOLD_MAX; | |
289 | if (new < 1) | |
290 | new = 1; | |
291 | old = b->threshold_limit; | |
292 | b->threshold_limit = new; | |
293 | ||
294 | oldmask = affinity_set(b->cpu); | |
295 | threshold_restart_bank(b, 0, old); | |
296 | affinity_restore(oldmask); | |
297 | ||
298 | return end - buf; | |
299 | } | |
300 | ||
95268664 | 301 | static ssize_t show_error_count(struct threshold_block *b, char *buf) |
89b831ef JS |
302 | { |
303 | u32 high, low; | |
304 | cpumask_t oldmask; | |
305 | oldmask = affinity_set(b->cpu); | |
95268664 | 306 | rdmsr(b->address, low, high); |
89b831ef JS |
307 | affinity_restore(oldmask); |
308 | return sprintf(buf, "%x\n", | |
309 | (high & 0xFFF) - (THRESHOLD_MAX - b->threshold_limit)); | |
310 | } | |
311 | ||
95268664 | 312 | static ssize_t store_error_count(struct threshold_block *b, |
89b831ef JS |
313 | const char *buf, size_t count) |
314 | { | |
315 | cpumask_t oldmask; | |
316 | oldmask = affinity_set(b->cpu); | |
317 | threshold_restart_bank(b, 1, 0); | |
318 | affinity_restore(oldmask); | |
319 | return 1; | |
320 | } | |
321 | ||
322 | #define THRESHOLD_ATTR(_name,_mode,_show,_store) { \ | |
323 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | |
324 | .show = _show, \ | |
325 | .store = _store, \ | |
326 | }; | |
327 | ||
2903ee85 JS |
328 | #define RW_ATTR(name) \ |
329 | static struct threshold_attr name = \ | |
89b831ef JS |
330 | THRESHOLD_ATTR(name, 0644, show_## name, store_## name) |
331 | ||
2903ee85 JS |
332 | RW_ATTR(interrupt_enable); |
333 | RW_ATTR(threshold_limit); | |
334 | RW_ATTR(error_count); | |
89b831ef JS |
335 | |
336 | static struct attribute *default_attrs[] = { | |
337 | &interrupt_enable.attr, | |
338 | &threshold_limit.attr, | |
339 | &error_count.attr, | |
340 | NULL | |
341 | }; | |
342 | ||
95268664 | 343 | #define to_block(k) container_of(k, struct threshold_block, kobj) |
2903ee85 | 344 | #define to_attr(a) container_of(a, struct threshold_attr, attr) |
89b831ef JS |
345 | |
346 | static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf) | |
347 | { | |
95268664 | 348 | struct threshold_block *b = to_block(kobj); |
89b831ef JS |
349 | struct threshold_attr *a = to_attr(attr); |
350 | ssize_t ret; | |
351 | ret = a->show ? a->show(b, buf) : -EIO; | |
352 | return ret; | |
353 | } | |
354 | ||
355 | static ssize_t store(struct kobject *kobj, struct attribute *attr, | |
356 | const char *buf, size_t count) | |
357 | { | |
95268664 | 358 | struct threshold_block *b = to_block(kobj); |
89b831ef JS |
359 | struct threshold_attr *a = to_attr(attr); |
360 | ssize_t ret; | |
361 | ret = a->store ? a->store(b, buf, count) : -EIO; | |
362 | return ret; | |
363 | } | |
364 | ||
365 | static struct sysfs_ops threshold_ops = { | |
366 | .show = show, | |
367 | .store = store, | |
368 | }; | |
369 | ||
370 | static struct kobj_type threshold_ktype = { | |
371 | .sysfs_ops = &threshold_ops, | |
372 | .default_attrs = default_attrs, | |
373 | }; | |
374 | ||
95268664 JS |
375 | static __cpuinit int allocate_threshold_blocks(unsigned int cpu, |
376 | unsigned int bank, | |
377 | unsigned int block, | |
378 | u32 address) | |
379 | { | |
380 | int err; | |
381 | u32 low, high; | |
382 | struct threshold_block *b = NULL; | |
383 | ||
384 | if ((bank >= NR_BANKS) || (block >= NR_BLOCKS)) | |
385 | return 0; | |
386 | ||
387 | if (rdmsr_safe(address, &low, &high)) | |
388 | goto recurse; | |
389 | ||
390 | if (!(high & MASK_VALID_HI)) { | |
391 | if (block) | |
392 | goto recurse; | |
393 | else | |
394 | return 0; | |
395 | } | |
396 | ||
397 | if (!(high & MASK_VALID_HI >> 1) || | |
398 | (high & MASK_VALID_HI >> 2)) | |
399 | goto recurse; | |
400 | ||
401 | b = kzalloc(sizeof(struct threshold_block), GFP_KERNEL); | |
402 | if (!b) | |
403 | return -ENOMEM; | |
404 | memset(b, 0, sizeof(struct threshold_block)); | |
405 | ||
406 | b->block = block; | |
407 | b->bank = bank; | |
408 | b->cpu = cpu; | |
409 | b->address = address; | |
410 | b->interrupt_enable = 0; | |
411 | b->threshold_limit = THRESHOLD_MAX; | |
412 | ||
413 | INIT_LIST_HEAD(&b->miscj); | |
414 | ||
415 | if (per_cpu(threshold_banks, cpu)[bank]->blocks) | |
416 | list_add(&b->miscj, | |
417 | &per_cpu(threshold_banks, cpu)[bank]->blocks->miscj); | |
418 | else | |
419 | per_cpu(threshold_banks, cpu)[bank]->blocks = b; | |
420 | ||
421 | kobject_set_name(&b->kobj, "misc%i", block); | |
422 | b->kobj.parent = &per_cpu(threshold_banks, cpu)[bank]->kobj; | |
423 | b->kobj.ktype = &threshold_ktype; | |
424 | err = kobject_register(&b->kobj); | |
425 | if (err) | |
426 | goto out_free; | |
427 | recurse: | |
428 | if (!block) { | |
429 | address = (low & MASK_BLKPTR_LO) >> 21; | |
430 | if (!address) | |
431 | return 0; | |
432 | address += MCG_XBLK_ADDR; | |
433 | } else | |
434 | ++address; | |
435 | ||
436 | err = allocate_threshold_blocks(cpu, bank, ++block, address); | |
437 | if (err) | |
438 | goto out_free; | |
439 | ||
440 | return err; | |
441 | ||
442 | out_free: | |
443 | if (b) { | |
444 | kobject_unregister(&b->kobj); | |
445 | kfree(b); | |
446 | } | |
447 | return err; | |
448 | } | |
449 | ||
89b831ef | 450 | /* symlinks sibling shared banks to first core. first core owns dir/files. */ |
95268664 | 451 | static __cpuinit int threshold_create_bank(unsigned int cpu, unsigned int bank) |
89b831ef | 452 | { |
95268664 | 453 | int i, err = 0; |
68209407 | 454 | struct threshold_bank *b = NULL; |
95268664 JS |
455 | cpumask_t oldmask = CPU_MASK_NONE; |
456 | char name[32]; | |
457 | ||
458 | sprintf(name, "threshold_bank%i", bank); | |
89b831ef JS |
459 | |
460 | #ifdef CONFIG_SMP | |
f3fa8ebc | 461 | if (cpu_data[cpu].cpu_core_id && shared_bank[bank]) { /* symlink */ |
95268664 JS |
462 | i = first_cpu(cpu_core_map[cpu]); |
463 | ||
464 | /* first core not up yet */ | |
465 | if (cpu_data[i].cpu_core_id) | |
466 | goto out; | |
467 | ||
468 | /* already linked */ | |
469 | if (per_cpu(threshold_banks, cpu)[bank]) | |
470 | goto out; | |
471 | ||
472 | b = per_cpu(threshold_banks, i)[bank]; | |
89b831ef | 473 | |
89b831ef JS |
474 | if (!b) |
475 | goto out; | |
95268664 | 476 | |
fff2e89f | 477 | err = sysfs_create_link(&per_cpu(device_mce, cpu).kobj, |
89b831ef JS |
478 | &b->kobj, name); |
479 | if (err) | |
480 | goto out; | |
95268664 JS |
481 | |
482 | b->cpus = cpu_core_map[cpu]; | |
89b831ef JS |
483 | per_cpu(threshold_banks, cpu)[bank] = b; |
484 | goto out; | |
485 | } | |
486 | #endif | |
487 | ||
95268664 | 488 | b = kzalloc(sizeof(struct threshold_bank), GFP_KERNEL); |
89b831ef JS |
489 | if (!b) { |
490 | err = -ENOMEM; | |
491 | goto out; | |
492 | } | |
493 | memset(b, 0, sizeof(struct threshold_bank)); | |
494 | ||
fff2e89f JS |
495 | kobject_set_name(&b->kobj, "threshold_bank%i", bank); |
496 | b->kobj.parent = &per_cpu(device_mce, cpu).kobj; | |
95268664 JS |
497 | #ifndef CONFIG_SMP |
498 | b->cpus = CPU_MASK_ALL; | |
499 | #else | |
500 | b->cpus = cpu_core_map[cpu]; | |
501 | #endif | |
89b831ef | 502 | err = kobject_register(&b->kobj); |
95268664 JS |
503 | if (err) |
504 | goto out_free; | |
505 | ||
89b831ef | 506 | per_cpu(threshold_banks, cpu)[bank] = b; |
95268664 JS |
507 | |
508 | oldmask = affinity_set(cpu); | |
509 | err = allocate_threshold_blocks(cpu, bank, 0, | |
510 | MSR_IA32_MC0_MISC + bank * 4); | |
511 | affinity_restore(oldmask); | |
512 | ||
513 | if (err) | |
514 | goto out_free; | |
515 | ||
516 | for_each_cpu_mask(i, b->cpus) { | |
517 | if (i == cpu) | |
518 | continue; | |
519 | ||
520 | err = sysfs_create_link(&per_cpu(device_mce, i).kobj, | |
521 | &b->kobj, name); | |
522 | if (err) | |
523 | goto out; | |
524 | ||
525 | per_cpu(threshold_banks, i)[bank] = b; | |
526 | } | |
527 | ||
528 | goto out; | |
529 | ||
530 | out_free: | |
531 | per_cpu(threshold_banks, cpu)[bank] = NULL; | |
532 | kfree(b); | |
2903ee85 | 533 | out: |
89b831ef JS |
534 | return err; |
535 | } | |
536 | ||
537 | /* create dir/files for all valid threshold banks */ | |
538 | static __cpuinit int threshold_create_device(unsigned int cpu) | |
539 | { | |
2903ee85 | 540 | unsigned int bank; |
89b831ef JS |
541 | int err = 0; |
542 | ||
89b831ef JS |
543 | for (bank = 0; bank < NR_BANKS; ++bank) { |
544 | if (!(per_cpu(bank_map, cpu) & 1 << bank)) | |
545 | continue; | |
546 | err = threshold_create_bank(cpu, bank); | |
547 | if (err) | |
548 | goto out; | |
549 | } | |
2903ee85 | 550 | out: |
89b831ef JS |
551 | return err; |
552 | } | |
553 | ||
554 | #ifdef CONFIG_HOTPLUG_CPU | |
555 | /* | |
556 | * let's be hotplug friendly. | |
557 | * in case of multiple core processors, the first core always takes ownership | |
558 | * of shared sysfs dir/files, and rest of the cores will be symlinked to it. | |
559 | */ | |
560 | ||
be6b5a35 | 561 | static void deallocate_threshold_block(unsigned int cpu, |
95268664 JS |
562 | unsigned int bank) |
563 | { | |
564 | struct threshold_block *pos = NULL; | |
565 | struct threshold_block *tmp = NULL; | |
566 | struct threshold_bank *head = per_cpu(threshold_banks, cpu)[bank]; | |
567 | ||
568 | if (!head) | |
569 | return; | |
570 | ||
571 | list_for_each_entry_safe(pos, tmp, &head->blocks->miscj, miscj) { | |
572 | kobject_unregister(&pos->kobj); | |
573 | list_del(&pos->miscj); | |
574 | kfree(pos); | |
575 | } | |
576 | ||
577 | kfree(per_cpu(threshold_banks, cpu)[bank]->blocks); | |
578 | per_cpu(threshold_banks, cpu)[bank]->blocks = NULL; | |
579 | } | |
580 | ||
be6b5a35 | 581 | static void threshold_remove_bank(unsigned int cpu, int bank) |
89b831ef | 582 | { |
95268664 | 583 | int i = 0; |
89b831ef | 584 | struct threshold_bank *b; |
95268664 | 585 | char name[32]; |
89b831ef JS |
586 | |
587 | b = per_cpu(threshold_banks, cpu)[bank]; | |
95268664 | 588 | |
89b831ef JS |
589 | if (!b) |
590 | return; | |
95268664 JS |
591 | |
592 | if (!b->blocks) | |
593 | goto free_out; | |
594 | ||
595 | sprintf(name, "threshold_bank%i", bank); | |
596 | ||
597 | /* sibling symlink */ | |
598 | if (shared_bank[bank] && b->blocks->cpu != cpu) { | |
fff2e89f | 599 | sysfs_remove_link(&per_cpu(device_mce, cpu).kobj, name); |
0d2caebd | 600 | per_cpu(threshold_banks, cpu)[bank] = NULL; |
95268664 | 601 | return; |
89b831ef | 602 | } |
95268664 JS |
603 | |
604 | /* remove all sibling symlinks before unregistering */ | |
605 | for_each_cpu_mask(i, b->cpus) { | |
606 | if (i == cpu) | |
607 | continue; | |
608 | ||
609 | sysfs_remove_link(&per_cpu(device_mce, i).kobj, name); | |
610 | per_cpu(threshold_banks, i)[bank] = NULL; | |
611 | } | |
612 | ||
613 | deallocate_threshold_block(cpu, bank); | |
614 | ||
615 | free_out: | |
616 | kobject_unregister(&b->kobj); | |
617 | kfree(b); | |
618 | per_cpu(threshold_banks, cpu)[bank] = NULL; | |
89b831ef JS |
619 | } |
620 | ||
be6b5a35 | 621 | static void threshold_remove_device(unsigned int cpu) |
89b831ef | 622 | { |
2903ee85 | 623 | unsigned int bank; |
89b831ef JS |
624 | |
625 | for (bank = 0; bank < NR_BANKS; ++bank) { | |
626 | if (!(per_cpu(bank_map, cpu) & 1 << bank)) | |
627 | continue; | |
628 | threshold_remove_bank(cpu, bank); | |
629 | } | |
89b831ef JS |
630 | } |
631 | ||
89b831ef | 632 | /* get notified when a cpu comes on/off */ |
be6b5a35 | 633 | static int threshold_cpu_callback(struct notifier_block *nfb, |
89b831ef JS |
634 | unsigned long action, void *hcpu) |
635 | { | |
636 | /* cpu was unsigned int to begin with */ | |
637 | unsigned int cpu = (unsigned long)hcpu; | |
638 | ||
639 | if (cpu >= NR_CPUS) | |
640 | goto out; | |
641 | ||
642 | switch (action) { | |
643 | case CPU_ONLINE: | |
644 | threshold_create_device(cpu); | |
89b831ef JS |
645 | break; |
646 | case CPU_DEAD: | |
647 | threshold_remove_device(cpu); | |
648 | break; | |
649 | default: | |
650 | break; | |
651 | } | |
652 | out: | |
653 | return NOTIFY_OK; | |
654 | } | |
655 | ||
be6b5a35 | 656 | static struct notifier_block threshold_cpu_notifier = { |
89b831ef JS |
657 | .notifier_call = threshold_cpu_callback, |
658 | }; | |
be6b5a35 | 659 | #endif /* CONFIG_HOTPLUG_CPU */ |
89b831ef JS |
660 | |
661 | static __init int threshold_init_device(void) | |
662 | { | |
2903ee85 | 663 | unsigned lcpu = 0; |
89b831ef | 664 | |
89b831ef JS |
665 | /* to hit CPUs online before the notifier is up */ |
666 | for_each_online_cpu(lcpu) { | |
fff2e89f | 667 | int err = threshold_create_device(lcpu); |
89b831ef | 668 | if (err) |
fff2e89f | 669 | return err; |
89b831ef | 670 | } |
be6b5a35 | 671 | register_hotcpu_notifier(&threshold_cpu_notifier); |
fff2e89f | 672 | return 0; |
89b831ef JS |
673 | } |
674 | ||
675 | device_initcall(threshold_init_device); |