diff options
author | Dave Jiang <djiang@mvista.com> | 2007-07-19 04:49:52 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-19 13:04:54 -0400 |
commit | 91b99041c1d577ded1da599ddc28cec2e07253cf (patch) | |
tree | 21b132d19166dca5c363b98e20741b78df4ad68a /drivers | |
parent | 81d87cb13e367bb804bf44889ae0de7369705d6c (diff) |
drivers/edac: updated PCI monitoring
Moving PCI to a per-instance device model
This should include the correct sysfs setup as well. Please review.
Signed-off-by: Dave Jiang <djiang@mvista.com>
Signed-off-by: Douglas Thompson <dougthompson@xmission.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/edac/Makefile | 5 | ||||
-rw-r--r-- | drivers/edac/e752x_edac.c | 16 | ||||
-rw-r--r-- | drivers/edac/edac_core.h | 120 | ||||
-rw-r--r-- | drivers/edac/edac_device.c | 23 | ||||
-rw-r--r-- | drivers/edac/edac_module.c | 32 | ||||
-rw-r--r-- | drivers/edac/edac_pci.c | 451 | ||||
-rw-r--r-- | drivers/edac/edac_pci_sysfs.c | 279 |
7 files changed, 861 insertions, 65 deletions
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 547ea135b64e..6a5e5d18db69 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile | |||
@@ -11,9 +11,12 @@ obj-$(CONFIG_EDAC) := edac_stub.o | |||
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_device.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 | |||
15 | edac_core-objs += edac_module.o edac_device_sysfs.o | 14 | edac_core-objs += edac_module.o edac_device_sysfs.o |
16 | 15 | ||
16 | ifdef CONFIG_PCI | ||
17 | edac_core-objs += edac_pci.o edac_pci_sysfs.o | ||
18 | endif | ||
19 | |||
17 | obj-$(CONFIG_EDAC_AMD76X) += amd76x_edac.o | 20 | obj-$(CONFIG_EDAC_AMD76X) += amd76x_edac.o |
18 | obj-$(CONFIG_EDAC_I5000) += i5000_edac.o | 21 | obj-$(CONFIG_EDAC_I5000) += i5000_edac.o |
19 | obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o | 22 | obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o |
diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index 10f84995472d..d8b86584afbb 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c | |||
@@ -30,6 +30,8 @@ | |||
30 | 30 | ||
31 | static int force_function_unhide; | 31 | static int force_function_unhide; |
32 | 32 | ||
33 | static struct edac_pci_ctl_info *e752x_pci; | ||
34 | |||
33 | #define e752x_printk(level, fmt, arg...) \ | 35 | #define e752x_printk(level, fmt, arg...) \ |
34 | edac_printk(level, "e752x", fmt, ##arg) | 36 | edac_printk(level, "e752x", fmt, ##arg) |
35 | 37 | ||
@@ -1040,6 +1042,17 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) | |||
1040 | e752x_init_error_reporting_regs(pvt); | 1042 | e752x_init_error_reporting_regs(pvt); |
1041 | e752x_get_error_info(mci, &discard); /* clear other MCH errors */ | 1043 | e752x_get_error_info(mci, &discard); /* clear other MCH errors */ |
1042 | 1044 | ||
1045 | /* allocating generic PCI control info */ | ||
1046 | e752x_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR); | ||
1047 | if (!e752x_pci) { | ||
1048 | printk(KERN_WARNING | ||
1049 | "%s(): Unable to create PCI control\n", | ||
1050 | __func__); | ||
1051 | printk(KERN_WARNING | ||
1052 | "%s(): PCI error report via EDAC not setup\n", | ||
1053 | __func__); | ||
1054 | } | ||
1055 | |||
1043 | /* get this far and it's successful */ | 1056 | /* get this far and it's successful */ |
1044 | debugf3("%s(): success\n", __func__); | 1057 | debugf3("%s(): success\n", __func__); |
1045 | return 0; | 1058 | return 0; |
@@ -1073,6 +1086,9 @@ static void __devexit e752x_remove_one(struct pci_dev *pdev) | |||
1073 | 1086 | ||
1074 | debugf0("%s()\n", __func__); | 1087 | debugf0("%s()\n", __func__); |
1075 | 1088 | ||
1089 | if (e752x_pci) | ||
1090 | edac_pci_release_generic_ctl(e752x_pci); | ||
1091 | |||
1076 | if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL) | 1092 | if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL) |
1077 | return; | 1093 | return; |
1078 | 1094 | ||
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index b73d659a4bb2..febff4111428 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h | |||
@@ -60,6 +60,10 @@ | |||
60 | #define edac_device_printk(ctl, level, fmt, arg...) \ | 60 | #define edac_device_printk(ctl, level, fmt, arg...) \ |
61 | printk(level "EDAC DEVICE%d: " fmt, ctl->dev_idx, ##arg) | 61 | printk(level "EDAC DEVICE%d: " fmt, ctl->dev_idx, ##arg) |
62 | 62 | ||
63 | /* edac_pci printk */ | ||
64 | #define edac_pci_printk(ctl, level, fmt, arg...) \ | ||
65 | printk(level "EDAC PCI%d: " fmt, ctl->pci_idx, ##arg) | ||
66 | |||
63 | /* prefixes for edac_printk() and edac_mc_printk() */ | 67 | /* prefixes for edac_printk() and edac_mc_printk() */ |
64 | #define EDAC_MC "MC" | 68 | #define EDAC_MC "MC" |
65 | #define EDAC_PCI "PCI" | 69 | #define EDAC_PCI "PCI" |
@@ -200,6 +204,13 @@ enum scrub_type { | |||
200 | 204 | ||
201 | /* FIXME - should have notify capabilities: NMI, LOG, PROC, etc */ | 205 | /* FIXME - should have notify capabilities: NMI, LOG, PROC, etc */ |
202 | 206 | ||
207 | /* EDAC internal operation states */ | ||
208 | #define OP_ALLOC 0x100 | ||
209 | #define OP_RUNNING_POLL 0x201 | ||
210 | #define OP_RUNNING_INTERRUPT 0x202 | ||
211 | #define OP_RUNNING_POLL_INTR 0x203 | ||
212 | #define OP_OFFLINE 0x300 | ||
213 | |||
203 | extern char * edac_align_ptr(void *ptr, unsigned size); | 214 | extern char * edac_align_ptr(void *ptr, unsigned size); |
204 | 215 | ||
205 | /* | 216 | /* |
@@ -520,12 +531,6 @@ struct edac_device_ctl_info { | |||
520 | 531 | ||
521 | /* the internal state of this controller instance */ | 532 | /* the internal state of this controller instance */ |
522 | int op_state; | 533 | int op_state; |
523 | #define OP_ALLOC 0x100 | ||
524 | #define OP_RUNNING_POLL 0x201 | ||
525 | #define OP_RUNNING_INTERRUPT 0x202 | ||
526 | #define OP_RUNNING_POLL_INTR 0x203 | ||
527 | #define OP_OFFLINE 0x300 | ||
528 | |||
529 | /* work struct for this instance */ | 534 | /* work struct for this instance */ |
530 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) | 535 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) |
531 | struct delayed_work work; | 536 | struct delayed_work work; |
@@ -626,6 +631,84 @@ extern void edac_device_free_ctl_info( struct edac_device_ctl_info *ctl_info); | |||
626 | 631 | ||
627 | #ifdef CONFIG_PCI | 632 | #ifdef CONFIG_PCI |
628 | 633 | ||
634 | struct edac_pci_counter { | ||
635 | atomic_t pe_count; | ||
636 | atomic_t npe_count; | ||
637 | }; | ||
638 | |||
639 | /* | ||
640 | * Abstract edac_pci control info structure | ||
641 | * | ||
642 | */ | ||
643 | struct edac_pci_ctl_info { | ||
644 | /* for global list of edac_pci_ctl_info structs */ | ||
645 | struct list_head link; | ||
646 | |||
647 | int pci_idx; | ||
648 | |||
649 | /* Per instance controls for this edac_device */ | ||
650 | int check_parity_error; /* boolean for checking parity errs */ | ||
651 | int log_parity_error; /* boolean for logging parity errs */ | ||
652 | int panic_on_pe; /* boolean for panic'ing on a PE */ | ||
653 | unsigned poll_msec; /* number of milliseconds to poll interval */ | ||
654 | unsigned long delay; /* number of jiffies for poll_msec */ | ||
655 | |||
656 | struct sysdev_class *edac_class; /* pointer to class */ | ||
657 | |||
658 | /* the internal state of this controller instance */ | ||
659 | int op_state; | ||
660 | /* work struct for this instance */ | ||
661 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) | ||
662 | struct delayed_work work; | ||
663 | #else | ||
664 | struct work_struct work; | ||
665 | #endif | ||
666 | |||
667 | /* pointer to edac polling checking routine: | ||
668 | * If NOT NULL: points to polling check routine | ||
669 | * If NULL: Then assumes INTERRUPT operation, where | ||
670 | * MC driver will receive events | ||
671 | */ | ||
672 | void (*edac_check) (struct edac_pci_ctl_info * edac_dev); | ||
673 | |||
674 | struct device *dev; /* pointer to device structure */ | ||
675 | |||
676 | const char *mod_name; /* module name */ | ||
677 | const char *ctl_name; /* edac controller name */ | ||
678 | const char *dev_name; /* pci/platform/etc... name */ | ||
679 | |||
680 | void *pvt_info; /* pointer to 'private driver' info */ | ||
681 | |||
682 | unsigned long start_time;/* edac_pci load start time (jiffies)*/ | ||
683 | |||
684 | /* these are for safe removal of devices from global list while | ||
685 | * NMI handlers may be traversing list | ||
686 | */ | ||
687 | struct rcu_head rcu; | ||
688 | struct completion complete; | ||
689 | |||
690 | /* sysfs top name under 'edac' directory | ||
691 | * and instance name: | ||
692 | * cpu/cpu0/... | ||
693 | * cpu/cpu1/... | ||
694 | * cpu/cpu2/... | ||
695 | * ... | ||
696 | */ | ||
697 | char name[EDAC_DEVICE_NAME_LEN + 1]; | ||
698 | |||
699 | /* Event counters for the this whole EDAC Device */ | ||
700 | struct edac_pci_counter counters; | ||
701 | |||
702 | /* edac sysfs device control for the 'name' | ||
703 | * device this structure controls | ||
704 | */ | ||
705 | struct kobject kobj; | ||
706 | struct completion kobj_complete; | ||
707 | }; | ||
708 | |||
709 | #define to_edac_pci_ctl_work(w) \ | ||
710 | container_of(w, struct edac_pci_ctl_info,work) | ||
711 | |||
629 | /* write all or some bits in a byte-register*/ | 712 | /* write all or some bits in a byte-register*/ |
630 | static inline void pci_write_bits8(struct pci_dev *pdev, int offset, u8 value, | 713 | static inline void pci_write_bits8(struct pci_dev *pdev, int offset, u8 value, |
631 | u8 mask) | 714 | u8 mask) |
@@ -726,5 +809,30 @@ extern void edac_device_handle_ue(struct edac_device_ctl_info *edac_dev, | |||
726 | extern void edac_device_handle_ce(struct edac_device_ctl_info *edac_dev, | 809 | extern void edac_device_handle_ce(struct edac_device_ctl_info *edac_dev, |
727 | int inst_nr, int block_nr, const char *msg); | 810 | int inst_nr, int block_nr, const char *msg); |
728 | 811 | ||
812 | /* | ||
813 | * edac_pci APIs | ||
814 | */ | ||
815 | extern struct edac_pci_ctl_info * | ||
816 | edac_pci_alloc_ctl_info(unsigned int sz_pvt, const char *edac_pci_name); | ||
817 | |||
818 | extern void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci); | ||
819 | |||
820 | extern void | ||
821 | edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci, unsigned long value); | ||
822 | |||
823 | extern int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx); | ||
824 | extern struct edac_pci_ctl_info * edac_pci_del_device(struct device *dev); | ||
825 | |||
826 | extern struct edac_pci_ctl_info * | ||
827 | edac_pci_create_generic_ctl(struct device *dev, const char *mod_name); | ||
828 | |||
829 | extern void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci); | ||
830 | extern int edac_pci_create_sysfs(struct edac_pci_ctl_info *pci); | ||
831 | extern void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci); | ||
832 | |||
833 | /* | ||
834 | * edac misc APIs | ||
835 | */ | ||
836 | extern char * edac_op_state_toString(int op_state); | ||
729 | 837 | ||
730 | #endif /* _EDAC_CORE_H_ */ | 838 | #endif /* _EDAC_CORE_H_ */ |
diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c index 3f4c8a28154a..3db8effa1fd0 100644 --- a/drivers/edac/edac_device.c +++ b/drivers/edac/edac_device.c | |||
@@ -418,27 +418,6 @@ void edac_device_reset_delay_period( | |||
418 | unlock_device_list(); | 418 | unlock_device_list(); |
419 | } | 419 | } |
420 | 420 | ||
421 | /* | ||
422 | * edac_op_state_toString(edac_dev) | ||
423 | */ | ||
424 | static char *edac_op_state_toString(struct edac_device_ctl_info *edac_dev) | ||
425 | { | ||
426 | int opstate = edac_dev->op_state; | ||
427 | |||
428 | if (opstate == OP_RUNNING_POLL) | ||
429 | return "POLLED"; | ||
430 | else if (opstate == OP_RUNNING_INTERRUPT) | ||
431 | return "INTERRUPT"; | ||
432 | else if (opstate == OP_RUNNING_POLL_INTR) | ||
433 | return "POLL-INTR"; | ||
434 | else if (opstate == OP_ALLOC) | ||
435 | return "ALLOC"; | ||
436 | else if (opstate == OP_OFFLINE) | ||
437 | return "OFFLINE"; | ||
438 | |||
439 | return "UNKNOWN"; | ||
440 | } | ||
441 | |||
442 | /** | 421 | /** |
443 | * edac_device_add_device: Insert the 'edac_dev' structure into the | 422 | * edac_device_add_device: Insert the 'edac_dev' structure into the |
444 | * edac_device global list and create sysfs entries associated with | 423 | * edac_device global list and create sysfs entries associated with |
@@ -496,7 +475,7 @@ int edac_device_add_device(struct edac_device_ctl_info *edac_dev, int edac_idx) | |||
496 | edac_dev->mod_name, | 475 | edac_dev->mod_name, |
497 | edac_dev->ctl_name, | 476 | edac_dev->ctl_name, |
498 | dev_name(edac_dev), | 477 | dev_name(edac_dev), |
499 | edac_op_state_toString(edac_dev) | 478 | edac_op_state_toString(edac_dev->op_state) |
500 | ); | 479 | ); |
501 | 480 | ||
502 | unlock_device_list(); | 481 | unlock_device_list(); |
diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c index dc900ed75178..38e4a71380aa 100644 --- a/drivers/edac/edac_module.c +++ b/drivers/edac/edac_module.c | |||
@@ -35,6 +35,25 @@ static struct sysdev_class edac_class = { | |||
35 | static int edac_class_valid = 0; | 35 | static int edac_class_valid = 0; |
36 | 36 | ||
37 | /* | 37 | /* |
38 | * edac_op_state_toString() | ||
39 | */ | ||
40 | char * edac_op_state_toString(int opstate) | ||
41 | { | ||
42 | if (opstate == OP_RUNNING_POLL) | ||
43 | return "POLLED"; | ||
44 | else if (opstate == OP_RUNNING_INTERRUPT) | ||
45 | return "INTERRUPT"; | ||
46 | else if (opstate == OP_RUNNING_POLL_INTR) | ||
47 | return "POLL-INTR"; | ||
48 | else if (opstate == OP_ALLOC) | ||
49 | return "ALLOC"; | ||
50 | else if (opstate == OP_OFFLINE) | ||
51 | return "OFFLINE"; | ||
52 | |||
53 | return "UNKNOWN"; | ||
54 | } | ||
55 | |||
56 | /* | ||
38 | * edac_get_edac_class() | 57 | * edac_get_edac_class() |
39 | * | 58 | * |
40 | * return pointer to the edac class of 'edac' | 59 | * return pointer to the edac class of 'edac' |
@@ -153,26 +172,16 @@ static int __init edac_init(void) | |||
153 | goto error_sysfs; | 172 | goto error_sysfs; |
154 | } | 173 | } |
155 | 174 | ||
156 | /* Create the PCI parity sysfs entries */ | ||
157 | if (edac_sysfs_pci_setup()) { | ||
158 | edac_printk(KERN_ERR, EDAC_MC, | ||
159 | "PCI: Error initializing sysfs code\n"); | ||
160 | err = -ENODEV; | ||
161 | goto error_mem; | ||
162 | } | ||
163 | |||
164 | /* Setup/Initialize the edac_device system */ | 175 | /* Setup/Initialize the edac_device system */ |
165 | err = edac_workqueue_setup(); | 176 | err = edac_workqueue_setup(); |
166 | if (err) { | 177 | if (err) { |
167 | edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n"); | 178 | edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n"); |
168 | goto error_pci; | 179 | goto error_mem; |
169 | } | 180 | } |
170 | 181 | ||
171 | return 0; | 182 | return 0; |
172 | 183 | ||
173 | /* Error teardown stack */ | 184 | /* Error teardown stack */ |
174 | error_pci: | ||
175 | edac_sysfs_pci_teardown(); | ||
176 | error_mem: | 185 | error_mem: |
177 | edac_sysfs_memctrl_teardown(); | 186 | edac_sysfs_memctrl_teardown(); |
178 | error_sysfs: | 187 | error_sysfs: |
@@ -192,7 +201,6 @@ static void __exit edac_exit(void) | |||
192 | /* tear down the various subsystems*/ | 201 | /* tear down the various subsystems*/ |
193 | edac_workqueue_teardown(); | 202 | edac_workqueue_teardown(); |
194 | edac_sysfs_memctrl_teardown(); | 203 | edac_sysfs_memctrl_teardown(); |
195 | edac_sysfs_pci_teardown(); | ||
196 | edac_unregister_sysfs_edac_name(); | 204 | edac_unregister_sysfs_edac_name(); |
197 | } | 205 | } |
198 | 206 | ||
diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c new file mode 100644 index 000000000000..677c603f5596 --- /dev/null +++ b/drivers/edac/edac_pci.c | |||
@@ -0,0 +1,451 @@ | |||
1 | /* | ||
2 | * EDAC PCI component | ||
3 | * | ||
4 | * Author: Dave Jiang <djiang@mvista.com> | ||
5 | * | ||
6 | * 2007 (c) MontaVista Software, Inc. This file is licensed under | ||
7 | * the terms of the GNU General Public License version 2. This program | ||
8 | * is licensed "as is" without any warranty of any kind, whether express | ||
9 | * or implied. | ||
10 | * | ||
11 | */ | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/smp.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/sysctl.h> | ||
17 | #include <linux/highmem.h> | ||
18 | #include <linux/timer.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/spinlock.h> | ||
21 | #include <linux/list.h> | ||
22 | #include <linux/sysdev.h> | ||
23 | #include <linux/ctype.h> | ||
24 | #include <linux/workqueue.h> | ||
25 | #include <asm/uaccess.h> | ||
26 | #include <asm/page.h> | ||
27 | |||
28 | #include "edac_core.h" | ||
29 | #include "edac_module.h" | ||
30 | |||
31 | static DEFINE_MUTEX(edac_pci_ctls_mutex); | ||
32 | static struct list_head edac_pci_list = LIST_HEAD_INIT(edac_pci_list); | ||
33 | |||
34 | static inline void edac_lock_pci_list(void) | ||
35 | { | ||
36 | mutex_lock(&edac_pci_ctls_mutex); | ||
37 | } | ||
38 | |||
39 | static inline void edac_unlock_pci_list(void) | ||
40 | { | ||
41 | mutex_unlock(&edac_pci_ctls_mutex); | ||
42 | } | ||
43 | |||
44 | /* | ||
45 | * The alloc() and free() functions for the 'edac_pci' control info | ||
46 | * structure. The chip driver will allocate one of these for each | ||
47 | * edac_pci it is going to control/register with the EDAC CORE. | ||
48 | */ | ||
49 | struct edac_pci_ctl_info * edac_pci_alloc_ctl_info( | ||
50 | unsigned int sz_pvt, | ||
51 | const char *edac_pci_name) | ||
52 | { | ||
53 | struct edac_pci_ctl_info *pci; | ||
54 | void *pvt; | ||
55 | unsigned int size; | ||
56 | |||
57 | pci = (struct edac_pci_ctl_info *)0; | ||
58 | pvt = edac_align_ptr(&pci[1], sz_pvt); | ||
59 | size = ((unsigned long)pvt) + sz_pvt; | ||
60 | |||
61 | if ((pci = kzalloc(size, GFP_KERNEL)) == NULL) | ||
62 | return NULL; | ||
63 | |||
64 | pvt = sz_pvt ? ((char *)pci) + ((unsigned long)pvt) : NULL; | ||
65 | |||
66 | pci->pvt_info = pvt; | ||
67 | |||
68 | pci->op_state = OP_ALLOC; | ||
69 | |||
70 | snprintf(pci->name, strlen(edac_pci_name)+1, "%s", edac_pci_name); | ||
71 | |||
72 | return pci; | ||
73 | } | ||
74 | EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info); | ||
75 | |||
76 | /* | ||
77 | * edac_pci_free_ctl_info() | ||
78 | * frees the memory allocated by edac_pci_alloc_ctl_info() function | ||
79 | */ | ||
80 | void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci) | ||
81 | { | ||
82 | kfree(pci); | ||
83 | } | ||
84 | EXPORT_SYMBOL_GPL(edac_pci_free_ctl_info); | ||
85 | |||
86 | /* | ||
87 | * find_edac_pci_by_dev() | ||
88 | * scans the edac_pci list for a specific 'struct device *' | ||
89 | */ | ||
90 | static struct edac_pci_ctl_info * find_edac_pci_by_dev(struct device *dev) | ||
91 | { | ||
92 | struct edac_pci_ctl_info *pci; | ||
93 | struct list_head *item; | ||
94 | |||
95 | debugf3("%s()\n", __func__); | ||
96 | |||
97 | list_for_each(item, &edac_pci_list) { | ||
98 | pci = list_entry(item, struct edac_pci_ctl_info, link); | ||
99 | |||
100 | if (pci->dev == dev) | ||
101 | return pci; | ||
102 | } | ||
103 | |||
104 | return NULL; | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * add_edac_pci_to_global_list | ||
109 | * Before calling this function, caller must assign a unique value to | ||
110 | * edac_dev->pci_idx. | ||
111 | * Return: | ||
112 | * 0 on success | ||
113 | * 1 on failure | ||
114 | */ | ||
115 | static int add_edac_pci_to_global_list(struct edac_pci_ctl_info *pci) | ||
116 | { | ||
117 | struct list_head *item, *insert_before; | ||
118 | struct edac_pci_ctl_info *rover; | ||
119 | |||
120 | insert_before = &edac_pci_list; | ||
121 | |||
122 | /* Determine if already on the list */ | ||
123 | if (unlikely((rover = find_edac_pci_by_dev(pci->dev)) != NULL)) | ||
124 | goto fail0; | ||
125 | |||
126 | /* Insert in ascending order by 'pci_idx', so find position */ | ||
127 | list_for_each(item, &edac_pci_list) { | ||
128 | rover = list_entry(item, struct edac_pci_ctl_info, link); | ||
129 | |||
130 | if (rover->pci_idx >= pci->pci_idx) { | ||
131 | if (unlikely(rover->pci_idx == pci->pci_idx)) | ||
132 | goto fail1; | ||
133 | |||
134 | insert_before = item; | ||
135 | break; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | list_add_tail_rcu(&pci->link, insert_before); | ||
140 | return 0; | ||
141 | |||
142 | fail0: | ||
143 | edac_printk(KERN_WARNING, EDAC_PCI, | ||
144 | "%s (%s) %s %s already assigned %d\n", | ||
145 | rover->dev->bus_id, dev_name(rover), | ||
146 | rover->mod_name, rover->ctl_name, rover->pci_idx); | ||
147 | return 1; | ||
148 | |||
149 | fail1: | ||
150 | edac_printk(KERN_WARNING, EDAC_PCI, | ||
151 | "but in low-level driver: attempt to assign\n" | ||
152 | "\tduplicate pci_idx %d in %s()\n", rover->pci_idx, __func__); | ||
153 | return 1; | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * complete_edac_pci_list_del | ||
158 | */ | ||
159 | static void complete_edac_pci_list_del(struct rcu_head *head) | ||
160 | { | ||
161 | struct edac_pci_ctl_info *pci; | ||
162 | |||
163 | pci = container_of(head, struct edac_pci_ctl_info, rcu); | ||
164 | INIT_LIST_HEAD(&pci->link); | ||
165 | complete(&pci->complete); | ||
166 | } | ||
167 | |||
168 | /* | ||
169 | * del_edac_pci_from_global_list | ||
170 | */ | ||
171 | static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci) | ||
172 | { | ||
173 | list_del_rcu(&pci->link); | ||
174 | init_completion(&pci->complete); | ||
175 | call_rcu(&pci->rcu, complete_edac_pci_list_del); | ||
176 | wait_for_completion(&pci->complete); | ||
177 | } | ||
178 | |||
179 | /* | ||
180 | * edac_pci_find() | ||
181 | * Search for an edac_pci_ctl_info structure whose index is 'idx' | ||
182 | * | ||
183 | * If found, return a pointer to the structure | ||
184 | * Else return NULL. | ||
185 | * | ||
186 | * Caller must hold pci_ctls_mutex. | ||
187 | */ | ||
188 | struct edac_pci_ctl_info * edac_pci_find(int idx) | ||
189 | { | ||
190 | struct list_head *item; | ||
191 | struct edac_pci_ctl_info *pci; | ||
192 | |||
193 | /* Iterage over list, looking for exact match of ID */ | ||
194 | list_for_each(item, &edac_pci_list) { | ||
195 | pci = list_entry(item, struct edac_pci_ctl_info, link); | ||
196 | |||
197 | if (pci->pci_idx >= idx) { | ||
198 | if (pci->pci_idx == idx) | ||
199 | return pci; | ||
200 | |||
201 | /* not on list, so terminate early */ | ||
202 | break; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | return NULL; | ||
207 | } | ||
208 | EXPORT_SYMBOL_GPL(edac_pci_find); | ||
209 | |||
210 | /* | ||
211 | * edac_pci_workq_function() | ||
212 | * performs the operation scheduled by a workq request | ||
213 | */ | ||
214 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) | ||
215 | static void edac_pci_workq_function(struct work_struct *work_req) | ||
216 | { | ||
217 | struct delayed_work *d_work = (struct delayed_work *)work_req; | ||
218 | struct edac_pci_ctl_info *pci = to_edac_pci_ctl_work(d_work); | ||
219 | #else | ||
220 | static void edac_pci_workq_function(void *ptr) | ||
221 | { | ||
222 | struct edac_pci_ctl_info *pci = ptr; | ||
223 | #endif | ||
224 | |||
225 | edac_lock_pci_list(); | ||
226 | |||
227 | if ((pci->op_state == OP_RUNNING_POLL) && | ||
228 | (pci->edac_check != NULL) && | ||
229 | (pci->check_parity_error)) | ||
230 | pci->edac_check(pci); | ||
231 | |||
232 | edac_unlock_pci_list(); | ||
233 | |||
234 | /* Reschedule */ | ||
235 | queue_delayed_work(edac_workqueue, &pci->work, pci->delay); | ||
236 | } | ||
237 | |||
238 | /* | ||
239 | * edac_pci_workq_setup() | ||
240 | * initialize a workq item for this edac_pci instance | ||
241 | * passing in the new delay period in msec | ||
242 | */ | ||
243 | static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci, | ||
244 | unsigned int msec) | ||
245 | { | ||
246 | debugf0("%s()\n", __func__); | ||
247 | |||
248 | pci->poll_msec = msec; | ||
249 | edac_calc_delay(pci); | ||
250 | |||
251 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) | ||
252 | INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function); | ||
253 | #else | ||
254 | INIT_WORK(&pci->work, edac_pci_workq_function, pci); | ||
255 | #endif | ||
256 | queue_delayed_work(edac_workqueue, &pci->work, pci->delay); | ||
257 | } | ||
258 | |||
259 | /* | ||
260 | * edac_pci_workq_teardown() | ||
261 | * stop the workq processing on this edac_pci instance | ||
262 | */ | ||
263 | static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci) | ||
264 | { | ||
265 | int status; | ||
266 | |||
267 | status = cancel_delayed_work(&pci->work); | ||
268 | if (status == 0) | ||
269 | flush_workqueue(edac_workqueue); | ||
270 | } | ||
271 | |||
272 | /* | ||
273 | * edac_pci_reset_delay_period | ||
274 | */ | ||
275 | void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci, | ||
276 | unsigned long value) | ||
277 | { | ||
278 | edac_lock_pci_list(); | ||
279 | |||
280 | edac_pci_workq_teardown(pci); | ||
281 | |||
282 | edac_pci_workq_setup(pci, value); | ||
283 | |||
284 | edac_unlock_pci_list(); | ||
285 | } | ||
286 | EXPORT_SYMBOL_GPL(edac_pci_reset_delay_period); | ||
287 | |||
288 | /* | ||
289 | * edac_pci_add_device: Insert the 'edac_dev' structure into the | ||
290 | * edac_pci global list and create sysfs entries associated with | ||
291 | * edac_pci structure. | ||
292 | * @pci: pointer to the edac_device structure to be added to the list | ||
293 | * @edac_idx: A unique numeric identifier to be assigned to the | ||
294 | * 'edac_pci' structure. | ||
295 | * | ||
296 | * Return: | ||
297 | * 0 Success | ||
298 | * !0 Failure | ||
299 | */ | ||
300 | int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx) | ||
301 | { | ||
302 | debugf0("%s()\n", __func__); | ||
303 | |||
304 | pci->pci_idx = edac_idx; | ||
305 | |||
306 | edac_lock_pci_list(); | ||
307 | |||
308 | if (add_edac_pci_to_global_list(pci)) | ||
309 | goto fail0; | ||
310 | |||
311 | pci->start_time = jiffies; | ||
312 | |||
313 | if (edac_pci_create_sysfs(pci)) { | ||
314 | edac_pci_printk(pci, KERN_WARNING, | ||
315 | "failed to create sysfs pci\n"); | ||
316 | goto fail1; | ||
317 | } | ||
318 | |||
319 | if (pci->edac_check != NULL) { | ||
320 | pci->op_state = OP_RUNNING_POLL; | ||
321 | |||
322 | edac_pci_workq_setup(pci, 1000); | ||
323 | } else { | ||
324 | pci->op_state = OP_RUNNING_INTERRUPT; | ||
325 | } | ||
326 | |||
327 | edac_pci_printk(pci, KERN_INFO, | ||
328 | "Giving out device to module '%s' controller '%s':" | ||
329 | " DEV '%s' (%s)\n", | ||
330 | pci->mod_name, | ||
331 | pci->ctl_name, | ||
332 | dev_name(pci), | ||
333 | edac_op_state_toString(pci->op_state)); | ||
334 | |||
335 | edac_unlock_pci_list(); | ||
336 | return 0; | ||
337 | |||
338 | fail1: | ||
339 | del_edac_pci_from_global_list(pci); | ||
340 | fail0: | ||
341 | edac_unlock_pci_list(); | ||
342 | return 1; | ||
343 | } | ||
344 | EXPORT_SYMBOL_GPL(edac_pci_add_device); | ||
345 | |||
346 | /* | ||
347 | * edac_pci_del_device() | ||
348 | * Remove sysfs entries for specified edac_pci structure and | ||
349 | * then remove edac_pci structure from global list | ||
350 | * | ||
351 | * @dev: | ||
352 | * Pointer to 'struct device' representing edac_pci structure | ||
353 | * to remove | ||
354 | * | ||
355 | * Return: | ||
356 | * Pointer to removed edac_pci structure, | ||
357 | * or NULL if device not found | ||
358 | */ | ||
359 | struct edac_pci_ctl_info * edac_pci_del_device(struct device *dev) | ||
360 | { | ||
361 | struct edac_pci_ctl_info *pci; | ||
362 | |||
363 | debugf0("%s()\n", __func__); | ||
364 | |||
365 | edac_lock_pci_list(); | ||
366 | |||
367 | if ((pci = find_edac_pci_by_dev(dev)) == NULL) { | ||
368 | edac_unlock_pci_list(); | ||
369 | return NULL; | ||
370 | } | ||
371 | |||
372 | pci->op_state = OP_OFFLINE; | ||
373 | |||
374 | edac_pci_workq_teardown(pci); | ||
375 | |||
376 | edac_pci_remove_sysfs(pci); | ||
377 | |||
378 | del_edac_pci_from_global_list(pci); | ||
379 | |||
380 | edac_unlock_pci_list(); | ||
381 | |||
382 | edac_printk(KERN_INFO, EDAC_PCI, | ||
383 | "Removed device %d for %s %s: DEV %s\n", | ||
384 | pci->pci_idx, | ||
385 | pci->mod_name, | ||
386 | pci->ctl_name, | ||
387 | dev_name(pci)); | ||
388 | |||
389 | return pci; | ||
390 | } | ||
391 | EXPORT_SYMBOL_GPL(edac_pci_del_device); | ||
392 | |||
393 | static inline int edac_pci_get_log_pe(struct edac_pci_ctl_info *pci) | ||
394 | { | ||
395 | return pci->log_parity_error; | ||
396 | } | ||
397 | |||
398 | static inline int edac_pci_get_panic_on_pe(struct edac_pci_ctl_info *pci) | ||
399 | { | ||
400 | return pci->panic_on_pe; | ||
401 | } | ||
402 | |||
403 | void edac_pci_generic_check(struct edac_pci_ctl_info *pci) | ||
404 | { | ||
405 | edac_pci_do_parity_check(); | ||
406 | } | ||
407 | |||
408 | static int edac_pci_idx = 0; | ||
409 | #define EDAC_PCI_GENCTL_NAME "EDAC PCI controller" | ||
410 | |||
411 | struct edac_pci_gen_data { | ||
412 | int edac_idx; | ||
413 | }; | ||
414 | |||
415 | struct edac_pci_ctl_info * | ||
416 | edac_pci_create_generic_ctl(struct device *dev, const char *mod_name) | ||
417 | { | ||
418 | struct edac_pci_ctl_info *pci; | ||
419 | struct edac_pci_gen_data *pdata; | ||
420 | |||
421 | pci = edac_pci_alloc_ctl_info(sizeof(*pdata), EDAC_PCI_GENCTL_NAME); | ||
422 | if (!pci) | ||
423 | return NULL; | ||
424 | |||
425 | pdata = pci->pvt_info; | ||
426 | pci->dev = dev; | ||
427 | dev_set_drvdata(pci->dev, pci); | ||
428 | pci->dev_name = pci_name(to_pci_dev(dev)); | ||
429 | |||
430 | pci->mod_name = mod_name; | ||
431 | pci->ctl_name = EDAC_PCI_GENCTL_NAME; | ||
432 | pci->edac_check = edac_pci_generic_check; | ||
433 | |||
434 | pdata->edac_idx = edac_pci_idx++; | ||
435 | |||
436 | if (edac_pci_add_device(pci, pdata->edac_idx) > 0) { | ||
437 | debugf3("%s(): failed edac_pci_add_device()\n", __func__); | ||
438 | edac_pci_free_ctl_info(pci); | ||
439 | return NULL; | ||
440 | } | ||
441 | |||
442 | return pci; | ||
443 | } | ||
444 | EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl); | ||
445 | |||
446 | void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci) | ||
447 | { | ||
448 | edac_pci_del_device(pci->dev); | ||
449 | edac_pci_free_ctl_info(pci); | ||
450 | } | ||
451 | EXPORT_SYMBOL_GPL(edac_pci_release_generic_ctl); | ||
diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c index 9388eaa794be..0b179e0fd112 100644 --- a/drivers/edac/edac_pci_sysfs.c +++ b/drivers/edac/edac_pci_sysfs.c | |||
@@ -15,13 +15,142 @@ | |||
15 | 15 | ||
16 | 16 | ||
17 | #ifdef CONFIG_PCI | 17 | #ifdef CONFIG_PCI |
18 | static int check_pci_parity = 0; /* default YES check PCI parity */ | 18 | |
19 | static int panic_on_pci_parity; /* default no panic on PCI Parity */ | 19 | #define EDAC_PCI_SYMLINK "device" |
20 | |||
21 | static int check_pci_errors = 0; /* default YES check PCI parity */ | ||
22 | static int panic_on_pci_parity = 0; /* default no panic on PCI Parity */ | ||
23 | static int log_pci_errs = 1; | ||
20 | static atomic_t pci_parity_count = ATOMIC_INIT(0); | 24 | static atomic_t pci_parity_count = ATOMIC_INIT(0); |
25 | static atomic_t pci_nonparity_count = ATOMIC_INIT(0); | ||
21 | 26 | ||
22 | static struct kobject edac_pci_kobj; /* /sys/devices/system/edac/pci */ | 27 | static struct kobject edac_pci_kobj; /* /sys/devices/system/edac/pci */ |
23 | static struct completion edac_pci_kobj_complete; | 28 | static struct completion edac_pci_kobj_complete; |
29 | static atomic_t edac_pci_sysfs_refcount = ATOMIC_INIT(0); | ||
30 | |||
31 | /**************************** EDAC PCI sysfs instance *******************/ | ||
32 | static ssize_t instance_pe_count_show(struct edac_pci_ctl_info *pci, char *data) | ||
33 | { | ||
34 | return sprintf(data,"%u\n", atomic_read(&pci->counters.pe_count)); | ||
35 | } | ||
36 | |||
37 | static ssize_t instance_npe_count_show(struct edac_pci_ctl_info *pci, | ||
38 | char *data) | ||
39 | { | ||
40 | return sprintf(data,"%u\n", atomic_read(&pci->counters.npe_count)); | ||
41 | } | ||
42 | |||
43 | #define to_instance(k) container_of(k, struct edac_pci_ctl_info, kobj) | ||
44 | #define to_instance_attr(a) container_of(a, struct instance_attribute, attr) | ||
45 | |||
46 | /* DEVICE instance kobject release() function */ | ||
47 | static void edac_pci_instance_release(struct kobject *kobj) | ||
48 | { | ||
49 | struct edac_pci_ctl_info *pci; | ||
50 | |||
51 | debugf1("%s()\n", __func__); | ||
52 | |||
53 | pci = to_instance(kobj); | ||
54 | complete(&pci->kobj_complete); | ||
55 | } | ||
56 | |||
57 | /* instance specific attribute structure */ | ||
58 | struct instance_attribute { | ||
59 | struct attribute attr; | ||
60 | ssize_t (*show)(struct edac_pci_ctl_info *, char *); | ||
61 | ssize_t (*store)(struct edac_pci_ctl_info *, const char *, size_t); | ||
62 | }; | ||
63 | |||
64 | /* Function to 'show' fields from the edac_pci 'instance' structure */ | ||
65 | static ssize_t edac_pci_instance_show(struct kobject *kobj, | ||
66 | struct attribute *attr, | ||
67 | char *buffer) | ||
68 | { | ||
69 | struct edac_pci_ctl_info *pci = to_instance(kobj); | ||
70 | struct instance_attribute *instance_attr = to_instance_attr(attr); | ||
71 | |||
72 | if (instance_attr->show) | ||
73 | return instance_attr->show(pci, buffer); | ||
74 | return -EIO; | ||
75 | } | ||
76 | |||
77 | |||
78 | /* Function to 'store' fields into the edac_pci 'instance' structure */ | ||
79 | static ssize_t edac_pci_instance_store(struct kobject *kobj, | ||
80 | struct attribute *attr, | ||
81 | const char *buffer, size_t count) | ||
82 | { | ||
83 | struct edac_pci_ctl_info *pci = to_instance(kobj); | ||
84 | struct instance_attribute *instance_attr = to_instance_attr(attr); | ||
85 | |||
86 | if (instance_attr->store) | ||
87 | return instance_attr->store(pci, buffer, count); | ||
88 | return -EIO; | ||
89 | } | ||
90 | |||
91 | static struct sysfs_ops pci_instance_ops = { | ||
92 | .show = edac_pci_instance_show, | ||
93 | .store = edac_pci_instance_store | ||
94 | }; | ||
95 | |||
96 | #define INSTANCE_ATTR(_name, _mode, _show, _store) \ | ||
97 | static struct instance_attribute attr_instance_##_name = { \ | ||
98 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
99 | .show = _show, \ | ||
100 | .store = _store, \ | ||
101 | }; | ||
102 | |||
103 | INSTANCE_ATTR(pe_count, S_IRUGO, instance_pe_count_show, NULL); | ||
104 | INSTANCE_ATTR(npe_count, S_IRUGO, instance_npe_count_show, NULL); | ||
105 | |||
106 | /* pci instance attributes */ | ||
107 | static struct instance_attribute *pci_instance_attr[] = { | ||
108 | &attr_instance_pe_count, | ||
109 | &attr_instance_npe_count, | ||
110 | NULL | ||
111 | }; | ||
24 | 112 | ||
113 | /* the ktype for pci instance */ | ||
114 | static struct kobj_type ktype_pci_instance = { | ||
115 | .release = edac_pci_instance_release, | ||
116 | .sysfs_ops = &pci_instance_ops, | ||
117 | .default_attrs = (struct attribute **)pci_instance_attr, | ||
118 | }; | ||
119 | |||
120 | static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx) | ||
121 | { | ||
122 | int err; | ||
123 | |||
124 | pci->kobj.parent = &edac_pci_kobj; | ||
125 | pci->kobj.ktype = &ktype_pci_instance; | ||
126 | |||
127 | err = kobject_set_name(&pci->kobj, "pci%d", idx); | ||
128 | if (err) | ||
129 | return err; | ||
130 | |||
131 | err = kobject_register(&pci->kobj); | ||
132 | if (err != 0) { | ||
133 | debugf2("%s() failed to register instance pci%d\n", | ||
134 | __func__, idx); | ||
135 | return err; | ||
136 | } | ||
137 | |||
138 | debugf1("%s() Register instance 'pci%d' kobject\n", __func__, idx); | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | static void | ||
144 | edac_pci_delete_instance_kobj(struct edac_pci_ctl_info *pci, int idx) | ||
145 | { | ||
146 | init_completion(&pci->kobj_complete); | ||
147 | kobject_unregister(&pci->kobj); | ||
148 | wait_for_completion(&pci->kobj_complete); | ||
149 | } | ||
150 | |||
151 | /***************************** EDAC PCI sysfs root **********************/ | ||
152 | #define to_edacpci(k) container_of(k, struct edac_pci_ctl_info, kobj) | ||
153 | #define to_edacpci_attr(a) container_of(a, struct edac_pci_attr, attr) | ||
25 | 154 | ||
26 | static ssize_t edac_pci_int_show(void *ptr, char *buffer) | 155 | static ssize_t edac_pci_int_show(void *ptr, char *buffer) |
27 | { | 156 | { |
@@ -91,25 +220,34 @@ static struct edac_pci_dev_attribute edac_pci_attr_##_name = { \ | |||
91 | }; | 220 | }; |
92 | 221 | ||
93 | /* PCI Parity control files */ | 222 | /* PCI Parity control files */ |
94 | EDAC_PCI_ATTR(check_pci_parity, S_IRUGO|S_IWUSR, edac_pci_int_show, | 223 | EDAC_PCI_ATTR(check_pci_errors, S_IRUGO|S_IWUSR, edac_pci_int_show, |
224 | edac_pci_int_store); | ||
225 | EDAC_PCI_ATTR(log_pci_errs, S_IRUGO|S_IWUSR, edac_pci_int_show, | ||
95 | edac_pci_int_store); | 226 | edac_pci_int_store); |
96 | EDAC_PCI_ATTR(panic_on_pci_parity, S_IRUGO|S_IWUSR, edac_pci_int_show, | 227 | EDAC_PCI_ATTR(panic_on_pci_parity, S_IRUGO|S_IWUSR, edac_pci_int_show, |
97 | edac_pci_int_store); | 228 | edac_pci_int_store); |
98 | EDAC_PCI_ATTR(pci_parity_count, S_IRUGO, edac_pci_int_show, NULL); | 229 | EDAC_PCI_ATTR(pci_parity_count, S_IRUGO, edac_pci_int_show, NULL); |
230 | EDAC_PCI_ATTR(pci_nonparity_count, S_IRUGO, edac_pci_int_show, NULL); | ||
99 | 231 | ||
100 | /* Base Attributes of the memory ECC object */ | 232 | /* Base Attributes of the memory ECC object */ |
101 | static struct edac_pci_dev_attribute *edac_pci_attr[] = { | 233 | static struct edac_pci_dev_attribute *edac_pci_attr[] = { |
102 | &edac_pci_attr_check_pci_parity, | 234 | &edac_pci_attr_check_pci_errors, |
235 | &edac_pci_attr_log_pci_errs, | ||
103 | &edac_pci_attr_panic_on_pci_parity, | 236 | &edac_pci_attr_panic_on_pci_parity, |
104 | &edac_pci_attr_pci_parity_count, | 237 | &edac_pci_attr_pci_parity_count, |
238 | &edac_pci_attr_pci_nonparity_count, | ||
105 | NULL, | 239 | NULL, |
106 | }; | 240 | }; |
107 | 241 | ||
108 | /* No memory to release */ | 242 | /* No memory to release */ |
109 | static void edac_pci_release(struct kobject *kobj) | 243 | static void edac_pci_release(struct kobject *kobj) |
110 | { | 244 | { |
245 | struct edac_pci_ctl_info *pci; | ||
246 | |||
247 | pci = to_edacpci(kobj); | ||
248 | |||
111 | debugf1("%s()\n", __func__); | 249 | debugf1("%s()\n", __func__); |
112 | complete(&edac_pci_kobj_complete); | 250 | complete(&pci->kobj_complete); |
113 | } | 251 | } |
114 | 252 | ||
115 | static struct kobj_type ktype_edac_pci = { | 253 | static struct kobj_type ktype_edac_pci = { |
@@ -124,7 +262,7 @@ static struct kobj_type ktype_edac_pci = { | |||
124 | * setup the sysfs for EDAC PCI attributes | 262 | * setup the sysfs for EDAC PCI attributes |
125 | * assumes edac_class has already been initialized | 263 | * assumes edac_class has already been initialized |
126 | */ | 264 | */ |
127 | int edac_sysfs_pci_setup(void) | 265 | int edac_pci_register_main_kobj(void) |
128 | { | 266 | { |
129 | int err; | 267 | int err; |
130 | struct sysdev_class *edac_class; | 268 | struct sysdev_class *edac_class; |
@@ -132,32 +270,39 @@ int edac_sysfs_pci_setup(void) | |||
132 | debugf1("%s()\n", __func__); | 270 | debugf1("%s()\n", __func__); |
133 | 271 | ||
134 | edac_class = edac_get_edac_class(); | 272 | edac_class = edac_get_edac_class(); |
273 | if (edac_class == NULL) { | ||
274 | debugf1("%s() no edac_class\n", __func__); | ||
275 | return -ENODEV; | ||
276 | } | ||
135 | 277 | ||
136 | memset(&edac_pci_kobj, 0, sizeof(edac_pci_kobj)); | ||
137 | edac_pci_kobj.parent = &edac_class->kset.kobj; | ||
138 | edac_pci_kobj.ktype = &ktype_edac_pci; | 278 | edac_pci_kobj.ktype = &ktype_edac_pci; |
279 | |||
280 | edac_pci_kobj.parent = &edac_class->kset.kobj; | ||
281 | |||
139 | err = kobject_set_name(&edac_pci_kobj, "pci"); | 282 | err = kobject_set_name(&edac_pci_kobj, "pci"); |
283 | if(err) | ||
284 | return err; | ||
140 | 285 | ||
141 | if (!err) { | 286 | /* Instanstiate the pci object */ |
142 | /* Instanstiate the pci object */ | 287 | /* FIXME: maybe new sysdev_create_subdir() */ |
143 | /* FIXME: maybe new sysdev_create_subdir() */ | 288 | err = kobject_register(&edac_pci_kobj); |
144 | err = kobject_register(&edac_pci_kobj); | ||
145 | 289 | ||
146 | if (err) | 290 | if (err) { |
147 | debugf1("Failed to register '.../edac/pci'\n"); | 291 | debugf1("Failed to register '.../edac/pci'\n"); |
148 | else | 292 | return err; |
149 | debugf1("Registered '.../edac/pci' kobject\n"); | ||
150 | } | 293 | } |
151 | 294 | ||
152 | return err; | 295 | debugf1("Registered '.../edac/pci' kobject\n"); |
296 | |||
297 | return 0; | ||
153 | } | 298 | } |
154 | 299 | ||
155 | /* | 300 | /* |
156 | * edac_sysfs_pci_teardown | 301 | * edac_pci_unregister_main_kobj() |
157 | * | 302 | * |
158 | * perform the sysfs teardown for the PCI attributes | 303 | * perform the sysfs teardown for the PCI attributes |
159 | */ | 304 | */ |
160 | void edac_sysfs_pci_teardown(void) | 305 | void edac_pci_unregister_main_kobj(void) |
161 | { | 306 | { |
162 | debugf0("%s()\n", __func__); | 307 | debugf0("%s()\n", __func__); |
163 | init_completion(&edac_pci_kobj_complete); | 308 | init_completion(&edac_pci_kobj_complete); |
@@ -165,7 +310,53 @@ void edac_sysfs_pci_teardown(void) | |||
165 | wait_for_completion(&edac_pci_kobj_complete); | 310 | wait_for_completion(&edac_pci_kobj_complete); |
166 | } | 311 | } |
167 | 312 | ||
313 | int edac_pci_create_sysfs(struct edac_pci_ctl_info *pci) | ||
314 | { | ||
315 | int err; | ||
316 | struct kobject *edac_kobj = &pci->kobj; | ||
317 | |||
318 | if (atomic_inc_return(&edac_pci_sysfs_refcount) == 1) { | ||
319 | err = edac_pci_register_main_kobj(); | ||
320 | if (err) { | ||
321 | atomic_dec(&edac_pci_sysfs_refcount); | ||
322 | return err; | ||
323 | } | ||
324 | } | ||
325 | |||
326 | err = edac_pci_create_instance_kobj(pci, pci->pci_idx); | ||
327 | if (err) { | ||
328 | if (atomic_dec_return(&edac_pci_sysfs_refcount) == 0) | ||
329 | edac_pci_unregister_main_kobj(); | ||
330 | } | ||
168 | 331 | ||
332 | |||
333 | debugf0("%s() idx=%d\n", __func__, pci->pci_idx); | ||
334 | |||
335 | err = sysfs_create_link(edac_kobj, | ||
336 | &pci->dev->kobj, | ||
337 | EDAC_PCI_SYMLINK); | ||
338 | if (err) { | ||
339 | debugf0("%s() sysfs_create_link() returned err= %d\n", | ||
340 | __func__, err); | ||
341 | return err; | ||
342 | } | ||
343 | |||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci) | ||
348 | { | ||
349 | debugf0("%s()\n", __func__); | ||
350 | |||
351 | edac_pci_delete_instance_kobj(pci, pci->pci_idx); | ||
352 | |||
353 | sysfs_remove_link(&pci->kobj, EDAC_PCI_SYMLINK); | ||
354 | |||
355 | if (atomic_dec_return(&edac_pci_sysfs_refcount) == 0) | ||
356 | edac_pci_unregister_main_kobj(); | ||
357 | } | ||
358 | |||
359 | /************************ PCI error handling *************************/ | ||
169 | static u16 get_pci_parity_status(struct pci_dev *dev, int secondary) | 360 | static u16 get_pci_parity_status(struct pci_dev *dev, int secondary) |
170 | { | 361 | { |
171 | int where; | 362 | int where; |
@@ -231,10 +422,12 @@ static void edac_pci_dev_parity_test(struct pci_dev *dev) | |||
231 | 422 | ||
232 | /* check the status reg for errors */ | 423 | /* check the status reg for errors */ |
233 | if (status) { | 424 | if (status) { |
234 | if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) | 425 | if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) { |
235 | edac_printk(KERN_CRIT, EDAC_PCI, | 426 | edac_printk(KERN_CRIT, EDAC_PCI, |
236 | "Signaled System Error on %s\n", | 427 | "Signaled System Error on %s\n", |
237 | pci_name(dev)); | 428 | pci_name(dev)); |
429 | atomic_inc(&pci_nonparity_count); | ||
430 | } | ||
238 | 431 | ||
239 | if (status & (PCI_STATUS_PARITY)) { | 432 | if (status & (PCI_STATUS_PARITY)) { |
240 | edac_printk(KERN_CRIT, EDAC_PCI, | 433 | edac_printk(KERN_CRIT, EDAC_PCI, |
@@ -267,10 +460,12 @@ static void edac_pci_dev_parity_test(struct pci_dev *dev) | |||
267 | 460 | ||
268 | /* check the secondary status reg for errors */ | 461 | /* check the secondary status reg for errors */ |
269 | if (status) { | 462 | if (status) { |
270 | if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) | 463 | if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) { |
271 | edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " | 464 | edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " |
272 | "Signaled System Error on %s\n", | 465 | "Signaled System Error on %s\n", |
273 | pci_name(dev)); | 466 | pci_name(dev)); |
467 | atomic_inc(&pci_nonparity_count); | ||
468 | } | ||
274 | 469 | ||
275 | if (status & (PCI_STATUS_PARITY)) { | 470 | if (status & (PCI_STATUS_PARITY)) { |
276 | edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " | 471 | edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " |
@@ -321,7 +516,7 @@ void edac_pci_do_parity_check(void) | |||
321 | 516 | ||
322 | debugf3("%s()\n", __func__); | 517 | debugf3("%s()\n", __func__); |
323 | 518 | ||
324 | if (!check_pci_parity) | 519 | if (!check_pci_errors) |
325 | return; | 520 | return; |
326 | 521 | ||
327 | before_count = atomic_read(&pci_parity_count); | 522 | before_count = atomic_read(&pci_parity_count); |
@@ -348,13 +543,49 @@ void edac_pci_clear_parity_errors(void) | |||
348 | */ | 543 | */ |
349 | edac_pci_dev_parity_iterator(edac_pci_dev_parity_clear); | 544 | edac_pci_dev_parity_iterator(edac_pci_dev_parity_clear); |
350 | } | 545 | } |
546 | void edac_pci_handle_pe(struct edac_pci_ctl_info *pci, const char *msg) | ||
547 | { | ||
548 | |||
549 | /* global PE counter incremented by edac_pci_do_parity_check() */ | ||
550 | atomic_inc(&pci->counters.pe_count); | ||
551 | |||
552 | if (log_pci_errs) | ||
553 | edac_pci_printk(pci, KERN_WARNING, | ||
554 | "Parity Error ctl: %s %d: %s\n", | ||
555 | pci->ctl_name, pci->pci_idx, msg); | ||
556 | |||
557 | /* | ||
558 | * poke all PCI devices and see which one is the troublemaker | ||
559 | * panic() is called if set | ||
560 | */ | ||
561 | edac_pci_do_parity_check(); | ||
562 | } | ||
563 | EXPORT_SYMBOL_GPL(edac_pci_handle_pe); | ||
351 | 564 | ||
565 | void edac_pci_handle_npe(struct edac_pci_ctl_info *pci, const char *msg) | ||
566 | { | ||
567 | |||
568 | /* global NPE counter incremented by edac_pci_do_parity_check() */ | ||
569 | atomic_inc(&pci->counters.npe_count); | ||
570 | |||
571 | if (log_pci_errs) | ||
572 | edac_pci_printk(pci, KERN_WARNING, | ||
573 | "Non-Parity Error ctl: %s %d: %s\n", | ||
574 | pci->ctl_name, pci->pci_idx, msg); | ||
575 | |||
576 | /* | ||
577 | * poke all PCI devices and see which one is the troublemaker | ||
578 | * panic() is called if set | ||
579 | */ | ||
580 | edac_pci_do_parity_check(); | ||
581 | } | ||
582 | EXPORT_SYMBOL_GPL(edac_pci_handle_npe); | ||
352 | 583 | ||
353 | /* | 584 | /* |
354 | * Define the PCI parameter to the module | 585 | * Define the PCI parameter to the module |
355 | */ | 586 | */ |
356 | module_param(check_pci_parity, int, 0644); | 587 | module_param(check_pci_errors, int, 0644); |
357 | MODULE_PARM_DESC(check_pci_parity, "Check for PCI bus parity errors: 0=off 1=on"); | 588 | MODULE_PARM_DESC(check_pci_errors, "Check for PCI bus parity errors: 0=off 1=on"); |
358 | module_param(panic_on_pci_parity, int, 0644); | 589 | module_param(panic_on_pci_parity, int, 0644); |
359 | MODULE_PARM_DESC(panic_on_pci_parity, "Panic on PCI Bus Parity error: 0=off 1=on"); | 590 | MODULE_PARM_DESC(panic_on_pci_parity, "Panic on PCI Bus Parity error: 0=off 1=on"); |
360 | 591 | ||