diff options
Diffstat (limited to 'drivers/edac')
-rw-r--r-- | drivers/edac/Makefile | 4 | ||||
-rw-r--r-- | drivers/edac/edac_core.h | 252 | ||||
-rw-r--r-- | drivers/edac/edac_device.c | 669 | ||||
-rw-r--r-- | drivers/edac/edac_device_sysfs.c | 837 | ||||
-rw-r--r-- | drivers/edac/edac_mc.c | 8 | ||||
-rw-r--r-- | drivers/edac/edac_mc_sysfs.c | 70 | ||||
-rw-r--r-- | drivers/edac/edac_module.c | 147 | ||||
-rw-r--r-- | drivers/edac/edac_module.h | 9 |
8 files changed, 1936 insertions, 60 deletions
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 51f59aa84d30..1c67cc809218 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile | |||
@@ -10,9 +10,9 @@ | |||
10 | 10 | ||
11 | obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o | 11 | obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o |
12 | 12 | ||
13 | edac_core-objs := edac_mc.o edac_mc_sysfs.o edac_pci_sysfs.o | 13 | edac_core-objs := edac_mc.o edac_device.o edac_mc_sysfs.o edac_pci_sysfs.o |
14 | 14 | ||
15 | edac_core-objs += edac_module.o | 15 | edac_core-objs += edac_module.o edac_device_sysfs.o |
16 | 16 | ||
17 | obj-$(CONFIG_EDAC_AMD76X) += amd76x_edac.o | 17 | obj-$(CONFIG_EDAC_AMD76X) += amd76x_edac.o |
18 | obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o | 18 | obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o |
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index 397f144791ec..a3e4b97fe4fe 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h | |||
@@ -32,9 +32,14 @@ | |||
32 | #include <linux/completion.h> | 32 | #include <linux/completion.h> |
33 | #include <linux/kobject.h> | 33 | #include <linux/kobject.h> |
34 | #include <linux/platform_device.h> | 34 | #include <linux/platform_device.h> |
35 | #include <linux/sysdev.h> | ||
36 | #include <linux/workqueue.h> | ||
37 | #include <linux/version.h> | ||
35 | 38 | ||
36 | #define EDAC_MC_LABEL_LEN 31 | 39 | #define EDAC_MC_LABEL_LEN 31 |
37 | #define MC_PROC_NAME_MAX_LEN 7 | 40 | #define EDAC_DEVICE_NAME_LEN 31 |
41 | #define EDAC_ATTRIB_VALUE_LEN 15 | ||
42 | #define MC_PROC_NAME_MAX_LEN 7 | ||
38 | 43 | ||
39 | #if PAGE_SHIFT < 20 | 44 | #if PAGE_SHIFT < 20 |
40 | #define PAGES_TO_MiB( pages ) ( ( pages ) >> ( 20 - PAGE_SHIFT ) ) | 45 | #define PAGES_TO_MiB( pages ) ( ( pages ) >> ( 20 - PAGE_SHIFT ) ) |
@@ -51,6 +56,10 @@ | |||
51 | #define edac_mc_chipset_printk(mci, level, prefix, fmt, arg...) \ | 56 | #define edac_mc_chipset_printk(mci, level, prefix, fmt, arg...) \ |
52 | printk(level "EDAC " prefix " MC%d: " fmt, mci->mc_idx, ##arg) | 57 | printk(level "EDAC " prefix " MC%d: " fmt, mci->mc_idx, ##arg) |
53 | 58 | ||
59 | /* edac_device printk */ | ||
60 | #define edac_device_printk(ctl, level, fmt, arg...) \ | ||
61 | printk(level "EDAC DEVICE%d: " fmt, ctl->dev_idx, ##arg) | ||
62 | |||
54 | /* prefixes for edac_printk() and edac_mc_printk() */ | 63 | /* prefixes for edac_printk() and edac_mc_printk() */ |
55 | #define EDAC_MC "MC" | 64 | #define EDAC_MC "MC" |
56 | #define EDAC_PCI "PCI" | 65 | #define EDAC_PCI "PCI" |
@@ -62,7 +71,7 @@ extern int edac_debug_level; | |||
62 | #define edac_debug_printk(level, fmt, arg...) \ | 71 | #define edac_debug_printk(level, fmt, arg...) \ |
63 | do { \ | 72 | do { \ |
64 | if (level <= edac_debug_level) \ | 73 | if (level <= edac_debug_level) \ |
65 | edac_printk(KERN_DEBUG, EDAC_DEBUG, fmt, ##arg); \ | 74 | edac_printk(KERN_EMERG, EDAC_DEBUG, fmt, ##arg); \ |
66 | } while(0) | 75 | } while(0) |
67 | 76 | ||
68 | #define debugf0( ... ) edac_debug_printk(0, __VA_ARGS__ ) | 77 | #define debugf0( ... ) edac_debug_printk(0, __VA_ARGS__ ) |
@@ -195,6 +204,8 @@ enum scrub_type { | |||
195 | 204 | ||
196 | /* FIXME - should have notify capabilities: NMI, LOG, PROC, etc */ | 205 | /* FIXME - should have notify capabilities: NMI, LOG, PROC, etc */ |
197 | 206 | ||
207 | extern char * edac_align_ptr(void *ptr, unsigned size); | ||
208 | |||
198 | /* | 209 | /* |
199 | * There are several things to be aware of that aren't at all obvious: | 210 | * There are several things to be aware of that aren't at all obvious: |
200 | * | 211 | * |
@@ -376,6 +387,231 @@ struct mem_ctl_info { | |||
376 | struct completion kobj_complete; | 387 | struct completion kobj_complete; |
377 | }; | 388 | }; |
378 | 389 | ||
390 | /* | ||
391 | * The following are the structures to provide for a generice | ||
392 | * or abstract 'edac_device'. This set of structures and the | ||
393 | * code that implements the APIs for the same, provide for | ||
394 | * registering EDAC type devices which are NOT standard memory. | ||
395 | * | ||
396 | * CPU caches (L1 and L2) | ||
397 | * DMA engines | ||
398 | * Core CPU swithces | ||
399 | * Fabric switch units | ||
400 | * PCIe interface controllers | ||
401 | * other EDAC/ECC type devices that can be monitored for | ||
402 | * errors, etc. | ||
403 | * | ||
404 | * It allows for a 2 level set of hiearchry. For example: | ||
405 | * | ||
406 | * cache could be composed of L1, L2 and L3 levels of cache. | ||
407 | * Each CPU core would have its own L1 cache, while sharing | ||
408 | * L2 and maybe L3 caches. | ||
409 | * | ||
410 | * View them arranged, via the sysfs presentation: | ||
411 | * /sys/devices/system/edac/.. | ||
412 | * | ||
413 | * mc/ <existing memory device directory> | ||
414 | * cpu/cpu0/.. <L1 and L2 block directory> | ||
415 | * /L1-cache/ce_count | ||
416 | * /ue_count | ||
417 | * /L2-cache/ce_count | ||
418 | * /ue_count | ||
419 | * cpu/cpu1/.. <L1 and L2 block directory> | ||
420 | * /L1-cache/ce_count | ||
421 | * /ue_count | ||
422 | * /L2-cache/ce_count | ||
423 | * /ue_count | ||
424 | * ... | ||
425 | * | ||
426 | * the L1 and L2 directories would be "edac_device_block's" | ||
427 | */ | ||
428 | |||
429 | struct edac_device_counter { | ||
430 | u32 ue_count; | ||
431 | u32 ce_count; | ||
432 | }; | ||
433 | |||
434 | #define INC_COUNTER(cnt) (cnt++) | ||
435 | |||
436 | /* | ||
437 | * An array of these is passed to the alloc() function | ||
438 | * to specify attributes of the edac_block | ||
439 | */ | ||
440 | struct edac_attrib_spec { | ||
441 | char name[EDAC_DEVICE_NAME_LEN + 1]; | ||
442 | |||
443 | int type; | ||
444 | #define EDAC_ATTR_INT 0x01 | ||
445 | #define EDAC_ATTR_CHAR 0x02 | ||
446 | }; | ||
447 | |||
448 | |||
449 | /* Attribute control structure | ||
450 | * In this structure is a pointer to the driver's edac_attrib_spec | ||
451 | * The life of this pointer is inclusive in the life of the driver's | ||
452 | * life cycle. | ||
453 | */ | ||
454 | struct edac_attrib { | ||
455 | struct edac_device_block *block; /* Up Pointer */ | ||
456 | |||
457 | struct edac_attrib_spec *spec; /* ptr to module spec entry */ | ||
458 | |||
459 | union { /* actual value */ | ||
460 | int edac_attrib_int_value; | ||
461 | char edac_attrib_char_value[EDAC_ATTRIB_VALUE_LEN + 1]; | ||
462 | } edac_attrib_value; | ||
463 | }; | ||
464 | |||
465 | /* device block control structure */ | ||
466 | struct edac_device_block { | ||
467 | struct edac_device_instance *instance; /* Up Pointer */ | ||
468 | char name[EDAC_DEVICE_NAME_LEN + 1]; | ||
469 | |||
470 | struct edac_device_counter counters; /* basic UE and CE counters */ | ||
471 | |||
472 | int nr_attribs; /* how many attributes */ | ||
473 | struct edac_attrib *attribs; /* this block's attributes */ | ||
474 | |||
475 | /* edac sysfs device control */ | ||
476 | struct kobject kobj; | ||
477 | struct completion kobj_complete; | ||
478 | }; | ||
479 | |||
480 | /* device instance control structure */ | ||
481 | struct edac_device_instance { | ||
482 | struct edac_device_ctl_info *ctl; /* Up pointer */ | ||
483 | char name[EDAC_DEVICE_NAME_LEN + 4]; | ||
484 | |||
485 | struct edac_device_counter counters; /* instance counters */ | ||
486 | |||
487 | u32 nr_blocks; /* how many blocks */ | ||
488 | struct edac_device_block *blocks; /* block array */ | ||
489 | |||
490 | /* edac sysfs device control */ | ||
491 | struct kobject kobj; | ||
492 | struct completion kobj_complete; | ||
493 | }; | ||
494 | |||
495 | |||
496 | /* | ||
497 | * Abstract edac_device control info structure | ||
498 | * | ||
499 | */ | ||
500 | struct edac_device_ctl_info { | ||
501 | /* for global list of edac_device_ctl_info structs */ | ||
502 | struct list_head link; | ||
503 | |||
504 | int dev_idx; | ||
505 | |||
506 | /* Per instance controls for this edac_device */ | ||
507 | int log_ue; /* boolean for logging UEs */ | ||
508 | int log_ce; /* boolean for logging CEs */ | ||
509 | int panic_on_ue; /* boolean for panic'ing on an UE */ | ||
510 | unsigned poll_msec; /* number of milliseconds to poll interval */ | ||
511 | unsigned long delay; /* number of jiffies for poll_msec */ | ||
512 | |||
513 | struct sysdev_class *edac_class; /* pointer to class */ | ||
514 | |||
515 | /* the internal state of this controller instance */ | ||
516 | int op_state; | ||
517 | #define OP_ALLOC 0x100 | ||
518 | #define OP_RUNNING_POLL 0x201 | ||
519 | #define OP_RUNNING_INTERRUPT 0x202 | ||
520 | #define OP_RUNNING_POLL_INTR 0x203 | ||
521 | #define OP_OFFLINE 0x300 | ||
522 | |||
523 | /* work struct for this instance */ | ||
524 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) | ||
525 | struct delayed_work work; | ||
526 | #else | ||
527 | struct work_struct work; | ||
528 | #endif | ||
529 | |||
530 | /* pointer to edac polling checking routine: | ||
531 | * If NOT NULL: points to polling check routine | ||
532 | * If NULL: Then assumes INTERRUPT operation, where | ||
533 | * MC driver will receive events | ||
534 | */ | ||
535 | void (*edac_check) (struct edac_device_ctl_info * edac_dev); | ||
536 | |||
537 | struct device *dev; /* pointer to device structure */ | ||
538 | |||
539 | const char *mod_name; /* module name */ | ||
540 | const char *ctl_name; /* edac controller name */ | ||
541 | |||
542 | void *pvt_info; /* pointer to 'private driver' info */ | ||
543 | |||
544 | unsigned long start_time;/* edac_device load start time (jiffies)*/ | ||
545 | |||
546 | /* these are for safe removal of mc devices from global list while | ||
547 | * NMI handlers may be traversing list | ||
548 | */ | ||
549 | struct rcu_head rcu; | ||
550 | struct completion complete; | ||
551 | |||
552 | /* sysfs top name under 'edac' directory | ||
553 | * and instance name: | ||
554 | * cpu/cpu0/... | ||
555 | * cpu/cpu1/... | ||
556 | * cpu/cpu2/... | ||
557 | * ... | ||
558 | */ | ||
559 | char name[EDAC_DEVICE_NAME_LEN + 1]; | ||
560 | |||
561 | /* Number of instances supported on this control structure | ||
562 | * and the array of those instances | ||
563 | */ | ||
564 | u32 nr_instances; | ||
565 | struct edac_device_instance *instances; | ||
566 | |||
567 | /* Event counters for the this whole EDAC Device */ | ||
568 | struct edac_device_counter counters; | ||
569 | |||
570 | /* edac sysfs device control for the 'name' | ||
571 | * device this structure controls | ||
572 | */ | ||
573 | struct kobject kobj; | ||
574 | struct completion kobj_complete; | ||
575 | }; | ||
576 | |||
577 | /* To get from the instance's wq to the beginning of the ctl structure */ | ||
578 | #define to_edac_device_ctl_work(w) \ | ||
579 | container_of(w,struct edac_device_ctl_info,work) | ||
580 | |||
581 | /* Function to calc the number of delay jiffies from poll_msec */ | ||
582 | static inline void edac_device_calc_delay( | ||
583 | struct edac_device_ctl_info *edac_dev) | ||
584 | { | ||
585 | /* convert from msec to jiffies */ | ||
586 | edac_dev->delay = edac_dev->poll_msec * HZ / 1000; | ||
587 | } | ||
588 | |||
589 | /* | ||
590 | * The alloc() and free() functions for the 'edac_device' control info | ||
591 | * structure. A MC driver will allocate one of these for each edac_device | ||
592 | * it is going to control/register with the EDAC CORE. | ||
593 | */ | ||
594 | extern struct edac_device_ctl_info *edac_device_alloc_ctl_info( | ||
595 | unsigned sizeof_private, | ||
596 | char *edac_device_name, | ||
597 | unsigned nr_instances, | ||
598 | char *edac_block_name, | ||
599 | unsigned nr_blocks, | ||
600 | unsigned offset_value, | ||
601 | struct edac_attrib_spec *attrib_spec, | ||
602 | unsigned nr_attribs | ||
603 | ); | ||
604 | |||
605 | /* The offset value can be: | ||
606 | * -1 indicating no offset value | ||
607 | * 0 for zero-based block numbers | ||
608 | * 1 for 1-based block number | ||
609 | * other for other-based block number | ||
610 | */ | ||
611 | #define BLOCK_OFFSET_VALUE_OFF ((unsigned) -1) | ||
612 | |||
613 | extern void edac_device_free_ctl_info( struct edac_device_ctl_info *ctl_info); | ||
614 | |||
379 | #ifdef CONFIG_PCI | 615 | #ifdef CONFIG_PCI |
380 | 616 | ||
381 | /* write all or some bits in a byte-register*/ | 617 | /* write all or some bits in a byte-register*/ |
@@ -466,13 +702,17 @@ extern void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci, | |||
466 | char *msg); | 702 | char *msg); |
467 | 703 | ||
468 | /* | 704 | /* |
469 | * This kmalloc's and initializes all the structures. | 705 | * edac_device APIs |
470 | * Can't be used if all structures don't have the same lifetime. | ||
471 | */ | 706 | */ |
472 | extern struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows, | 707 | extern struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows, |
473 | unsigned nr_chans); | 708 | unsigned nr_chans); |
474 | |||
475 | /* Free an mc previously allocated by edac_mc_alloc() */ | ||
476 | extern void edac_mc_free(struct mem_ctl_info *mci); | 709 | extern void edac_mc_free(struct mem_ctl_info *mci); |
710 | extern int edac_device_add_device(struct edac_device_ctl_info *edac_dev, int edac_idx); | ||
711 | extern struct edac_device_ctl_info * edac_device_del_device(struct device *dev); | ||
712 | extern void edac_device_handle_ue(struct edac_device_ctl_info *edac_dev, | ||
713 | int inst_nr, int block_nr, const char *msg); | ||
714 | extern void edac_device_handle_ce(struct edac_device_ctl_info *edac_dev, | ||
715 | int inst_nr, int block_nr, const char *msg); | ||
716 | |||
477 | 717 | ||
478 | #endif /* _EDAC_CORE_H_ */ | 718 | #endif /* _EDAC_CORE_H_ */ |
diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c new file mode 100644 index 000000000000..c579c498cc75 --- /dev/null +++ b/drivers/edac/edac_device.c | |||
@@ -0,0 +1,669 @@ | |||
1 | |||
2 | /* | ||
3 | * edac_device.c | ||
4 | * (C) 2007 www.douglaskthompson.com | ||
5 | * | ||
6 | * This file may be distributed under the terms of the | ||
7 | * GNU General Public License. | ||
8 | * | ||
9 | * Written by Doug Thompson <norsk5@xmission.com> | ||
10 | * | ||
11 | * edac_device API implementation | ||
12 | * 19 Jan 2007 | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/smp.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/sysctl.h> | ||
20 | #include <linux/highmem.h> | ||
21 | #include <linux/timer.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/list.h> | ||
25 | #include <linux/sysdev.h> | ||
26 | #include <linux/ctype.h> | ||
27 | #include <linux/workqueue.h> | ||
28 | #include <asm/uaccess.h> | ||
29 | #include <asm/page.h> | ||
30 | |||
31 | #include "edac_core.h" | ||
32 | #include "edac_module.h" | ||
33 | |||
34 | /* lock to memory controller's control array */ | ||
35 | static DECLARE_MUTEX(device_ctls_mutex); | ||
36 | static struct list_head edac_device_list = LIST_HEAD_INIT(edac_device_list); | ||
37 | |||
38 | |||
39 | static inline void lock_device_list(void) | ||
40 | { | ||
41 | down(&device_ctls_mutex); | ||
42 | } | ||
43 | |||
44 | static inline void unlock_device_list(void) | ||
45 | { | ||
46 | up(&device_ctls_mutex); | ||
47 | } | ||
48 | |||
49 | |||
50 | #ifdef CONFIG_EDAC_DEBUG | ||
51 | static void edac_device_dump_device(struct edac_device_ctl_info *edac_dev) | ||
52 | { | ||
53 | debugf3("\tedac_dev = %p dev_idx=%d \n", edac_dev,edac_dev->dev_idx); | ||
54 | debugf4("\tedac_dev->edac_check = %p\n", edac_dev->edac_check); | ||
55 | debugf3("\tdev = %p\n", edac_dev->dev); | ||
56 | debugf3("\tmod_name:ctl_name = %s:%s\n", | ||
57 | edac_dev->mod_name, edac_dev->ctl_name); | ||
58 | debugf3("\tpvt_info = %p\n\n", edac_dev->pvt_info); | ||
59 | } | ||
60 | #endif /* CONFIG_EDAC_DEBUG */ | ||
61 | |||
62 | /* | ||
63 | * The alloc() and free() functions for the 'edac_device' control info | ||
64 | * structure. A MC driver will allocate one of these for each edac_device | ||
65 | * it is going to control/register with the EDAC CORE. | ||
66 | */ | ||
67 | struct edac_device_ctl_info *edac_device_alloc_ctl_info( | ||
68 | unsigned sz_private, | ||
69 | char *edac_device_name, | ||
70 | unsigned nr_instances, | ||
71 | char *edac_block_name, | ||
72 | unsigned nr_blocks, | ||
73 | unsigned offset_value, | ||
74 | struct edac_attrib_spec *attrib_spec, | ||
75 | unsigned nr_attribs) | ||
76 | { | ||
77 | struct edac_device_ctl_info *dev_ctl; | ||
78 | struct edac_device_instance *dev_inst, *inst; | ||
79 | struct edac_device_block *dev_blk, *blk_p, *blk; | ||
80 | struct edac_attrib *dev_attrib, *attrib_p, *attrib; | ||
81 | unsigned total_size; | ||
82 | unsigned count; | ||
83 | unsigned instance, block, attr; | ||
84 | void *pvt; | ||
85 | |||
86 | debugf1("%s() instances=%d blocks=%d\n", | ||
87 | __func__,nr_instances,nr_blocks); | ||
88 | |||
89 | /* Figure out the offsets of the various items from the start of an | ||
90 | * ctl_info structure. We want the alignment of each item | ||
91 | * to be at least as stringent as what the compiler would | ||
92 | * provide if we could simply hardcode everything into a single struct. | ||
93 | */ | ||
94 | dev_ctl = (struct edac_device_ctl_info *) 0; | ||
95 | |||
96 | /* Calc the 'end' offset past the ctl_info structure */ | ||
97 | dev_inst = (struct edac_device_instance *) | ||
98 | edac_align_ptr(&dev_ctl[1],sizeof(*dev_inst)); | ||
99 | |||
100 | /* Calc the 'end' offset past the instance array */ | ||
101 | dev_blk = (struct edac_device_block *) | ||
102 | edac_align_ptr(&dev_inst[nr_instances],sizeof(*dev_blk)); | ||
103 | |||
104 | /* Calc the 'end' offset past the dev_blk array */ | ||
105 | count = nr_instances * nr_blocks; | ||
106 | dev_attrib = (struct edac_attrib *) | ||
107 | edac_align_ptr(&dev_blk[count],sizeof(*dev_attrib)); | ||
108 | |||
109 | /* Check for case of NO attributes specified */ | ||
110 | if (nr_attribs > 0) | ||
111 | count *= nr_attribs; | ||
112 | |||
113 | /* Calc the 'end' offset past the attributes array */ | ||
114 | pvt = edac_align_ptr(&dev_attrib[count],sz_private); | ||
115 | total_size = ((unsigned long) pvt) + sz_private; | ||
116 | |||
117 | /* Allocate the amount of memory for the set of control structures */ | ||
118 | if ((dev_ctl = kmalloc(total_size, GFP_KERNEL)) == NULL) | ||
119 | return NULL; | ||
120 | |||
121 | /* Adjust pointers so they point within the memory we just allocated | ||
122 | * rather than an imaginary chunk of memory located at address 0. | ||
123 | */ | ||
124 | dev_inst = (struct edac_device_instance *) | ||
125 | (((char *) dev_ctl) + ((unsigned long) dev_inst)); | ||
126 | dev_blk = (struct edac_device_block *) | ||
127 | (((char *) dev_ctl) + ((unsigned long) dev_blk)); | ||
128 | dev_attrib = (struct edac_attrib *) | ||
129 | (((char *) dev_ctl) + ((unsigned long) dev_attrib)); | ||
130 | pvt = sz_private ? | ||
131 | (((char *) dev_ctl) + ((unsigned long) pvt)) : NULL; | ||
132 | |||
133 | memset(dev_ctl, 0, total_size); /* clear all fields */ | ||
134 | dev_ctl->nr_instances = nr_instances; | ||
135 | dev_ctl->instances = dev_inst; | ||
136 | dev_ctl->pvt_info = pvt; | ||
137 | |||
138 | /* Name of this edac device, ensure null terminated */ | ||
139 | snprintf(dev_ctl->name,sizeof(dev_ctl->name),"%s", edac_device_name); | ||
140 | dev_ctl->name[sizeof(dev_ctl->name)-1] = '\0'; | ||
141 | |||
142 | /* Initialize every Instance */ | ||
143 | for (instance = 0; instance < nr_instances; instance++) { | ||
144 | inst = &dev_inst[instance]; | ||
145 | inst->ctl = dev_ctl; | ||
146 | inst->nr_blocks = nr_blocks; | ||
147 | blk_p = &dev_blk[instance * nr_blocks]; | ||
148 | inst->blocks = blk_p; | ||
149 | |||
150 | /* name of this instance */ | ||
151 | snprintf(inst->name, sizeof(inst->name), | ||
152 | "%s%u", edac_device_name, instance); | ||
153 | inst->name[sizeof(inst->name)-1] = '\0'; | ||
154 | |||
155 | /* Initialize every block in each instance */ | ||
156 | for ( block = 0; | ||
157 | block < nr_blocks; | ||
158 | block++) { | ||
159 | blk = &blk_p[block]; | ||
160 | blk->instance = inst; | ||
161 | blk->nr_attribs = nr_attribs; | ||
162 | attrib_p = &dev_attrib[block * nr_attribs]; | ||
163 | blk->attribs = attrib_p; | ||
164 | snprintf(blk->name, sizeof(blk->name), | ||
165 | "%s%d", edac_block_name,block+1); | ||
166 | blk->name[sizeof(blk->name)-1] = '\0'; | ||
167 | |||
168 | debugf1("%s() instance=%d block=%d name=%s\n", | ||
169 | __func__, instance,block,blk->name); | ||
170 | |||
171 | if (attrib_spec != NULL) { | ||
172 | /* when there is an attrib_spec passed int then | ||
173 | * Initialize every attrib of each block | ||
174 | */ | ||
175 | for (attr = 0; attr < nr_attribs; attr++) { | ||
176 | attrib = &attrib_p[attr]; | ||
177 | attrib->block = blk; | ||
178 | |||
179 | /* Link each attribute to the caller's | ||
180 | * spec entry, for name and type | ||
181 | */ | ||
182 | attrib->spec = &attrib_spec[attr]; | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
188 | /* Mark this instance as merely ALLOCATED */ | ||
189 | dev_ctl->op_state = OP_ALLOC; | ||
190 | |||
191 | return dev_ctl; | ||
192 | } | ||
193 | EXPORT_SYMBOL_GPL(edac_device_alloc_ctl_info); | ||
194 | |||
195 | /* | ||
196 | * edac_device_free_ctl_info() | ||
197 | * frees the memory allocated by the edac_device_alloc_ctl_info() | ||
198 | * function | ||
199 | */ | ||
200 | void edac_device_free_ctl_info( struct edac_device_ctl_info *ctl_info) { | ||
201 | kfree(ctl_info); | ||
202 | } | ||
203 | EXPORT_SYMBOL_GPL(edac_device_free_ctl_info); | ||
204 | |||
205 | |||
206 | |||
207 | /* | ||
208 | * find_edac_device_by_dev | ||
209 | * scans the edac_device list for a specific 'struct device *' | ||
210 | */ | ||
211 | static struct edac_device_ctl_info * | ||
212 | find_edac_device_by_dev(struct device *dev) | ||
213 | { | ||
214 | struct edac_device_ctl_info *edac_dev; | ||
215 | struct list_head *item; | ||
216 | |||
217 | debugf3("%s()\n", __func__); | ||
218 | |||
219 | list_for_each(item, &edac_device_list) { | ||
220 | edac_dev = list_entry(item, struct edac_device_ctl_info, link); | ||
221 | |||
222 | if (edac_dev->dev == dev) | ||
223 | return edac_dev; | ||
224 | } | ||
225 | |||
226 | return NULL; | ||
227 | } | ||
228 | |||
229 | /* | ||
230 | * add_edac_dev_to_global_list | ||
231 | * Before calling this function, caller must | ||
232 | * assign a unique value to edac_dev->dev_idx. | ||
233 | * Return: | ||
234 | * 0 on success | ||
235 | * 1 on failure. | ||
236 | */ | ||
237 | static int add_edac_dev_to_global_list (struct edac_device_ctl_info *edac_dev) | ||
238 | { | ||
239 | struct list_head *item, *insert_before; | ||
240 | struct edac_device_ctl_info *rover; | ||
241 | |||
242 | insert_before = &edac_device_list; | ||
243 | |||
244 | /* Determine if already on the list */ | ||
245 | if (unlikely((rover = find_edac_device_by_dev(edac_dev->dev)) != NULL)) | ||
246 | goto fail0; | ||
247 | |||
248 | /* Insert in ascending order by 'dev_idx', so find position */ | ||
249 | list_for_each(item, &edac_device_list) { | ||
250 | rover = list_entry(item, struct edac_device_ctl_info, link); | ||
251 | |||
252 | if (rover->dev_idx >= edac_dev->dev_idx) { | ||
253 | if (unlikely(rover->dev_idx == edac_dev->dev_idx)) | ||
254 | goto fail1; | ||
255 | |||
256 | insert_before = item; | ||
257 | break; | ||
258 | } | ||
259 | } | ||
260 | |||
261 | list_add_tail_rcu(&edac_dev->link, insert_before); | ||
262 | return 0; | ||
263 | |||
264 | fail0: | ||
265 | edac_printk(KERN_WARNING, EDAC_MC, | ||
266 | "%s (%s) %s %s already assigned %d\n", | ||
267 | rover->dev->bus_id, dev_name(rover->dev), | ||
268 | rover->mod_name, rover->ctl_name, rover->dev_idx); | ||
269 | return 1; | ||
270 | |||
271 | fail1: | ||
272 | edac_printk(KERN_WARNING, EDAC_MC, | ||
273 | "bug in low-level driver: attempt to assign\n" | ||
274 | " duplicate dev_idx %d in %s()\n", rover->dev_idx, __func__); | ||
275 | return 1; | ||
276 | } | ||
277 | |||
278 | /* | ||
279 | * complete_edac_device_list_del | ||
280 | */ | ||
281 | static void complete_edac_device_list_del(struct rcu_head *head) | ||
282 | { | ||
283 | struct edac_device_ctl_info *edac_dev; | ||
284 | |||
285 | edac_dev = container_of(head, struct edac_device_ctl_info, rcu); | ||
286 | INIT_LIST_HEAD(&edac_dev->link); | ||
287 | complete(&edac_dev->complete); | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * del_edac_device_from_global_list | ||
292 | */ | ||
293 | static void del_edac_device_from_global_list( | ||
294 | struct edac_device_ctl_info *edac_device) | ||
295 | { | ||
296 | list_del_rcu(&edac_device->link); | ||
297 | init_completion(&edac_device->complete); | ||
298 | call_rcu(&edac_device->rcu, complete_edac_device_list_del); | ||
299 | wait_for_completion(&edac_device->complete); | ||
300 | } | ||
301 | |||
302 | /** | ||
303 | * edac_device_find | ||
304 | * Search for a edac_device_ctl_info structure whose index is 'idx'. | ||
305 | * | ||
306 | * If found, return a pointer to the structure. | ||
307 | * Else return NULL. | ||
308 | * | ||
309 | * Caller must hold device_ctls_mutex. | ||
310 | */ | ||
311 | struct edac_device_ctl_info * edac_device_find(int idx) | ||
312 | { | ||
313 | struct list_head *item; | ||
314 | struct edac_device_ctl_info *edac_dev; | ||
315 | |||
316 | /* Iterate over list, looking for exact match of ID */ | ||
317 | list_for_each(item, &edac_device_list) { | ||
318 | edac_dev = list_entry(item, struct edac_device_ctl_info, link); | ||
319 | |||
320 | if (edac_dev->dev_idx >= idx) { | ||
321 | if (edac_dev->dev_idx == idx) | ||
322 | return edac_dev; | ||
323 | |||
324 | /* not on list, so terminate early */ | ||
325 | break; | ||
326 | } | ||
327 | } | ||
328 | |||
329 | return NULL; | ||
330 | } | ||
331 | EXPORT_SYMBOL(edac_device_find); | ||
332 | |||
333 | |||
334 | /* | ||
335 | * edac_workq_function | ||
336 | * performs the operation scheduled by a workq request | ||
337 | */ | ||
338 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) | ||
339 | static void edac_workq_function(struct work_struct *work_req) | ||
340 | { | ||
341 | struct delayed_work *d_work = (struct delayed_work*) work_req; | ||
342 | struct edac_device_ctl_info *edac_dev = | ||
343 | to_edac_device_ctl_work(d_work); | ||
344 | #else | ||
345 | static void edac_workq_function(void *ptr) | ||
346 | { | ||
347 | struct edac_device_ctl_info *edac_dev = | ||
348 | (struct edac_device_ctl_info *) ptr; | ||
349 | #endif | ||
350 | |||
351 | //debugf0("%s() here and running\n", __func__); | ||
352 | lock_device_list(); | ||
353 | |||
354 | /* Only poll controllers that are running polled and have a check */ | ||
355 | if ((edac_dev->op_state == OP_RUNNING_POLL) && | ||
356 | (edac_dev->edac_check != NULL)) { | ||
357 | edac_dev->edac_check(edac_dev); | ||
358 | } | ||
359 | |||
360 | unlock_device_list(); | ||
361 | |||
362 | /* Reschedule */ | ||
363 | queue_delayed_work(edac_workqueue,&edac_dev->work, edac_dev->delay); | ||
364 | } | ||
365 | |||
366 | /* | ||
367 | * edac_workq_setup | ||
368 | * initialize a workq item for this edac_device instance | ||
369 | * passing in the new delay period in msec | ||
370 | */ | ||
371 | void edac_workq_setup(struct edac_device_ctl_info *edac_dev, unsigned msec) | ||
372 | { | ||
373 | debugf0("%s()\n", __func__); | ||
374 | |||
375 | edac_dev->poll_msec = msec; | ||
376 | edac_device_calc_delay(edac_dev); /* Calc delay jiffies */ | ||
377 | |||
378 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) | ||
379 | INIT_DELAYED_WORK(&edac_dev->work,edac_workq_function); | ||
380 | #else | ||
381 | INIT_WORK(&edac_dev->work,edac_workq_function,edac_dev); | ||
382 | #endif | ||
383 | queue_delayed_work(edac_workqueue,&edac_dev->work, edac_dev->delay); | ||
384 | } | ||
385 | |||
386 | /* | ||
387 | * edac_workq_teardown | ||
388 | * stop the workq processing on this edac_dev | ||
389 | */ | ||
390 | void edac_workq_teardown(struct edac_device_ctl_info *edac_dev) | ||
391 | { | ||
392 | int status; | ||
393 | |||
394 | status = cancel_delayed_work(&edac_dev->work); | ||
395 | if (status == 0) { | ||
396 | /* workq instance might be running, wait for it */ | ||
397 | flush_workqueue(edac_workqueue); | ||
398 | } | ||
399 | } | ||
400 | |||
401 | /* | ||
402 | * edac_device_reset_delay_period | ||
403 | */ | ||
404 | |||
405 | void edac_device_reset_delay_period( | ||
406 | struct edac_device_ctl_info *edac_dev, | ||
407 | unsigned long value) | ||
408 | { | ||
409 | lock_device_list(); | ||
410 | |||
411 | /* cancel the current workq request */ | ||
412 | edac_workq_teardown(edac_dev); | ||
413 | |||
414 | /* restart the workq request, with new delay value */ | ||
415 | edac_workq_setup(edac_dev, value); | ||
416 | |||
417 | unlock_device_list(); | ||
418 | } | ||
419 | |||
420 | /* | ||
421 | * edac_op_state_toString(edac_dev) | ||
422 | */ | ||
423 | static char *edac_op_state_toString(struct edac_device_ctl_info *edac_dev) | ||
424 | { | ||
425 | int opstate = edac_dev->op_state; | ||
426 | |||
427 | if (opstate == OP_RUNNING_POLL) | ||
428 | return "POLLED"; | ||
429 | else if (opstate == OP_RUNNING_INTERRUPT) | ||
430 | return "INTERRUPT"; | ||
431 | else if (opstate == OP_RUNNING_POLL_INTR) | ||
432 | return "POLL-INTR"; | ||
433 | else if (opstate == OP_ALLOC) | ||
434 | return "ALLOC"; | ||
435 | else if (opstate == OP_OFFLINE) | ||
436 | return "OFFLINE"; | ||
437 | |||
438 | return "UNKNOWN"; | ||
439 | } | ||
440 | |||
441 | /** | ||
442 | * edac_device_add_device: Insert the 'edac_dev' structure into the | ||
443 | * edac_device global list and create sysfs entries associated with | ||
444 | * edac_device structure. | ||
445 | * @edac_device: pointer to the edac_device structure to be added to the list | ||
446 | * @edac_idx: A unique numeric identifier to be assigned to the | ||
447 | * 'edac_device' structure. | ||
448 | * | ||
449 | * Return: | ||
450 | * 0 Success | ||
451 | * !0 Failure | ||
452 | */ | ||
453 | int edac_device_add_device(struct edac_device_ctl_info *edac_dev, int edac_idx) | ||
454 | { | ||
455 | debugf0("%s()\n", __func__); | ||
456 | |||
457 | edac_dev->dev_idx = edac_idx; | ||
458 | #ifdef CONFIG_EDAC_DEBUG | ||
459 | if (edac_debug_level >= 3) | ||
460 | edac_device_dump_device(edac_dev); | ||
461 | #endif | ||
462 | lock_device_list(); | ||
463 | |||
464 | if (add_edac_dev_to_global_list(edac_dev)) | ||
465 | goto fail0; | ||
466 | |||
467 | /* set load time so that error rate can be tracked */ | ||
468 | edac_dev->start_time = jiffies; | ||
469 | |||
470 | /* create this instance's sysfs entries */ | ||
471 | if (edac_device_create_sysfs(edac_dev)) { | ||
472 | edac_device_printk(edac_dev, KERN_WARNING, | ||
473 | "failed to create sysfs device\n"); | ||
474 | goto fail1; | ||
475 | } | ||
476 | |||
477 | /* If there IS a check routine, then we are running POLLED */ | ||
478 | if (edac_dev->edac_check != NULL) { | ||
479 | /* This instance is NOW RUNNING */ | ||
480 | edac_dev->op_state = OP_RUNNING_POLL; | ||
481 | |||
482 | /* enable workq processing on this instance, default = 1000 msec */ | ||
483 | edac_workq_setup(edac_dev, 1000); | ||
484 | } else { | ||
485 | edac_dev->op_state = OP_RUNNING_INTERRUPT; | ||
486 | } | ||
487 | |||
488 | |||
489 | /* Report action taken */ | ||
490 | edac_device_printk(edac_dev, KERN_INFO, | ||
491 | "Giving out device to module '%s' controller '%s': DEV '%s' (%s)\n", | ||
492 | edac_dev->mod_name, | ||
493 | edac_dev->ctl_name, | ||
494 | dev_name(edac_dev->dev), | ||
495 | edac_op_state_toString(edac_dev) | ||
496 | ); | ||
497 | |||
498 | unlock_device_list(); | ||
499 | return 0; | ||
500 | |||
501 | fail1: | ||
502 | /* Some error, so remove the entry from the lsit */ | ||
503 | del_edac_device_from_global_list(edac_dev); | ||
504 | |||
505 | fail0: | ||
506 | unlock_device_list(); | ||
507 | return 1; | ||
508 | } | ||
509 | EXPORT_SYMBOL_GPL(edac_device_add_device); | ||
510 | |||
511 | /** | ||
512 | * edac_device_del_device: | ||
513 | * Remove sysfs entries for specified edac_device structure and | ||
514 | * then remove edac_device structure from global list | ||
515 | * | ||
516 | * @pdev: | ||
517 | * Pointer to 'struct device' representing edac_device | ||
518 | * structure to remove. | ||
519 | * | ||
520 | * Return: | ||
521 | * Pointer to removed edac_device structure, | ||
522 | * OR NULL if device not found. | ||
523 | */ | ||
524 | struct edac_device_ctl_info * edac_device_del_device(struct device *dev) | ||
525 | { | ||
526 | struct edac_device_ctl_info *edac_dev; | ||
527 | |||
528 | debugf0("MC: %s()\n", __func__); | ||
529 | |||
530 | lock_device_list(); | ||
531 | |||
532 | if ((edac_dev = find_edac_device_by_dev(dev)) == NULL) { | ||
533 | unlock_device_list(); | ||
534 | return NULL; | ||
535 | } | ||
536 | |||
537 | /* mark this instance as OFFLINE */ | ||
538 | edac_dev->op_state = OP_OFFLINE; | ||
539 | |||
540 | /* clear workq processing on this instance */ | ||
541 | edac_workq_teardown(edac_dev); | ||
542 | |||
543 | /* Tear down the sysfs entries for this instance */ | ||
544 | edac_device_remove_sysfs(edac_dev); | ||
545 | |||
546 | /* deregister from global list */ | ||
547 | del_edac_device_from_global_list(edac_dev); | ||
548 | |||
549 | unlock_device_list(); | ||
550 | |||
551 | edac_printk(KERN_INFO, EDAC_MC, | ||
552 | "Removed device %d for %s %s: DEV %s\n", | ||
553 | edac_dev->dev_idx, | ||
554 | edac_dev->mod_name, | ||
555 | edac_dev->ctl_name, | ||
556 | dev_name(edac_dev->dev)); | ||
557 | |||
558 | return edac_dev; | ||
559 | } | ||
560 | EXPORT_SYMBOL_GPL(edac_device_del_device); | ||
561 | |||
562 | |||
563 | static inline int edac_device_get_log_ce(struct edac_device_ctl_info *edac_dev) | ||
564 | { | ||
565 | return edac_dev->log_ce; | ||
566 | } | ||
567 | |||
568 | static inline int edac_device_get_log_ue(struct edac_device_ctl_info *edac_dev) | ||
569 | { | ||
570 | return edac_dev->log_ue; | ||
571 | } | ||
572 | |||
573 | static inline int edac_device_get_panic_on_ue( | ||
574 | struct edac_device_ctl_info *edac_dev) | ||
575 | { | ||
576 | return edac_dev->panic_on_ue; | ||
577 | } | ||
578 | |||
579 | /* | ||
580 | * edac_device_handle_ce | ||
581 | * perform a common output and handling of an 'edac_dev' CE event | ||
582 | */ | ||
583 | void edac_device_handle_ce(struct edac_device_ctl_info *edac_dev, | ||
584 | int inst_nr, int block_nr, const char *msg) | ||
585 | { | ||
586 | struct edac_device_instance *instance; | ||
587 | struct edac_device_block *block = NULL; | ||
588 | |||
589 | if ((inst_nr >= edac_dev->nr_instances) || (inst_nr < 0)) { | ||
590 | edac_device_printk(edac_dev, KERN_ERR, | ||
591 | "INTERNAL ERROR: 'instance' out of range " | ||
592 | "(%d >= %d)\n", inst_nr, edac_dev->nr_instances); | ||
593 | return; | ||
594 | } | ||
595 | |||
596 | instance = edac_dev->instances + inst_nr; | ||
597 | |||
598 | if ((block_nr >= instance->nr_blocks) || (block_nr < 0)) { | ||
599 | edac_device_printk(edac_dev, KERN_ERR, | ||
600 | "INTERNAL ERROR: instance %d 'block' out of range " | ||
601 | "(%d >= %d)\n", inst_nr, block_nr, instance->nr_blocks); | ||
602 | return; | ||
603 | } | ||
604 | |||
605 | if (instance->nr_blocks > 0) { | ||
606 | block = instance->blocks + block_nr; | ||
607 | block->counters.ce_count++; | ||
608 | } | ||
609 | |||
610 | /* Propogate the count up the 'totals' tree */ | ||
611 | instance->counters.ce_count++; | ||
612 | edac_dev->counters.ce_count++; | ||
613 | |||
614 | if (edac_device_get_log_ce(edac_dev)) | ||
615 | edac_device_printk(edac_dev, KERN_WARNING, | ||
616 | "CE ctl: %s, instance: %s, block: %s: %s\n", | ||
617 | edac_dev->ctl_name, instance->name, | ||
618 | block ? block->name : "N/A", msg); | ||
619 | } | ||
620 | EXPORT_SYMBOL_GPL(edac_device_handle_ce); | ||
621 | |||
622 | /* | ||
623 | * edac_device_handle_ue | ||
624 | * perform a common output and handling of an 'edac_dev' UE event | ||
625 | */ | ||
626 | void edac_device_handle_ue(struct edac_device_ctl_info *edac_dev, | ||
627 | int inst_nr, int block_nr, const char *msg) | ||
628 | { | ||
629 | struct edac_device_instance *instance; | ||
630 | struct edac_device_block *block = NULL; | ||
631 | |||
632 | if ((inst_nr >= edac_dev->nr_instances) || (inst_nr < 0)) { | ||
633 | edac_device_printk(edac_dev, KERN_ERR, | ||
634 | "INTERNAL ERROR: 'instance' out of range " | ||
635 | "(%d >= %d)\n", inst_nr, edac_dev->nr_instances); | ||
636 | return; | ||
637 | } | ||
638 | |||
639 | instance = edac_dev->instances + inst_nr; | ||
640 | |||
641 | if ((block_nr >= instance->nr_blocks) || (block_nr < 0)) { | ||
642 | edac_device_printk(edac_dev, KERN_ERR, | ||
643 | "INTERNAL ERROR: instance %d 'block' out of range " | ||
644 | "(%d >= %d)\n", inst_nr, block_nr, instance->nr_blocks); | ||
645 | return; | ||
646 | } | ||
647 | |||
648 | if (instance->nr_blocks > 0) { | ||
649 | block = instance->blocks + block_nr; | ||
650 | block->counters.ue_count++; | ||
651 | } | ||
652 | |||
653 | /* Propogate the count up the 'totals' tree */ | ||
654 | instance->counters.ue_count++; | ||
655 | edac_dev->counters.ue_count++; | ||
656 | |||
657 | if (edac_device_get_log_ue(edac_dev)) | ||
658 | edac_device_printk(edac_dev, KERN_EMERG, | ||
659 | "UE ctl: %s, instance: %s, block: %s: %s\n", | ||
660 | edac_dev->ctl_name, instance->name, | ||
661 | block ? block->name : "N/A", msg); | ||
662 | |||
663 | if (edac_device_get_panic_on_ue(edac_dev)) | ||
664 | panic("EDAC %s: UE instance: %s, block %s: %s\n", | ||
665 | edac_dev->ctl_name, instance->name, | ||
666 | block ? block->name : "N/A", msg); | ||
667 | } | ||
668 | EXPORT_SYMBOL_GPL(edac_device_handle_ue); | ||
669 | |||
diff --git a/drivers/edac/edac_device_sysfs.c b/drivers/edac/edac_device_sysfs.c new file mode 100644 index 000000000000..afb190502646 --- /dev/null +++ b/drivers/edac/edac_device_sysfs.c | |||
@@ -0,0 +1,837 @@ | |||
1 | /* | ||
2 | * file for managing the edac_device class of devices for EDAC | ||
3 | * | ||
4 | * (C) 2007 SoftwareBitMaker(http://www.softwarebitmaker.com) | ||
5 | * This file may be distributed under the terms of the | ||
6 | * GNU General Public License. | ||
7 | * | ||
8 | * Written Doug Thompson <norsk5@xmission.com> | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/sysdev.h> | ||
14 | #include <linux/ctype.h> | ||
15 | |||
16 | #include "edac_core.h" | ||
17 | #include "edac_module.h" | ||
18 | |||
19 | |||
20 | #define EDAC_DEVICE_SYMLINK "device" | ||
21 | |||
22 | #define to_edacdev(k) container_of(k, struct edac_device_ctl_info, kobj) | ||
23 | #define to_edacdev_attr(a) container_of(a, struct edacdev_attribute, attr) | ||
24 | |||
25 | #ifdef DKT | ||
26 | |||
27 | static ssize_t edac_dev_ue_count_show(struct edac_device_ctl_info *edac_dev, | ||
28 | char *data) | ||
29 | { | ||
30 | return sprintf(data,"%d\n", edac_dev->ue_count); | ||
31 | } | ||
32 | |||
33 | static ssize_t edac_dev_ce_count_show(struct edac_device_ctl_info *edac_dev, | ||
34 | char *data) | ||
35 | { | ||
36 | return sprintf(data,"%d\n", edac_dev->ce_count); | ||
37 | } | ||
38 | |||
39 | static ssize_t edac_dev_seconds_show(struct edac_device_ctl_info *edac_dev, | ||
40 | char *data) | ||
41 | { | ||
42 | return sprintf(data,"%ld\n", (jiffies - edac_dev->start_time) / HZ); | ||
43 | } | ||
44 | |||
45 | static ssize_t edac_dev_ctl_name_show(struct edac_device_ctl_info *edac_dev, | ||
46 | char *data) | ||
47 | { | ||
48 | return sprintf(data,"%s\n", edac_dev->ctl_name); | ||
49 | } | ||
50 | |||
51 | |||
52 | struct edacdev_attribute { | ||
53 | struct attribute attr; | ||
54 | ssize_t (*show)(struct edac_device_ctl_info *,char *); | ||
55 | ssize_t (*store)(struct edac_device_ctl_info *, const char *,size_t); | ||
56 | }; | ||
57 | |||
58 | |||
59 | /* EDAC DEVICE show/store functions for top most object */ | ||
60 | static ssize_t edacdev_show(struct kobject *kobj, struct attribute *attr, | ||
61 | char *buffer) | ||
62 | { | ||
63 | struct edac_device_ctl_info *edac_dev = to_edacdev(kobj); | ||
64 | struct edacdev_attribute * edacdev_attr = to_edacdev_attr(attr); | ||
65 | |||
66 | if (edacdev_attr->show) | ||
67 | return edacdev_attr->show(edac_dev, buffer); | ||
68 | |||
69 | return -EIO; | ||
70 | } | ||
71 | |||
72 | static ssize_t edacdev_store(struct kobject *kobj, struct attribute *attr, | ||
73 | const char *buffer, size_t count) | ||
74 | { | ||
75 | struct edac_device_ctl_info *edac_dev = to_edacdev(kobj); | ||
76 | struct edacdev_attribute * edacdev_attr = to_edacdev_attr(attr); | ||
77 | |||
78 | if (edacdev_attr->store) | ||
79 | return edacdev_attr->store(edac_dev, buffer, count); | ||
80 | |||
81 | return -EIO; | ||
82 | } | ||
83 | |||
84 | static struct sysfs_ops edac_dev_ops = { | ||
85 | .show = edacdev_show, | ||
86 | .store = edacdev_store | ||
87 | }; | ||
88 | |||
89 | #define EDACDEV_ATTR(_name,_mode,_show,_store) \ | ||
90 | static struct edacdev_attribute edac_dev_attr_##_name = { \ | ||
91 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
92 | .show = _show, \ | ||
93 | .store = _store, \ | ||
94 | }; | ||
95 | |||
96 | /* default Control file */ | ||
97 | EDACDEV_ATTR(reset_counters,S_IWUSR,NULL,edac_dev_reset_counters_store); | ||
98 | |||
99 | /* default Attribute files */ | ||
100 | EDACDEV_ATTR(mc_name,S_IRUGO,edac_dev_ctl_name_show,NULL); | ||
101 | EDACDEV_ATTR(seconds_since_reset,S_IRUGO,edac_dev_seconds_show,NULL); | ||
102 | EDACDEV_ATTR(ue_count,S_IRUGO,edac_dev_ue_count_show,NULL); | ||
103 | EDACDEV_ATTR(ce_count,S_IRUGO,edac_dev_ce_count_show,NULL); | ||
104 | |||
105 | |||
106 | static struct edacdev_attribute *edacdev_attr[] = { | ||
107 | &edacdev_attr_reset_counters, | ||
108 | &edacdev_attr_mc_name, | ||
109 | &edacdev_attr_seconds_since_reset, | ||
110 | &edacdev_attr_ue_count, | ||
111 | &edacdev_attr_ce_count, | ||
112 | NULL | ||
113 | }; | ||
114 | |||
115 | /* | ||
116 | * Release of a Edac Device controlling instance | ||
117 | */ | ||
118 | static void edac_dev_instance_release(struct kobject *kobj) | ||
119 | { | ||
120 | struct edac_device_ctl_info *edac_dev; | ||
121 | |||
122 | edac_dev = to_edacdev(kobj); | ||
123 | debugf0("%s() idx=%d\n", __func__, edac_dev->dev_idx); | ||
124 | complete(&edac_dev->kobj_complete); | ||
125 | } | ||
126 | |||
127 | static struct kobj_type ktype_device = { | ||
128 | .release = edac_dev_instance_release, | ||
129 | .sysfs_ops = &edacdev_ops, | ||
130 | .default_attrs = (struct attribute **) edacdev_attr, | ||
131 | }; | ||
132 | |||
133 | #endif | ||
134 | |||
135 | /************************** edac_device sysfs code and data **************/ | ||
136 | |||
137 | /* | ||
138 | * Set of edac_device_ctl_info attribute store/show functions | ||
139 | */ | ||
140 | |||
141 | /* 'log_ue' */ | ||
142 | static ssize_t edac_device_ctl_log_ue_show( | ||
143 | struct edac_device_ctl_info *ctl_info, char *data) | ||
144 | { | ||
145 | return sprintf(data,"%u\n", ctl_info->log_ue); | ||
146 | } | ||
147 | |||
148 | static ssize_t edac_device_ctl_log_ue_store( | ||
149 | struct edac_device_ctl_info *ctl_info, | ||
150 | const char *data,size_t count) | ||
151 | { | ||
152 | /* if parameter is zero, turn off flag, if non-zero turn on flag */ | ||
153 | ctl_info->log_ue = (simple_strtoul(data,NULL,0) != 0); | ||
154 | |||
155 | return count; | ||
156 | } | ||
157 | |||
158 | /* 'log_ce' */ | ||
159 | static ssize_t edac_device_ctl_log_ce_show( | ||
160 | struct edac_device_ctl_info *ctl_info,char *data) | ||
161 | { | ||
162 | return sprintf(data,"%u\n", ctl_info->log_ce); | ||
163 | } | ||
164 | |||
165 | static ssize_t edac_device_ctl_log_ce_store( | ||
166 | struct edac_device_ctl_info *ctl_info, | ||
167 | const char *data,size_t count) | ||
168 | { | ||
169 | /* if parameter is zero, turn off flag, if non-zero turn on flag */ | ||
170 | ctl_info->log_ce = (simple_strtoul(data,NULL,0) != 0); | ||
171 | |||
172 | return count; | ||
173 | } | ||
174 | |||
175 | |||
176 | /* 'panic_on_ue' */ | ||
177 | static ssize_t edac_device_ctl_panic_on_ue_show( | ||
178 | struct edac_device_ctl_info *ctl_info, char *data) | ||
179 | { | ||
180 | return sprintf(data,"%u\n", ctl_info->panic_on_ue); | ||
181 | } | ||
182 | |||
183 | static ssize_t edac_device_ctl_panic_on_ue_store( | ||
184 | struct edac_device_ctl_info *ctl_info, | ||
185 | const char *data,size_t count) | ||
186 | { | ||
187 | /* if parameter is zero, turn off flag, if non-zero turn on flag */ | ||
188 | ctl_info->panic_on_ue = (simple_strtoul(data,NULL,0) != 0); | ||
189 | |||
190 | return count; | ||
191 | } | ||
192 | |||
193 | /* 'poll_msec' show and store functions*/ | ||
194 | static ssize_t edac_device_ctl_poll_msec_show( | ||
195 | struct edac_device_ctl_info *ctl_info, char *data) | ||
196 | { | ||
197 | return sprintf(data,"%u\n", ctl_info->poll_msec); | ||
198 | } | ||
199 | |||
200 | static ssize_t edac_device_ctl_poll_msec_store( | ||
201 | struct edac_device_ctl_info *ctl_info, | ||
202 | const char *data,size_t count) | ||
203 | { | ||
204 | unsigned long value; | ||
205 | |||
206 | /* get the value and enforce that it is non-zero, must be at least | ||
207 | * one millisecond for the delay period, between scans | ||
208 | * Then cancel last outstanding delay for the work request | ||
209 | * and set a new one. | ||
210 | */ | ||
211 | value = simple_strtoul(data,NULL,0); | ||
212 | edac_device_reset_delay_period(ctl_info,value); | ||
213 | |||
214 | return count; | ||
215 | } | ||
216 | |||
217 | |||
218 | /* edac_device_ctl_info specific attribute structure */ | ||
219 | struct ctl_info_attribute { | ||
220 | struct attribute attr; | ||
221 | ssize_t (*show)(struct edac_device_ctl_info *,char *); | ||
222 | ssize_t (*store)(struct edac_device_ctl_info *,const char *,size_t); | ||
223 | }; | ||
224 | |||
225 | #define to_ctl_info(k) container_of(k, struct edac_device_ctl_info, kobj) | ||
226 | #define to_ctl_info_attr(a) container_of(a,struct ctl_info_attribute,attr) | ||
227 | |||
228 | /* Function to 'show' fields from the edac_dev 'ctl_info' structure */ | ||
229 | static ssize_t edac_dev_ctl_info_show(struct kobject *kobj, | ||
230 | struct attribute *attr, | ||
231 | char *buffer) | ||
232 | { | ||
233 | struct edac_device_ctl_info *edac_dev = to_ctl_info(kobj); | ||
234 | struct ctl_info_attribute *ctl_info_attr = to_ctl_info_attr(attr); | ||
235 | |||
236 | if (ctl_info_attr->show) | ||
237 | return ctl_info_attr->show(edac_dev,buffer); | ||
238 | return -EIO; | ||
239 | } | ||
240 | |||
241 | /* Function to 'store' fields into the edac_dev 'ctl_info' structure */ | ||
242 | static ssize_t edac_dev_ctl_info_store(struct kobject *kobj, | ||
243 | struct attribute *attr, | ||
244 | const char *buffer, size_t count) | ||
245 | { | ||
246 | struct edac_device_ctl_info *edac_dev = to_ctl_info(kobj); | ||
247 | struct ctl_info_attribute *ctl_info_attr = to_ctl_info_attr(attr); | ||
248 | |||
249 | if (ctl_info_attr->store) | ||
250 | return ctl_info_attr->store(edac_dev, buffer, count); | ||
251 | return -EIO; | ||
252 | } | ||
253 | |||
254 | /* edac_dev file operations for an 'ctl_info' */ | ||
255 | static struct sysfs_ops device_ctl_info_ops = { | ||
256 | .show = edac_dev_ctl_info_show, | ||
257 | .store = edac_dev_ctl_info_store | ||
258 | }; | ||
259 | |||
260 | #define CTL_INFO_ATTR(_name,_mode,_show,_store) \ | ||
261 | static struct ctl_info_attribute attr_ctl_info_##_name = { \ | ||
262 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
263 | .show = _show, \ | ||
264 | .store = _store, \ | ||
265 | }; | ||
266 | |||
267 | |||
268 | /* Declare the various ctl_info attributes here and their respective ops */ | ||
269 | CTL_INFO_ATTR(log_ue,S_IRUGO|S_IWUSR, | ||
270 | edac_device_ctl_log_ue_show, | ||
271 | edac_device_ctl_log_ue_store); | ||
272 | CTL_INFO_ATTR(log_ce,S_IRUGO|S_IWUSR, | ||
273 | edac_device_ctl_log_ce_show, | ||
274 | edac_device_ctl_log_ce_store); | ||
275 | CTL_INFO_ATTR(panic_on_ue,S_IRUGO|S_IWUSR, | ||
276 | edac_device_ctl_panic_on_ue_show, | ||
277 | edac_device_ctl_panic_on_ue_store); | ||
278 | CTL_INFO_ATTR(poll_msec,S_IRUGO|S_IWUSR, | ||
279 | edac_device_ctl_poll_msec_show, | ||
280 | edac_device_ctl_poll_msec_store); | ||
281 | |||
282 | |||
283 | /* Base Attributes of the EDAC_DEVICE ECC object */ | ||
284 | static struct ctl_info_attribute *device_ctrl_attr[] = { | ||
285 | &attr_ctl_info_panic_on_ue, | ||
286 | &attr_ctl_info_log_ue, | ||
287 | &attr_ctl_info_log_ce, | ||
288 | &attr_ctl_info_poll_msec, | ||
289 | NULL, | ||
290 | }; | ||
291 | |||
292 | /* Main DEVICE kobject release() function */ | ||
293 | static void edac_device_ctrl_master_release(struct kobject *kobj) | ||
294 | { | ||
295 | struct edac_device_ctl_info *edac_dev; | ||
296 | |||
297 | edac_dev = to_edacdev(kobj); | ||
298 | |||
299 | debugf1("%s()\n", __func__); | ||
300 | complete(&edac_dev->kobj_complete); | ||
301 | } | ||
302 | |||
303 | static struct kobj_type ktype_device_ctrl = { | ||
304 | .release = edac_device_ctrl_master_release, | ||
305 | .sysfs_ops = &device_ctl_info_ops, | ||
306 | .default_attrs = (struct attribute **) device_ctrl_attr, | ||
307 | }; | ||
308 | |||
309 | |||
310 | /**************** edac_device main kobj ctor/dtor code *********************/ | ||
311 | |||
312 | /* | ||
313 | * edac_device_register_main_kobj | ||
314 | * | ||
315 | * perform the high level setup for the new edac_device instance | ||
316 | * | ||
317 | * Return: 0 SUCCESS | ||
318 | * !0 FAILURE | ||
319 | */ | ||
320 | static int edac_device_register_main_kobj( | ||
321 | struct edac_device_ctl_info *edac_dev) | ||
322 | { | ||
323 | int err = 0; | ||
324 | struct sysdev_class *edac_class; | ||
325 | |||
326 | debugf1("%s()\n", __func__); | ||
327 | |||
328 | /* get the /sys/devices/system/edac reference */ | ||
329 | edac_class = edac_get_edac_class(); | ||
330 | if (edac_class == NULL) { | ||
331 | debugf1("%s() no edac_class error=%d\n", __func__, err); | ||
332 | return err; | ||
333 | } | ||
334 | |||
335 | /* Point to the 'edac_class' this instance 'reports' to */ | ||
336 | edac_dev->edac_class = edac_class; | ||
337 | |||
338 | /* Init the devices's kobject */ | ||
339 | memset(&edac_dev->kobj, 0, sizeof (struct kobject)); | ||
340 | edac_dev->kobj.ktype = &ktype_device_ctrl; | ||
341 | |||
342 | /* set this new device under the edac_class kobject */ | ||
343 | edac_dev->kobj.parent = &edac_class->kset.kobj; | ||
344 | |||
345 | /* generate sysfs "..../edac/<name>" */ | ||
346 | debugf1("%s() set name of kobject to: %s\n", | ||
347 | __func__, edac_dev->name); | ||
348 | err = kobject_set_name(&edac_dev->kobj,"%s",edac_dev->name); | ||
349 | if (err) | ||
350 | return err; | ||
351 | err = kobject_register(&edac_dev->kobj); | ||
352 | if (err) { | ||
353 | debugf1("%s()Failed to register '.../edac/%s'\n", | ||
354 | __func__,edac_dev->name); | ||
355 | return err; | ||
356 | } | ||
357 | |||
358 | debugf1("%s() Registered '.../edac/%s' kobject\n", | ||
359 | __func__, edac_dev->name); | ||
360 | |||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | /* | ||
365 | * edac_device_unregister_main_kobj: | ||
366 | * the '..../edac/<name>' kobject | ||
367 | */ | ||
368 | static void edac_device_unregister_main_kobj( | ||
369 | struct edac_device_ctl_info *edac_dev) | ||
370 | { | ||
371 | debugf0("%s()\n", __func__); | ||
372 | debugf1("%s() name of kobject is: %s\n", | ||
373 | __func__, kobject_name(&edac_dev->kobj)); | ||
374 | |||
375 | init_completion(&edac_dev->kobj_complete); | ||
376 | |||
377 | /* | ||
378 | * Unregister the edac device's kobject and | ||
379 | * wait for reference count to reach 0. | ||
380 | */ | ||
381 | kobject_unregister(&edac_dev->kobj); | ||
382 | wait_for_completion(&edac_dev->kobj_complete); | ||
383 | } | ||
384 | |||
385 | |||
386 | /*************** edac_dev -> instance information ***********/ | ||
387 | |||
388 | /* | ||
389 | * Set of low-level instance attribute show functions | ||
390 | */ | ||
391 | static ssize_t instance_ue_count_show( | ||
392 | struct edac_device_instance *instance, char *data) | ||
393 | { | ||
394 | return sprintf(data,"%u\n", instance->counters.ue_count); | ||
395 | } | ||
396 | |||
397 | static ssize_t instance_ce_count_show( | ||
398 | struct edac_device_instance *instance, char *data) | ||
399 | { | ||
400 | return sprintf(data,"%u\n", instance->counters.ce_count); | ||
401 | } | ||
402 | |||
403 | |||
404 | |||
405 | #define to_instance(k) container_of(k, struct edac_device_instance, kobj) | ||
406 | #define to_instance_attr(a) container_of(a,struct instance_attribute,attr) | ||
407 | |||
408 | /* DEVICE instance kobject release() function */ | ||
409 | static void edac_device_ctrl_instance_release(struct kobject *kobj) | ||
410 | { | ||
411 | struct edac_device_instance *instance; | ||
412 | |||
413 | debugf1("%s()\n", __func__); | ||
414 | |||
415 | instance = to_instance(kobj); | ||
416 | complete(&instance->kobj_complete); | ||
417 | } | ||
418 | |||
419 | |||
420 | /* instance specific attribute structure */ | ||
421 | struct instance_attribute { | ||
422 | struct attribute attr; | ||
423 | ssize_t (*show)(struct edac_device_instance *,char *); | ||
424 | ssize_t (*store)(struct edac_device_instance *,const char *,size_t); | ||
425 | }; | ||
426 | |||
427 | |||
428 | /* Function to 'show' fields from the edac_dev 'instance' structure */ | ||
429 | static ssize_t edac_dev_instance_show(struct kobject *kobj, | ||
430 | struct attribute *attr, | ||
431 | char *buffer) | ||
432 | { | ||
433 | struct edac_device_instance *instance = to_instance(kobj); | ||
434 | struct instance_attribute *instance_attr = to_instance_attr(attr); | ||
435 | |||
436 | if (instance_attr->show) | ||
437 | return instance_attr->show(instance,buffer); | ||
438 | return -EIO; | ||
439 | } | ||
440 | |||
441 | |||
442 | /* Function to 'store' fields into the edac_dev 'instance' structure */ | ||
443 | static ssize_t edac_dev_instance_store(struct kobject *kobj, | ||
444 | struct attribute *attr, | ||
445 | const char *buffer, size_t count) | ||
446 | { | ||
447 | struct edac_device_instance *instance = to_instance(kobj); | ||
448 | struct instance_attribute *instance_attr = to_instance_attr(attr); | ||
449 | |||
450 | if (instance_attr->store) | ||
451 | return instance_attr->store(instance, buffer, count); | ||
452 | return -EIO; | ||
453 | } | ||
454 | |||
455 | |||
456 | |||
457 | /* edac_dev file operations for an 'instance' */ | ||
458 | static struct sysfs_ops device_instance_ops = { | ||
459 | .show = edac_dev_instance_show, | ||
460 | .store = edac_dev_instance_store | ||
461 | }; | ||
462 | |||
463 | #define INSTANCE_ATTR(_name,_mode,_show,_store) \ | ||
464 | static struct instance_attribute attr_instance_##_name = { \ | ||
465 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
466 | .show = _show, \ | ||
467 | .store = _store, \ | ||
468 | }; | ||
469 | |||
470 | /* | ||
471 | * Define attributes visible for the edac_device instance object | ||
472 | * Each contains a pointer to a show and an optional set | ||
473 | * function pointer that does the low level output/input | ||
474 | */ | ||
475 | INSTANCE_ATTR(ce_count,S_IRUGO,instance_ce_count_show,NULL); | ||
476 | INSTANCE_ATTR(ue_count,S_IRUGO,instance_ue_count_show,NULL); | ||
477 | |||
478 | /* list of edac_dev 'instance' attributes */ | ||
479 | static struct instance_attribute *device_instance_attr[] = { | ||
480 | &attr_instance_ce_count, | ||
481 | &attr_instance_ue_count, | ||
482 | NULL, | ||
483 | }; | ||
484 | |||
485 | /* The 'ktype' for each edac_dev 'instance' */ | ||
486 | static struct kobj_type ktype_instance_ctrl = { | ||
487 | .release = edac_device_ctrl_instance_release, | ||
488 | .sysfs_ops = &device_instance_ops, | ||
489 | .default_attrs = (struct attribute **) device_instance_attr, | ||
490 | }; | ||
491 | |||
492 | |||
493 | /*************** edac_dev -> instance -> block information *********/ | ||
494 | |||
495 | /* | ||
496 | * Set of low-level block attribute show functions | ||
497 | */ | ||
498 | static ssize_t block_ue_count_show( | ||
499 | struct edac_device_block *block, char *data) | ||
500 | { | ||
501 | return sprintf(data,"%u\n", block->counters.ue_count); | ||
502 | } | ||
503 | |||
504 | static ssize_t block_ce_count_show( | ||
505 | struct edac_device_block *block, char *data) | ||
506 | { | ||
507 | return sprintf(data,"%u\n", block->counters.ce_count); | ||
508 | } | ||
509 | |||
510 | |||
511 | |||
512 | #define to_block(k) container_of(k, struct edac_device_block, kobj) | ||
513 | #define to_block_attr(a) container_of(a,struct block_attribute,attr) | ||
514 | |||
515 | /* DEVICE block kobject release() function */ | ||
516 | static void edac_device_ctrl_block_release(struct kobject *kobj) | ||
517 | { | ||
518 | struct edac_device_block *block; | ||
519 | |||
520 | debugf1("%s()\n", __func__); | ||
521 | |||
522 | block = to_block(kobj); | ||
523 | complete(&block->kobj_complete); | ||
524 | } | ||
525 | |||
526 | /* block specific attribute structure */ | ||
527 | struct block_attribute { | ||
528 | struct attribute attr; | ||
529 | ssize_t (*show)(struct edac_device_block *,char *); | ||
530 | ssize_t (*store)(struct edac_device_block *,const char *,size_t); | ||
531 | }; | ||
532 | |||
533 | /* Function to 'show' fields from the edac_dev 'block' structure */ | ||
534 | static ssize_t edac_dev_block_show(struct kobject *kobj, | ||
535 | struct attribute *attr, | ||
536 | char *buffer) | ||
537 | { | ||
538 | struct edac_device_block *block = to_block(kobj); | ||
539 | struct block_attribute *block_attr = to_block_attr(attr); | ||
540 | |||
541 | if (block_attr->show) | ||
542 | return block_attr->show(block,buffer); | ||
543 | return -EIO; | ||
544 | } | ||
545 | |||
546 | |||
547 | /* Function to 'store' fields into the edac_dev 'block' structure */ | ||
548 | static ssize_t edac_dev_block_store(struct kobject *kobj, | ||
549 | struct attribute *attr, | ||
550 | const char *buffer, size_t count) | ||
551 | { | ||
552 | struct edac_device_block *block = to_block(kobj); | ||
553 | struct block_attribute *block_attr = to_block_attr(attr); | ||
554 | |||
555 | if (block_attr->store) | ||
556 | return block_attr->store(block, buffer, count); | ||
557 | return -EIO; | ||
558 | } | ||
559 | |||
560 | |||
561 | /* edac_dev file operations for a 'block' */ | ||
562 | static struct sysfs_ops device_block_ops = { | ||
563 | .show = edac_dev_block_show, | ||
564 | .store = edac_dev_block_store | ||
565 | }; | ||
566 | |||
567 | |||
568 | #define BLOCK_ATTR(_name,_mode,_show,_store) \ | ||
569 | static struct block_attribute attr_block_##_name = { \ | ||
570 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
571 | .show = _show, \ | ||
572 | .store = _store, \ | ||
573 | }; | ||
574 | |||
575 | BLOCK_ATTR(ce_count,S_IRUGO,block_ce_count_show,NULL); | ||
576 | BLOCK_ATTR(ue_count,S_IRUGO,block_ue_count_show,NULL); | ||
577 | |||
578 | |||
579 | /* list of edac_dev 'block' attributes */ | ||
580 | static struct block_attribute *device_block_attr[] = { | ||
581 | &attr_block_ce_count, | ||
582 | &attr_block_ue_count, | ||
583 | NULL, | ||
584 | }; | ||
585 | |||
586 | /* The 'ktype' for each edac_dev 'block' */ | ||
587 | static struct kobj_type ktype_block_ctrl = { | ||
588 | .release = edac_device_ctrl_block_release, | ||
589 | .sysfs_ops = &device_block_ops, | ||
590 | .default_attrs = (struct attribute **) device_block_attr, | ||
591 | }; | ||
592 | |||
593 | |||
594 | /************** block ctor/dtor code ************/ | ||
595 | |||
596 | /* | ||
597 | * edac_device_create_block | ||
598 | */ | ||
599 | static int edac_device_create_block( | ||
600 | struct edac_device_ctl_info *edac_dev, | ||
601 | struct edac_device_instance *instance, | ||
602 | int idx) | ||
603 | { | ||
604 | int err; | ||
605 | struct edac_device_block *block; | ||
606 | |||
607 | block = &instance->blocks[idx]; | ||
608 | |||
609 | debugf1("%s() Instance '%s' block[%d] '%s'\n", | ||
610 | __func__,instance->name, idx, block->name); | ||
611 | |||
612 | /* init this block's kobject */ | ||
613 | memset(&block->kobj, 0, sizeof (struct kobject)); | ||
614 | block->kobj.parent = &instance->kobj; | ||
615 | block->kobj.ktype = &ktype_block_ctrl; | ||
616 | |||
617 | err = kobject_set_name(&block->kobj,"%s",block->name); | ||
618 | if (err) | ||
619 | return err; | ||
620 | |||
621 | err = kobject_register(&block->kobj); | ||
622 | if (err) { | ||
623 | debugf1("%s()Failed to register instance '%s'\n", | ||
624 | __func__,block->name); | ||
625 | return err; | ||
626 | } | ||
627 | |||
628 | return 0; | ||
629 | } | ||
630 | |||
631 | /* | ||
632 | * edac_device_delete_block(edac_dev,j); | ||
633 | */ | ||
634 | static void edac_device_delete_block( | ||
635 | struct edac_device_ctl_info *edac_dev, | ||
636 | struct edac_device_instance *instance, | ||
637 | int idx) | ||
638 | { | ||
639 | struct edac_device_block *block; | ||
640 | |||
641 | block = &instance->blocks[idx]; | ||
642 | |||
643 | /* unregister this block's kobject */ | ||
644 | init_completion(&block->kobj_complete); | ||
645 | kobject_unregister(&block->kobj); | ||
646 | wait_for_completion(&block->kobj_complete); | ||
647 | } | ||
648 | |||
649 | /************** instance ctor/dtor code ************/ | ||
650 | |||
651 | /* | ||
652 | * edac_device_create_instance | ||
653 | * create just one instance of an edac_device 'instance' | ||
654 | */ | ||
655 | static int edac_device_create_instance( | ||
656 | struct edac_device_ctl_info *edac_dev, int idx) | ||
657 | { | ||
658 | int i, j; | ||
659 | int err; | ||
660 | struct edac_device_instance *instance; | ||
661 | |||
662 | instance = &edac_dev->instances[idx]; | ||
663 | |||
664 | /* Init the instance's kobject */ | ||
665 | memset(&instance->kobj, 0, sizeof (struct kobject)); | ||
666 | |||
667 | /* set this new device under the edac_device main kobject */ | ||
668 | instance->kobj.parent = &edac_dev->kobj; | ||
669 | instance->kobj.ktype = &ktype_instance_ctrl; | ||
670 | |||
671 | err = kobject_set_name(&instance->kobj,"%s",instance->name); | ||
672 | if (err) | ||
673 | return err; | ||
674 | |||
675 | err = kobject_register(&instance->kobj); | ||
676 | if (err != 0) { | ||
677 | debugf2("%s() Failed to register instance '%s'\n", | ||
678 | __func__,instance->name); | ||
679 | return err; | ||
680 | } | ||
681 | |||
682 | debugf1("%s() now register '%d' blocks for instance %d\n", | ||
683 | __func__,instance->nr_blocks,idx); | ||
684 | |||
685 | /* register all blocks of this instance */ | ||
686 | for (i = 0; i < instance->nr_blocks; i++ ) { | ||
687 | err = edac_device_create_block(edac_dev,instance,i); | ||
688 | if (err) { | ||
689 | for (j = 0; j < i; j++) { | ||
690 | edac_device_delete_block(edac_dev,instance,j); | ||
691 | } | ||
692 | return err; | ||
693 | } | ||
694 | } | ||
695 | |||
696 | debugf1("%s() Registered instance %d '%s' kobject\n", | ||
697 | __func__, idx, instance->name); | ||
698 | |||
699 | return 0; | ||
700 | } | ||
701 | |||
702 | /* | ||
703 | * edac_device_remove_instance | ||
704 | * remove an edac_device instance | ||
705 | */ | ||
706 | static void edac_device_delete_instance( | ||
707 | struct edac_device_ctl_info *edac_dev, int idx) | ||
708 | { | ||
709 | int i; | ||
710 | struct edac_device_instance *instance; | ||
711 | |||
712 | instance = &edac_dev->instances[idx]; | ||
713 | |||
714 | /* unregister all blocks in this instance */ | ||
715 | for (i = 0; i < instance->nr_blocks; i++) { | ||
716 | edac_device_delete_block(edac_dev,instance,i); | ||
717 | } | ||
718 | |||
719 | /* unregister this instance's kobject */ | ||
720 | init_completion(&instance->kobj_complete); | ||
721 | kobject_unregister(&instance->kobj); | ||
722 | wait_for_completion(&instance->kobj_complete); | ||
723 | } | ||
724 | |||
725 | /* | ||
726 | * edac_device_create_instances | ||
727 | * create the first level of 'instances' for this device | ||
728 | * (ie 'cache' might have 'cache0', 'cache1', 'cache2', etc | ||
729 | */ | ||
730 | static int edac_device_create_instances(struct edac_device_ctl_info *edac_dev) | ||
731 | { | ||
732 | int i, j; | ||
733 | int err; | ||
734 | |||
735 | debugf0("%s()\n", __func__); | ||
736 | |||
737 | /* iterate over creation of the instances */ | ||
738 | for (i = 0; i < edac_dev->nr_instances; i++ ) { | ||
739 | err = edac_device_create_instance(edac_dev,i); | ||
740 | if (err) { | ||
741 | /* unwind previous instances on error */ | ||
742 | for (j = 0; j < i; j++) { | ||
743 | edac_device_delete_instance(edac_dev,j); | ||
744 | } | ||
745 | return err; | ||
746 | } | ||
747 | } | ||
748 | |||
749 | return 0; | ||
750 | } | ||
751 | |||
752 | /* | ||
753 | * edac_device_delete_instances(edac_dev); | ||
754 | * unregister all the kobjects of the instances | ||
755 | */ | ||
756 | static void edac_device_delete_instances(struct edac_device_ctl_info *edac_dev) | ||
757 | { | ||
758 | int i; | ||
759 | |||
760 | /* iterate over creation of the instances */ | ||
761 | for (i = 0; i < edac_dev->nr_instances; i++ ) { | ||
762 | edac_device_delete_instance(edac_dev,i); | ||
763 | } | ||
764 | } | ||
765 | |||
766 | /******************* edac_dev sysfs ctor/dtor code *************/ | ||
767 | |||
768 | /* | ||
769 | * edac_device_create_sysfs() Constructor | ||
770 | * | ||
771 | * Create a new edac_device kobject instance, | ||
772 | * | ||
773 | * Return: | ||
774 | * 0 Success | ||
775 | * !0 Failure | ||
776 | */ | ||
777 | int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev) | ||
778 | { | ||
779 | int err; | ||
780 | struct kobject *edac_kobj=&edac_dev->kobj; | ||
781 | |||
782 | /* register this instance's main kobj with the edac class kobj */ | ||
783 | err = edac_device_register_main_kobj(edac_dev); | ||
784 | if (err) | ||
785 | return err; | ||
786 | |||
787 | debugf0("%s() idx=%d\n", __func__, edac_dev->dev_idx); | ||
788 | |||
789 | /* create a symlink from the edac device | ||
790 | * to the platform 'device' being used for this | ||
791 | */ | ||
792 | err = sysfs_create_link(edac_kobj, | ||
793 | &edac_dev->dev->kobj, | ||
794 | EDAC_DEVICE_SYMLINK); | ||
795 | if (err) { | ||
796 | debugf0("%s() sysfs_create_link() returned err= %d\n", | ||
797 | __func__, err); | ||
798 | return err; | ||
799 | } | ||
800 | |||
801 | debugf0("%s() calling create-instances, idx=%d\n", | ||
802 | __func__, edac_dev->dev_idx); | ||
803 | |||
804 | /* Create the first level instance directories */ | ||
805 | err = edac_device_create_instances(edac_dev); | ||
806 | if (err) { | ||
807 | goto error0; | ||
808 | } | ||
809 | |||
810 | return 0; | ||
811 | |||
812 | /* Error unwind stack */ | ||
813 | |||
814 | error0: | ||
815 | edac_device_unregister_main_kobj(edac_dev); | ||
816 | |||
817 | return err; | ||
818 | } | ||
819 | |||
820 | /* | ||
821 | * edac_device_remove_sysfs() destructor | ||
822 | * | ||
823 | * remove a edac_device instance | ||
824 | */ | ||
825 | void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev) | ||
826 | { | ||
827 | debugf0("%s()\n", __func__); | ||
828 | |||
829 | edac_device_delete_instances(edac_dev); | ||
830 | |||
831 | /* remove the sym link */ | ||
832 | sysfs_remove_link(&edac_dev->kobj, EDAC_DEVICE_SYMLINK); | ||
833 | |||
834 | /* unregister the instance's main kobj */ | ||
835 | edac_device_unregister_main_kobj(edac_dev); | ||
836 | } | ||
837 | |||
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 3be5b7fe79cd..1f22d8cad6f7 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c | |||
@@ -88,7 +88,7 @@ static void edac_mc_dump_mci(struct mem_ctl_info *mci) | |||
88 | * If 'size' is a constant, the compiler will optimize this whole function | 88 | * If 'size' is a constant, the compiler will optimize this whole function |
89 | * down to either a no-op or the addition of a constant to the value of 'ptr'. | 89 | * down to either a no-op or the addition of a constant to the value of 'ptr'. |
90 | */ | 90 | */ |
91 | static inline char * align_ptr(void *ptr, unsigned size) | 91 | char * edac_align_ptr(void *ptr, unsigned size) |
92 | { | 92 | { |
93 | unsigned align, r; | 93 | unsigned align, r; |
94 | 94 | ||
@@ -147,10 +147,10 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows, | |||
147 | * hardcode everything into a single struct. | 147 | * hardcode everything into a single struct. |
148 | */ | 148 | */ |
149 | mci = (struct mem_ctl_info *) 0; | 149 | mci = (struct mem_ctl_info *) 0; |
150 | csi = (struct csrow_info *)align_ptr(&mci[1], sizeof(*csi)); | 150 | csi = (struct csrow_info *)edac_align_ptr(&mci[1], sizeof(*csi)); |
151 | chi = (struct channel_info *) | 151 | chi = (struct channel_info *) |
152 | align_ptr(&csi[nr_csrows], sizeof(*chi)); | 152 | edac_align_ptr(&csi[nr_csrows], sizeof(*chi)); |
153 | pvt = align_ptr(&chi[nr_chans * nr_csrows], sz_pvt); | 153 | pvt = edac_align_ptr(&chi[nr_chans * nr_csrows], sz_pvt); |
154 | size = ((unsigned long) pvt) + sz_pvt; | 154 | size = ((unsigned long) pvt) + sz_pvt; |
155 | 155 | ||
156 | if ((mci = kmalloc(size, GFP_KERNEL)) == NULL) | 156 | if ((mci = kmalloc(size, GFP_KERNEL)) == NULL) |
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 4a5e335f61d3..35bcf926017f 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c | |||
@@ -94,14 +94,6 @@ static const char *edac_caps[] = { | |||
94 | [EDAC_S16ECD16ED] = "S16ECD16ED" | 94 | [EDAC_S16ECD16ED] = "S16ECD16ED" |
95 | }; | 95 | }; |
96 | 96 | ||
97 | /* | ||
98 | * sysfs object: /sys/devices/system/edac | ||
99 | * need to export to other files in this modules | ||
100 | */ | ||
101 | struct sysdev_class edac_class = { | ||
102 | set_kset_name("edac"), | ||
103 | }; | ||
104 | |||
105 | /* sysfs object: | 97 | /* sysfs object: |
106 | * /sys/devices/system/edac/mc | 98 | * /sys/devices/system/edac/mc |
107 | */ | 99 | */ |
@@ -224,43 +216,38 @@ static struct kobj_type ktype_memctrl = { | |||
224 | int edac_sysfs_memctrl_setup(void) | 216 | int edac_sysfs_memctrl_setup(void) |
225 | { | 217 | { |
226 | int err = 0; | 218 | int err = 0; |
219 | struct sysdev_class *edac_class; | ||
227 | 220 | ||
228 | debugf1("%s()\n", __func__); | 221 | debugf1("%s()\n", __func__); |
229 | 222 | ||
230 | /* create the /sys/devices/system/edac directory */ | 223 | /* get the /sys/devices/system/edac class reference */ |
231 | err = sysdev_class_register(&edac_class); | 224 | edac_class = edac_get_edac_class(); |
232 | 225 | if (edac_class == NULL) { | |
233 | if (err) { | 226 | debugf1("%s() no edac_class error=%d\n", __func__, err); |
234 | debugf1("%s() error=%d\n", __func__, err); | ||
235 | return err; | 227 | return err; |
236 | } | 228 | } |
237 | 229 | ||
238 | /* Init the MC's kobject */ | 230 | /* Init the MC's kobject */ |
239 | memset(&edac_memctrl_kobj, 0, sizeof (edac_memctrl_kobj)); | 231 | memset(&edac_memctrl_kobj, 0, sizeof (edac_memctrl_kobj)); |
240 | edac_memctrl_kobj.parent = &edac_class.kset.kobj; | 232 | edac_memctrl_kobj.parent = &edac_class->kset.kobj; |
241 | edac_memctrl_kobj.ktype = &ktype_memctrl; | 233 | edac_memctrl_kobj.ktype = &ktype_memctrl; |
242 | 234 | ||
243 | /* generate sysfs "..../edac/mc" */ | 235 | /* generate sysfs "..../edac/mc" */ |
244 | err = kobject_set_name(&edac_memctrl_kobj,"mc"); | 236 | err = kobject_set_name(&edac_memctrl_kobj,"mc"); |
245 | 237 | if (err) { | |
246 | if (err) | 238 | debugf1("%s() Failed to set name '.../edac/mc'\n", __func__ ); |
247 | goto fail; | 239 | return err; |
240 | } | ||
248 | 241 | ||
249 | /* FIXME: maybe new sysdev_create_subdir() */ | 242 | /* FIXME: maybe new sysdev_create_subdir() */ |
250 | err = kobject_register(&edac_memctrl_kobj); | 243 | err = kobject_register(&edac_memctrl_kobj); |
251 | |||
252 | if (err) { | 244 | if (err) { |
253 | debugf1("Failed to register '.../edac/mc'\n"); | 245 | debugf1("%s() Failed to register '.../edac/mc'\n", __func__ ); |
254 | goto fail; | 246 | return err; |
255 | } | 247 | } |
256 | 248 | ||
257 | debugf1("Registered '.../edac/mc' kobject\n"); | 249 | debugf1("%s() Registered '.../edac/mc' kobject\n",__func__); |
258 | |||
259 | return 0; | 250 | return 0; |
260 | |||
261 | fail: | ||
262 | sysdev_class_unregister(&edac_class); | ||
263 | return err; | ||
264 | } | 251 | } |
265 | 252 | ||
266 | /* | 253 | /* |
@@ -276,9 +263,6 @@ void edac_sysfs_memctrl_teardown(void) | |||
276 | init_completion(&edac_memctrl_kobj_complete); | 263 | init_completion(&edac_memctrl_kobj_complete); |
277 | kobject_unregister(&edac_memctrl_kobj); | 264 | kobject_unregister(&edac_memctrl_kobj); |
278 | wait_for_completion(&edac_memctrl_kobj_complete); | 265 | wait_for_completion(&edac_memctrl_kobj_complete); |
279 | |||
280 | /* Unregister the 'edac' object */ | ||
281 | sysdev_class_unregister(&edac_class); | ||
282 | } | 266 | } |
283 | 267 | ||
284 | 268 | ||
@@ -286,32 +270,38 @@ void edac_sysfs_memctrl_teardown(void) | |||
286 | */ | 270 | */ |
287 | 271 | ||
288 | /* Set of more default csrow<id> attribute show/store functions */ | 272 | /* Set of more default csrow<id> attribute show/store functions */ |
289 | static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data, int private) | 273 | static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data, |
274 | int private) | ||
290 | { | 275 | { |
291 | return sprintf(data,"%u\n", csrow->ue_count); | 276 | return sprintf(data,"%u\n", csrow->ue_count); |
292 | } | 277 | } |
293 | 278 | ||
294 | static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data, int private) | 279 | static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data, |
280 | int private) | ||
295 | { | 281 | { |
296 | return sprintf(data,"%u\n", csrow->ce_count); | 282 | return sprintf(data,"%u\n", csrow->ce_count); |
297 | } | 283 | } |
298 | 284 | ||
299 | static ssize_t csrow_size_show(struct csrow_info *csrow, char *data, int private) | 285 | static ssize_t csrow_size_show(struct csrow_info *csrow, char *data, |
286 | int private) | ||
300 | { | 287 | { |
301 | return sprintf(data,"%u\n", PAGES_TO_MiB(csrow->nr_pages)); | 288 | return sprintf(data,"%u\n", PAGES_TO_MiB(csrow->nr_pages)); |
302 | } | 289 | } |
303 | 290 | ||
304 | static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data, int private) | 291 | static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data, |
292 | int private) | ||
305 | { | 293 | { |
306 | return sprintf(data,"%s\n", mem_types[csrow->mtype]); | 294 | return sprintf(data,"%s\n", mem_types[csrow->mtype]); |
307 | } | 295 | } |
308 | 296 | ||
309 | static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data, int private) | 297 | static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data, |
298 | int private) | ||
310 | { | 299 | { |
311 | return sprintf(data,"%s\n", dev_types[csrow->dtype]); | 300 | return sprintf(data,"%s\n", dev_types[csrow->dtype]); |
312 | } | 301 | } |
313 | 302 | ||
314 | static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data, int private) | 303 | static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data, |
304 | int private) | ||
315 | { | 305 | { |
316 | return sprintf(data,"%s\n", edac_caps[csrow->edac_mode]); | 306 | return sprintf(data,"%s\n", edac_caps[csrow->edac_mode]); |
317 | } | 307 | } |
@@ -509,9 +499,10 @@ static int edac_create_channel_files(struct kobject *kobj, int chan) | |||
509 | if (!err) { | 499 | if (!err) { |
510 | /* create the CE Count attribute file */ | 500 | /* create the CE Count attribute file */ |
511 | err = sysfs_create_file(kobj, | 501 | err = sysfs_create_file(kobj, |
512 | (struct attribute *) dynamic_csrow_ce_count_attr[chan]); | 502 | (struct attribute *)dynamic_csrow_ce_count_attr[chan]); |
513 | } else { | 503 | } else { |
514 | debugf1("%s() dimm labels and ce_count files created", __func__); | 504 | debugf1("%s() dimm labels and ce_count files created", |
505 | __func__); | ||
515 | } | 506 | } |
516 | 507 | ||
517 | return err; | 508 | return err; |
@@ -643,7 +634,7 @@ static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) | |||
643 | } else { | 634 | } else { |
644 | /* FIXME: produce "not implemented" ERROR for user-side. */ | 635 | /* FIXME: produce "not implemented" ERROR for user-side. */ |
645 | edac_printk(KERN_WARNING, EDAC_MC, | 636 | edac_printk(KERN_WARNING, EDAC_MC, |
646 | "Memory scrubbing 'get' control is not implemented!\n"); | 637 | "Memory scrubbing 'get' control is not implemented\n"); |
647 | } | 638 | } |
648 | return sprintf(data, "%d\n", bandwidth); | 639 | return sprintf(data, "%d\n", bandwidth); |
649 | } | 640 | } |
@@ -755,7 +746,8 @@ MCIDEV_ATTR(ue_count,S_IRUGO,mci_ue_count_show,NULL); | |||
755 | MCIDEV_ATTR(ce_count,S_IRUGO,mci_ce_count_show,NULL); | 746 | MCIDEV_ATTR(ce_count,S_IRUGO,mci_ce_count_show,NULL); |
756 | 747 | ||
757 | /* memory scrubber attribute file */ | 748 | /* memory scrubber attribute file */ |
758 | MCIDEV_ATTR(sdram_scrub_rate,S_IRUGO|S_IWUSR,mci_sdram_scrub_rate_show,mci_sdram_scrub_rate_store); | 749 | MCIDEV_ATTR(sdram_scrub_rate,S_IRUGO|S_IWUSR,mci_sdram_scrub_rate_show,\ |
750 | mci_sdram_scrub_rate_store); | ||
759 | 751 | ||
760 | static struct mcidev_attribute *mci_attr[] = { | 752 | static struct mcidev_attribute *mci_attr[] = { |
761 | &mci_attr_reset_counters, | 753 | &mci_attr_reset_counters, |
diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c index 8db0471a9476..3cd3a236821c 100644 --- a/drivers/edac/edac_module.c +++ b/drivers/edac/edac_module.c | |||
@@ -13,8 +13,77 @@ int edac_debug_level = 1; | |||
13 | EXPORT_SYMBOL_GPL(edac_debug_level); | 13 | EXPORT_SYMBOL_GPL(edac_debug_level); |
14 | #endif | 14 | #endif |
15 | 15 | ||
16 | /* scope is to module level only */ | ||
17 | struct workqueue_struct *edac_workqueue; | ||
18 | |||
19 | /* private to this file */ | ||
16 | static struct task_struct *edac_thread; | 20 | static struct task_struct *edac_thread; |
17 | 21 | ||
22 | |||
23 | /* | ||
24 | * sysfs object: /sys/devices/system/edac | ||
25 | * need to export to other files in this modules | ||
26 | */ | ||
27 | static struct sysdev_class edac_class = { | ||
28 | set_kset_name("edac"), | ||
29 | }; | ||
30 | static int edac_class_valid = 0; | ||
31 | |||
32 | /* | ||
33 | * edac_get_edac_class() | ||
34 | * | ||
35 | * return pointer to the edac class of 'edac' | ||
36 | */ | ||
37 | struct sysdev_class *edac_get_edac_class(void) | ||
38 | { | ||
39 | struct sysdev_class *classptr=NULL; | ||
40 | |||
41 | if (edac_class_valid) | ||
42 | classptr = &edac_class; | ||
43 | |||
44 | return classptr; | ||
45 | } | ||
46 | |||
47 | /* | ||
48 | * edac_register_sysfs_edac_name() | ||
49 | * | ||
50 | * register the 'edac' into /sys/devices/system | ||
51 | * | ||
52 | * return: | ||
53 | * 0 success | ||
54 | * !0 error | ||
55 | */ | ||
56 | static int edac_register_sysfs_edac_name(void) | ||
57 | { | ||
58 | int err; | ||
59 | |||
60 | /* create the /sys/devices/system/edac directory */ | ||
61 | err = sysdev_class_register(&edac_class); | ||
62 | |||
63 | if (err) { | ||
64 | debugf1("%s() error=%d\n", __func__, err); | ||
65 | return err; | ||
66 | } | ||
67 | |||
68 | edac_class_valid = 1; | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * sysdev_class_unregister() | ||
74 | * | ||
75 | * unregister the 'edac' from /sys/devices/system | ||
76 | */ | ||
77 | static void edac_unregister_sysfs_edac_name(void) | ||
78 | { | ||
79 | /* only if currently registered, then unregister it */ | ||
80 | if (edac_class_valid) | ||
81 | sysdev_class_unregister(&edac_class); | ||
82 | |||
83 | edac_class_valid = 0; | ||
84 | } | ||
85 | |||
86 | |||
18 | /* | 87 | /* |
19 | * Check MC status every edac_get_poll_msec(). | 88 | * Check MC status every edac_get_poll_msec(). |
20 | * Check PCI status every edac_get_poll_msec() as well. | 89 | * Check PCI status every edac_get_poll_msec() as well. |
@@ -53,11 +122,40 @@ static int edac_kernel_thread(void *arg) | |||
53 | } | 122 | } |
54 | 123 | ||
55 | /* | 124 | /* |
125 | * edac_workqueue_setup | ||
126 | * initialize the edac work queue for polling operations | ||
127 | */ | ||
128 | static int edac_workqueue_setup(void) | ||
129 | { | ||
130 | edac_workqueue = create_singlethread_workqueue("edac-poller"); | ||
131 | if (edac_workqueue == NULL) | ||
132 | return -ENODEV; | ||
133 | else | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * edac_workqueue_teardown | ||
139 | * teardown the edac workqueue | ||
140 | */ | ||
141 | static void edac_workqueue_teardown(void) | ||
142 | { | ||
143 | if (edac_workqueue) { | ||
144 | flush_workqueue(edac_workqueue); | ||
145 | destroy_workqueue(edac_workqueue); | ||
146 | edac_workqueue = NULL; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | |||
151 | /* | ||
56 | * edac_init | 152 | * edac_init |
57 | * module initialization entry point | 153 | * module initialization entry point |
58 | */ | 154 | */ |
59 | static int __init edac_init(void) | 155 | static int __init edac_init(void) |
60 | { | 156 | { |
157 | int err = 0; | ||
158 | |||
61 | edac_printk(KERN_INFO, EDAC_MC, EDAC_MC_VERSION "\n"); | 159 | edac_printk(KERN_INFO, EDAC_MC, EDAC_MC_VERSION "\n"); |
62 | 160 | ||
63 | /* | 161 | /* |
@@ -69,32 +167,61 @@ static int __init edac_init(void) | |||
69 | */ | 167 | */ |
70 | edac_pci_clear_parity_errors(); | 168 | edac_pci_clear_parity_errors(); |
71 | 169 | ||
72 | /* Create the MC sysfs entries */ | 170 | /* |
171 | * perform the registration of the /sys/devices/system/edac object | ||
172 | */ | ||
173 | if (edac_register_sysfs_edac_name()) { | ||
174 | edac_printk(KERN_ERR, EDAC_MC, | ||
175 | "Error initializing 'edac' kobject\n"); | ||
176 | err = -ENODEV; | ||
177 | goto error; | ||
178 | } | ||
179 | |||
180 | /* Create the MC sysfs entries, must be first | ||
181 | */ | ||
73 | if (edac_sysfs_memctrl_setup()) { | 182 | if (edac_sysfs_memctrl_setup()) { |
74 | edac_printk(KERN_ERR, EDAC_MC, | 183 | edac_printk(KERN_ERR, EDAC_MC, |
75 | "Error initializing sysfs code\n"); | 184 | "Error initializing sysfs code\n"); |
76 | return -ENODEV; | 185 | err = -ENODEV; |
186 | goto error_sysfs; | ||
77 | } | 187 | } |
78 | 188 | ||
79 | /* Create the PCI parity sysfs entries */ | 189 | /* Create the PCI parity sysfs entries */ |
80 | if (edac_sysfs_pci_setup()) { | 190 | if (edac_sysfs_pci_setup()) { |
81 | edac_sysfs_memctrl_teardown(); | ||
82 | edac_printk(KERN_ERR, EDAC_MC, | 191 | edac_printk(KERN_ERR, EDAC_MC, |
83 | "PCI: Error initializing sysfs code\n"); | 192 | "PCI: Error initializing sysfs code\n"); |
84 | return -ENODEV; | 193 | err = -ENODEV; |
194 | goto error_mem; | ||
195 | } | ||
196 | |||
197 | /* Setup/Initialize the edac_device system */ | ||
198 | err = edac_workqueue_setup(); | ||
199 | if (err) { | ||
200 | edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n"); | ||
201 | goto error_pci; | ||
85 | } | 202 | } |
86 | 203 | ||
87 | /* create our kernel thread */ | 204 | /* create our kernel thread */ |
88 | edac_thread = kthread_run(edac_kernel_thread, NULL, "kedac"); | 205 | edac_thread = kthread_run(edac_kernel_thread, NULL, "kedac"); |
89 | 206 | ||
90 | if (IS_ERR(edac_thread)) { | 207 | if (IS_ERR(edac_thread)) { |
91 | /* remove the sysfs entries */ | 208 | err = PTR_ERR(edac_thread); |
92 | edac_sysfs_memctrl_teardown(); | 209 | goto error_work; |
93 | edac_sysfs_pci_teardown(); | ||
94 | return PTR_ERR(edac_thread); | ||
95 | } | 210 | } |
96 | 211 | ||
97 | return 0; | 212 | return 0; |
213 | |||
214 | /* Error teardown stack */ | ||
215 | error_work: | ||
216 | edac_workqueue_teardown(); | ||
217 | error_pci: | ||
218 | edac_sysfs_pci_teardown(); | ||
219 | error_mem: | ||
220 | edac_sysfs_memctrl_teardown(); | ||
221 | error_sysfs: | ||
222 | edac_unregister_sysfs_edac_name(); | ||
223 | error: | ||
224 | return err; | ||
98 | } | 225 | } |
99 | 226 | ||
100 | /* | 227 | /* |
@@ -106,9 +233,11 @@ static void __exit edac_exit(void) | |||
106 | debugf0("%s()\n", __func__); | 233 | debugf0("%s()\n", __func__); |
107 | kthread_stop(edac_thread); | 234 | kthread_stop(edac_thread); |
108 | 235 | ||
109 | /* tear down the sysfs device */ | 236 | /* tear down the various subsystems*/ |
237 | edac_workqueue_teardown(); | ||
110 | edac_sysfs_memctrl_teardown(); | 238 | edac_sysfs_memctrl_teardown(); |
111 | edac_sysfs_pci_teardown(); | 239 | edac_sysfs_pci_teardown(); |
240 | edac_unregister_sysfs_edac_name(); | ||
112 | } | 241 | } |
113 | 242 | ||
114 | /* | 243 | /* |
diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h index 69c77f85bcd4..2758d03c3e03 100644 --- a/drivers/edac/edac_module.h +++ b/drivers/edac/edac_module.h | |||
@@ -33,6 +33,15 @@ extern int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev); | |||
33 | extern void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev); | 33 | extern void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev); |
34 | extern struct sysdev_class *edac_get_edac_class(void); | 34 | extern struct sysdev_class *edac_get_edac_class(void); |
35 | 35 | ||
36 | /* edac core workqueue: single CPU mode */ | ||
37 | extern struct workqueue_struct *edac_workqueue; | ||
38 | extern void edac_workq_setup(struct edac_device_ctl_info *edac_dev, | ||
39 | unsigned msec); | ||
40 | extern void edac_workq_teardown(struct edac_device_ctl_info *edac_dev); | ||
41 | extern void edac_device_reset_delay_period( | ||
42 | struct edac_device_ctl_info *edac_dev, | ||
43 | unsigned long value); | ||
44 | |||
36 | 45 | ||
37 | /* | 46 | /* |
38 | * EDAC PCI functions | 47 | * EDAC PCI functions |