diff options
| -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 547ea135b6..6a5e5d18db 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 10f8499547..d8b86584af 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 b73d659a4b..febff41114 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 3f4c8a2815..3db8effa1f 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 dc900ed751..38e4a71380 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 0000000000..677c603f55 --- /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 9388eaa794..0b179e0fd1 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 | ||
