diff options
| -rw-r--r-- | drivers/edac/Makefile | 7 | ||||
| -rw-r--r-- | drivers/edac/edac_core.h | 478 | ||||
| -rw-r--r-- | drivers/edac/edac_mc.c | 1317 | ||||
| -rw-r--r-- | drivers/edac/edac_mc.h | 471 | ||||
| -rw-r--r-- | drivers/edac/edac_mc_sysfs.c | 889 | ||||
| -rw-r--r-- | drivers/edac/edac_module.c | 130 | ||||
| -rw-r--r-- | drivers/edac/edac_module.h | 55 | ||||
| -rw-r--r-- | drivers/edac/edac_pci_sysfs.c | 361 |
8 files changed, 1932 insertions, 1776 deletions
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 93137fdab4b3..51f59aa84d30 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile | |||
| @@ -8,7 +8,12 @@ | |||
| 8 | # $Id: Makefile,v 1.4.2.3 2005/07/08 22:05:38 dsp_llnl Exp $ | 8 | # $Id: Makefile,v 1.4.2.3 2005/07/08 22:05:38 dsp_llnl Exp $ |
| 9 | 9 | ||
| 10 | 10 | ||
| 11 | obj-$(CONFIG_EDAC_MM_EDAC) += edac_mc.o | 11 | obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o |
| 12 | |||
| 13 | edac_core-objs := edac_mc.o edac_mc_sysfs.o edac_pci_sysfs.o | ||
| 14 | |||
| 15 | edac_core-objs += edac_module.o | ||
| 16 | |||
| 12 | obj-$(CONFIG_EDAC_AMD76X) += amd76x_edac.o | 17 | obj-$(CONFIG_EDAC_AMD76X) += amd76x_edac.o |
| 13 | obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o | 18 | obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o |
| 14 | obj-$(CONFIG_EDAC_E752X) += e752x_edac.o | 19 | obj-$(CONFIG_EDAC_E752X) += e752x_edac.o |
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h new file mode 100644 index 000000000000..397f144791ec --- /dev/null +++ b/drivers/edac/edac_core.h | |||
| @@ -0,0 +1,478 @@ | |||
| 1 | /* | ||
| 2 | * Defines, structures, APIs for edac_core module | ||
| 3 | * | ||
| 4 | * (C) 2007 Linux Networx (http://lnxi.com) | ||
| 5 | * This file may be distributed under the terms of the | ||
| 6 | * GNU General Public License. | ||
| 7 | * | ||
| 8 | * Written by Thayne Harbaugh | ||
| 9 | * Based on work by Dan Hollis <goemon at anime dot net> and others. | ||
| 10 | * http://www.anime.net/~goemon/linux-ecc/ | ||
| 11 | * | ||
| 12 | * NMI handling support added by | ||
| 13 | * Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com> | ||
| 14 | * | ||
| 15 | * Refactored for multi-source files: | ||
| 16 | * Doug Thompson <norsk5@xmission.com> | ||
| 17 | * | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef _EDAC_CORE_H_ | ||
| 21 | #define _EDAC_CORE_H_ | ||
| 22 | |||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/types.h> | ||
| 25 | #include <linux/module.h> | ||
| 26 | #include <linux/spinlock.h> | ||
| 27 | #include <linux/smp.h> | ||
| 28 | #include <linux/pci.h> | ||
| 29 | #include <linux/time.h> | ||
| 30 | #include <linux/nmi.h> | ||
| 31 | #include <linux/rcupdate.h> | ||
| 32 | #include <linux/completion.h> | ||
| 33 | #include <linux/kobject.h> | ||
| 34 | #include <linux/platform_device.h> | ||
| 35 | |||
| 36 | #define EDAC_MC_LABEL_LEN 31 | ||
| 37 | #define MC_PROC_NAME_MAX_LEN 7 | ||
| 38 | |||
| 39 | #if PAGE_SHIFT < 20 | ||
| 40 | #define PAGES_TO_MiB( pages ) ( ( pages ) >> ( 20 - PAGE_SHIFT ) ) | ||
| 41 | #else /* PAGE_SHIFT > 20 */ | ||
| 42 | #define PAGES_TO_MiB( pages ) ( ( pages ) << ( PAGE_SHIFT - 20 ) ) | ||
| 43 | #endif | ||
| 44 | |||
| 45 | #define edac_printk(level, prefix, fmt, arg...) \ | ||
| 46 | printk(level "EDAC " prefix ": " fmt, ##arg) | ||
| 47 | |||
| 48 | #define edac_mc_printk(mci, level, fmt, arg...) \ | ||
| 49 | printk(level "EDAC MC%d: " fmt, mci->mc_idx, ##arg) | ||
| 50 | |||
| 51 | #define edac_mc_chipset_printk(mci, level, prefix, fmt, arg...) \ | ||
| 52 | printk(level "EDAC " prefix " MC%d: " fmt, mci->mc_idx, ##arg) | ||
| 53 | |||
| 54 | /* prefixes for edac_printk() and edac_mc_printk() */ | ||
| 55 | #define EDAC_MC "MC" | ||
| 56 | #define EDAC_PCI "PCI" | ||
| 57 | #define EDAC_DEBUG "DEBUG" | ||
| 58 | |||
| 59 | #ifdef CONFIG_EDAC_DEBUG | ||
| 60 | extern int edac_debug_level; | ||
| 61 | |||
| 62 | #define edac_debug_printk(level, fmt, arg...) \ | ||
| 63 | do { \ | ||
| 64 | if (level <= edac_debug_level) \ | ||
| 65 | edac_printk(KERN_DEBUG, EDAC_DEBUG, fmt, ##arg); \ | ||
| 66 | } while(0) | ||
| 67 | |||
| 68 | #define debugf0( ... ) edac_debug_printk(0, __VA_ARGS__ ) | ||
| 69 | #define debugf1( ... ) edac_debug_printk(1, __VA_ARGS__ ) | ||
| 70 | #define debugf2( ... ) edac_debug_printk(2, __VA_ARGS__ ) | ||
| 71 | #define debugf3( ... ) edac_debug_printk(3, __VA_ARGS__ ) | ||
| 72 | #define debugf4( ... ) edac_debug_printk(4, __VA_ARGS__ ) | ||
| 73 | |||
| 74 | #else /* !CONFIG_EDAC_DEBUG */ | ||
| 75 | |||
| 76 | #define debugf0( ... ) | ||
| 77 | #define debugf1( ... ) | ||
| 78 | #define debugf2( ... ) | ||
| 79 | #define debugf3( ... ) | ||
| 80 | #define debugf4( ... ) | ||
| 81 | |||
| 82 | #endif /* !CONFIG_EDAC_DEBUG */ | ||
| 83 | |||
| 84 | #define BIT(x) (1 << (x)) | ||
| 85 | |||
| 86 | #define PCI_VEND_DEV(vend, dev) PCI_VENDOR_ID_ ## vend, \ | ||
| 87 | PCI_DEVICE_ID_ ## vend ## _ ## dev | ||
| 88 | |||
| 89 | #if defined(CONFIG_X86) && defined(CONFIG_PCI) | ||
| 90 | #define dev_name(dev) pci_name(to_pci_dev(dev)) | ||
| 91 | #else | ||
| 92 | #define dev_name(dev) to_platform_device(dev)->name | ||
| 93 | #endif | ||
| 94 | |||
| 95 | /* memory devices */ | ||
| 96 | enum dev_type { | ||
| 97 | DEV_UNKNOWN = 0, | ||
| 98 | DEV_X1, | ||
| 99 | DEV_X2, | ||
| 100 | DEV_X4, | ||
| 101 | DEV_X8, | ||
| 102 | DEV_X16, | ||
| 103 | DEV_X32, /* Do these parts exist? */ | ||
| 104 | DEV_X64 /* Do these parts exist? */ | ||
| 105 | }; | ||
| 106 | |||
| 107 | #define DEV_FLAG_UNKNOWN BIT(DEV_UNKNOWN) | ||
| 108 | #define DEV_FLAG_X1 BIT(DEV_X1) | ||
| 109 | #define DEV_FLAG_X2 BIT(DEV_X2) | ||
| 110 | #define DEV_FLAG_X4 BIT(DEV_X4) | ||
| 111 | #define DEV_FLAG_X8 BIT(DEV_X8) | ||
| 112 | #define DEV_FLAG_X16 BIT(DEV_X16) | ||
| 113 | #define DEV_FLAG_X32 BIT(DEV_X32) | ||
| 114 | #define DEV_FLAG_X64 BIT(DEV_X64) | ||
| 115 | |||
| 116 | /* memory types */ | ||
| 117 | enum mem_type { | ||
| 118 | MEM_EMPTY = 0, /* Empty csrow */ | ||
| 119 | MEM_RESERVED, /* Reserved csrow type */ | ||
| 120 | MEM_UNKNOWN, /* Unknown csrow type */ | ||
| 121 | MEM_FPM, /* Fast page mode */ | ||
| 122 | MEM_EDO, /* Extended data out */ | ||
| 123 | MEM_BEDO, /* Burst Extended data out */ | ||
| 124 | MEM_SDR, /* Single data rate SDRAM */ | ||
| 125 | MEM_RDR, /* Registered single data rate SDRAM */ | ||
| 126 | MEM_DDR, /* Double data rate SDRAM */ | ||
| 127 | MEM_RDDR, /* Registered Double data rate SDRAM */ | ||
| 128 | MEM_RMBS, /* Rambus DRAM */ | ||
| 129 | MEM_DDR2, /* DDR2 RAM */ | ||
| 130 | MEM_FB_DDR2, /* fully buffered DDR2 */ | ||
| 131 | MEM_RDDR2, /* Registered DDR2 RAM */ | ||
| 132 | }; | ||
| 133 | |||
| 134 | #define MEM_FLAG_EMPTY BIT(MEM_EMPTY) | ||
| 135 | #define MEM_FLAG_RESERVED BIT(MEM_RESERVED) | ||
| 136 | #define MEM_FLAG_UNKNOWN BIT(MEM_UNKNOWN) | ||
| 137 | #define MEM_FLAG_FPM BIT(MEM_FPM) | ||
| 138 | #define MEM_FLAG_EDO BIT(MEM_EDO) | ||
| 139 | #define MEM_FLAG_BEDO BIT(MEM_BEDO) | ||
| 140 | #define MEM_FLAG_SDR BIT(MEM_SDR) | ||
| 141 | #define MEM_FLAG_RDR BIT(MEM_RDR) | ||
| 142 | #define MEM_FLAG_DDR BIT(MEM_DDR) | ||
| 143 | #define MEM_FLAG_RDDR BIT(MEM_RDDR) | ||
| 144 | #define MEM_FLAG_RMBS BIT(MEM_RMBS) | ||
| 145 | #define MEM_FLAG_DDR2 BIT(MEM_DDR2) | ||
| 146 | #define MEM_FLAG_FB_DDR2 BIT(MEM_FB_DDR2) | ||
| 147 | #define MEM_FLAG_RDDR2 BIT(MEM_RDDR2) | ||
| 148 | |||
| 149 | /* chipset Error Detection and Correction capabilities and mode */ | ||
| 150 | enum edac_type { | ||
| 151 | EDAC_UNKNOWN = 0, /* Unknown if ECC is available */ | ||
| 152 | EDAC_NONE, /* Doesnt support ECC */ | ||
| 153 | EDAC_RESERVED, /* Reserved ECC type */ | ||
| 154 | EDAC_PARITY, /* Detects parity errors */ | ||
| 155 | EDAC_EC, /* Error Checking - no correction */ | ||
| 156 | EDAC_SECDED, /* Single bit error correction, Double detection */ | ||
| 157 | EDAC_S2ECD2ED, /* Chipkill x2 devices - do these exist? */ | ||
| 158 | EDAC_S4ECD4ED, /* Chipkill x4 devices */ | ||
| 159 | EDAC_S8ECD8ED, /* Chipkill x8 devices */ | ||
| 160 | EDAC_S16ECD16ED, /* Chipkill x16 devices */ | ||
| 161 | }; | ||
| 162 | |||
| 163 | #define EDAC_FLAG_UNKNOWN BIT(EDAC_UNKNOWN) | ||
| 164 | #define EDAC_FLAG_NONE BIT(EDAC_NONE) | ||
| 165 | #define EDAC_FLAG_PARITY BIT(EDAC_PARITY) | ||
| 166 | #define EDAC_FLAG_EC BIT(EDAC_EC) | ||
| 167 | #define EDAC_FLAG_SECDED BIT(EDAC_SECDED) | ||
| 168 | #define EDAC_FLAG_S2ECD2ED BIT(EDAC_S2ECD2ED) | ||
| 169 | #define EDAC_FLAG_S4ECD4ED BIT(EDAC_S4ECD4ED) | ||
| 170 | #define EDAC_FLAG_S8ECD8ED BIT(EDAC_S8ECD8ED) | ||
| 171 | #define EDAC_FLAG_S16ECD16ED BIT(EDAC_S16ECD16ED) | ||
| 172 | |||
| 173 | /* scrubbing capabilities */ | ||
| 174 | enum scrub_type { | ||
| 175 | SCRUB_UNKNOWN = 0, /* Unknown if scrubber is available */ | ||
| 176 | SCRUB_NONE, /* No scrubber */ | ||
| 177 | SCRUB_SW_PROG, /* SW progressive (sequential) scrubbing */ | ||
| 178 | SCRUB_SW_SRC, /* Software scrub only errors */ | ||
| 179 | SCRUB_SW_PROG_SRC, /* Progressive software scrub from an error */ | ||
| 180 | SCRUB_SW_TUNABLE, /* Software scrub frequency is tunable */ | ||
| 181 | SCRUB_HW_PROG, /* HW progressive (sequential) scrubbing */ | ||
| 182 | SCRUB_HW_SRC, /* Hardware scrub only errors */ | ||
| 183 | SCRUB_HW_PROG_SRC, /* Progressive hardware scrub from an error */ | ||
| 184 | SCRUB_HW_TUNABLE /* Hardware scrub frequency is tunable */ | ||
| 185 | }; | ||
| 186 | |||
| 187 | #define SCRUB_FLAG_SW_PROG BIT(SCRUB_SW_PROG) | ||
| 188 | #define SCRUB_FLAG_SW_SRC BIT(SCRUB_SW_SRC_CORR) | ||
| 189 | #define SCRUB_FLAG_SW_PROG_SRC BIT(SCRUB_SW_PROG_SRC_CORR) | ||
| 190 | #define SCRUB_FLAG_SW_TUN BIT(SCRUB_SW_SCRUB_TUNABLE) | ||
| 191 | #define SCRUB_FLAG_HW_PROG BIT(SCRUB_HW_PROG) | ||
| 192 | #define SCRUB_FLAG_HW_SRC BIT(SCRUB_HW_SRC_CORR) | ||
| 193 | #define SCRUB_FLAG_HW_PROG_SRC BIT(SCRUB_HW_PROG_SRC_CORR) | ||
| 194 | #define SCRUB_FLAG_HW_TUN BIT(SCRUB_HW_TUNABLE) | ||
| 195 | |||
| 196 | /* FIXME - should have notify capabilities: NMI, LOG, PROC, etc */ | ||
| 197 | |||
| 198 | /* | ||
| 199 | * There are several things to be aware of that aren't at all obvious: | ||
| 200 | * | ||
| 201 | * | ||
| 202 | * SOCKETS, SOCKET SETS, BANKS, ROWS, CHIP-SELECT ROWS, CHANNELS, etc.. | ||
| 203 | * | ||
| 204 | * These are some of the many terms that are thrown about that don't always | ||
| 205 | * mean what people think they mean (Inconceivable!). In the interest of | ||
| 206 | * creating a common ground for discussion, terms and their definitions | ||
| 207 | * will be established. | ||
| 208 | * | ||
| 209 | * Memory devices: The individual chip on a memory stick. These devices | ||
| 210 | * commonly output 4 and 8 bits each. Grouping several | ||
| 211 | * of these in parallel provides 64 bits which is common | ||
| 212 | * for a memory stick. | ||
| 213 | * | ||
| 214 | * Memory Stick: A printed circuit board that agregates multiple | ||
| 215 | * memory devices in parallel. This is the atomic | ||
| 216 | * memory component that is purchaseable by Joe consumer | ||
| 217 | * and loaded into a memory socket. | ||
| 218 | * | ||
| 219 | * Socket: A physical connector on the motherboard that accepts | ||
| 220 | * a single memory stick. | ||
| 221 | * | ||
| 222 | * Channel: Set of memory devices on a memory stick that must be | ||
| 223 | * grouped in parallel with one or more additional | ||
| 224 | * channels from other memory sticks. This parallel | ||
| 225 | * grouping of the output from multiple channels are | ||
| 226 | * necessary for the smallest granularity of memory access. | ||
| 227 | * Some memory controllers are capable of single channel - | ||
| 228 | * which means that memory sticks can be loaded | ||
| 229 | * individually. Other memory controllers are only | ||
| 230 | * capable of dual channel - which means that memory | ||
| 231 | * sticks must be loaded as pairs (see "socket set"). | ||
| 232 | * | ||
| 233 | * Chip-select row: All of the memory devices that are selected together. | ||
| 234 | * for a single, minimum grain of memory access. | ||
| 235 | * This selects all of the parallel memory devices across | ||
| 236 | * all of the parallel channels. Common chip-select rows | ||
| 237 | * for single channel are 64 bits, for dual channel 128 | ||
| 238 | * bits. | ||
| 239 | * | ||
| 240 | * Single-Ranked stick: A Single-ranked stick has 1 chip-select row of memmory. | ||
| 241 | * Motherboards commonly drive two chip-select pins to | ||
| 242 | * a memory stick. A single-ranked stick, will occupy | ||
| 243 | * only one of those rows. The other will be unused. | ||
| 244 | * | ||
| 245 | * Double-Ranked stick: A double-ranked stick has two chip-select rows which | ||
| 246 | * access different sets of memory devices. The two | ||
| 247 | * rows cannot be accessed concurrently. | ||
| 248 | * | ||
| 249 | * Double-sided stick: DEPRECATED TERM, see Double-Ranked stick. | ||
| 250 | * A double-sided stick has two chip-select rows which | ||
| 251 | * access different sets of memory devices. The two | ||
| 252 | * rows cannot be accessed concurrently. "Double-sided" | ||
| 253 | * is irrespective of the memory devices being mounted | ||
| 254 | * on both sides of the memory stick. | ||
| 255 | * | ||
| 256 | * Socket set: All of the memory sticks that are required for for | ||
| 257 | * a single memory access or all of the memory sticks | ||
| 258 | * spanned by a chip-select row. A single socket set | ||
| 259 | * has two chip-select rows and if double-sided sticks | ||
| 260 | * are used these will occupy those chip-select rows. | ||
| 261 | * | ||
| 262 | * Bank: This term is avoided because it is unclear when | ||
| 263 | * needing to distinguish between chip-select rows and | ||
| 264 | * socket sets. | ||
| 265 | * | ||
| 266 | * Controller pages: | ||
| 267 | * | ||
| 268 | * Physical pages: | ||
| 269 | * | ||
| 270 | * Virtual pages: | ||
| 271 | * | ||
| 272 | * | ||
| 273 | * STRUCTURE ORGANIZATION AND CHOICES | ||
| 274 | * | ||
| 275 | * | ||
| 276 | * | ||
| 277 | * PS - I enjoyed writing all that about as much as you enjoyed reading it. | ||
| 278 | */ | ||
| 279 | |||
| 280 | struct channel_info { | ||
| 281 | int chan_idx; /* channel index */ | ||
| 282 | u32 ce_count; /* Correctable Errors for this CHANNEL */ | ||
| 283 | char label[EDAC_MC_LABEL_LEN + 1]; /* DIMM label on motherboard */ | ||
| 284 | struct csrow_info *csrow; /* the parent */ | ||
| 285 | }; | ||
| 286 | |||
| 287 | struct csrow_info { | ||
| 288 | unsigned long first_page; /* first page number in dimm */ | ||
| 289 | unsigned long last_page; /* last page number in dimm */ | ||
| 290 | unsigned long page_mask; /* used for interleaving - | ||
| 291 | * 0UL for non intlv | ||
| 292 | */ | ||
| 293 | u32 nr_pages; /* number of pages in csrow */ | ||
| 294 | u32 grain; /* granularity of reported error in bytes */ | ||
| 295 | int csrow_idx; /* the chip-select row */ | ||
| 296 | enum dev_type dtype; /* memory device type */ | ||
| 297 | u32 ue_count; /* Uncorrectable Errors for this csrow */ | ||
| 298 | u32 ce_count; /* Correctable Errors for this csrow */ | ||
| 299 | enum mem_type mtype; /* memory csrow type */ | ||
| 300 | enum edac_type edac_mode; /* EDAC mode for this csrow */ | ||
| 301 | struct mem_ctl_info *mci; /* the parent */ | ||
| 302 | |||
| 303 | struct kobject kobj; /* sysfs kobject for this csrow */ | ||
| 304 | struct completion kobj_complete; | ||
| 305 | |||
| 306 | /* FIXME the number of CHANNELs might need to become dynamic */ | ||
| 307 | u32 nr_channels; | ||
| 308 | struct channel_info *channels; | ||
| 309 | }; | ||
| 310 | |||
| 311 | struct mem_ctl_info { | ||
| 312 | struct list_head link; /* for global list of mem_ctl_info structs */ | ||
| 313 | unsigned long mtype_cap; /* memory types supported by mc */ | ||
| 314 | unsigned long edac_ctl_cap; /* Mem controller EDAC capabilities */ | ||
| 315 | unsigned long edac_cap; /* configuration capabilities - this is | ||
| 316 | * closely related to edac_ctl_cap. The | ||
| 317 | * difference is that the controller may be | ||
| 318 | * capable of s4ecd4ed which would be listed | ||
| 319 | * in edac_ctl_cap, but if channels aren't | ||
| 320 | * capable of s4ecd4ed then the edac_cap would | ||
| 321 | * not have that capability. | ||
| 322 | */ | ||
| 323 | unsigned long scrub_cap; /* chipset scrub capabilities */ | ||
| 324 | enum scrub_type scrub_mode; /* current scrub mode */ | ||
| 325 | |||
| 326 | /* Translates sdram memory scrub rate given in bytes/sec to the | ||
| 327 | internal representation and configures whatever else needs | ||
| 328 | to be configured. | ||
| 329 | */ | ||
| 330 | int (*set_sdram_scrub_rate) (struct mem_ctl_info *mci, u32 *bw); | ||
| 331 | |||
| 332 | /* Get the current sdram memory scrub rate from the internal | ||
| 333 | representation and converts it to the closest matching | ||
| 334 | bandwith in bytes/sec. | ||
| 335 | */ | ||
| 336 | int (*get_sdram_scrub_rate) (struct mem_ctl_info *mci, u32 *bw); | ||
| 337 | |||
| 338 | /* pointer to edac checking routine */ | ||
| 339 | void (*edac_check) (struct mem_ctl_info * mci); | ||
| 340 | |||
| 341 | /* | ||
| 342 | * Remaps memory pages: controller pages to physical pages. | ||
| 343 | * For most MC's, this will be NULL. | ||
| 344 | */ | ||
| 345 | /* FIXME - why not send the phys page to begin with? */ | ||
| 346 | unsigned long (*ctl_page_to_phys) (struct mem_ctl_info * mci, | ||
| 347 | unsigned long page); | ||
| 348 | int mc_idx; | ||
| 349 | int nr_csrows; | ||
| 350 | struct csrow_info *csrows; | ||
| 351 | /* | ||
| 352 | * FIXME - what about controllers on other busses? - IDs must be | ||
| 353 | * unique. dev pointer should be sufficiently unique, but | ||
| 354 | * BUS:SLOT.FUNC numbers may not be unique. | ||
| 355 | */ | ||
| 356 | struct device *dev; | ||
| 357 | const char *mod_name; | ||
| 358 | const char *mod_ver; | ||
| 359 | const char *ctl_name; | ||
| 360 | char proc_name[MC_PROC_NAME_MAX_LEN + 1]; | ||
| 361 | void *pvt_info; | ||
| 362 | u32 ue_noinfo_count; /* Uncorrectable Errors w/o info */ | ||
| 363 | u32 ce_noinfo_count; /* Correctable Errors w/o info */ | ||
| 364 | u32 ue_count; /* Total Uncorrectable Errors for this MC */ | ||
| 365 | u32 ce_count; /* Total Correctable Errors for this MC */ | ||
| 366 | unsigned long start_time; /* mci load start time (in jiffies) */ | ||
| 367 | |||
| 368 | /* this stuff is for safe removal of mc devices from global list while | ||
| 369 | * NMI handlers may be traversing list | ||
| 370 | */ | ||
| 371 | struct rcu_head rcu; | ||
| 372 | struct completion complete; | ||
| 373 | |||
| 374 | /* edac sysfs device control */ | ||
| 375 | struct kobject edac_mci_kobj; | ||
| 376 | struct completion kobj_complete; | ||
| 377 | }; | ||
| 378 | |||
| 379 | #ifdef CONFIG_PCI | ||
| 380 | |||
| 381 | /* write all or some bits in a byte-register*/ | ||
| 382 | static inline void pci_write_bits8(struct pci_dev *pdev, int offset, u8 value, | ||
| 383 | u8 mask) | ||
| 384 | { | ||
| 385 | if (mask != 0xff) { | ||
| 386 | u8 buf; | ||
| 387 | |||
| 388 | pci_read_config_byte(pdev, offset, &buf); | ||
| 389 | value &= mask; | ||
| 390 | buf &= ~mask; | ||
| 391 | value |= buf; | ||
| 392 | } | ||
| 393 | |||
| 394 | pci_write_config_byte(pdev, offset, value); | ||
| 395 | } | ||
| 396 | |||
| 397 | /* write all or some bits in a word-register*/ | ||
| 398 | static inline void pci_write_bits16(struct pci_dev *pdev, int offset, | ||
| 399 | u16 value, u16 mask) | ||
| 400 | { | ||
| 401 | if (mask != 0xffff) { | ||
| 402 | u16 buf; | ||
| 403 | |||
| 404 | pci_read_config_word(pdev, offset, &buf); | ||
| 405 | value &= mask; | ||
| 406 | buf &= ~mask; | ||
| 407 | value |= buf; | ||
| 408 | } | ||
| 409 | |||
| 410 | pci_write_config_word(pdev, offset, value); | ||
| 411 | } | ||
| 412 | |||
| 413 | /* write all or some bits in a dword-register*/ | ||
| 414 | static inline void pci_write_bits32(struct pci_dev *pdev, int offset, | ||
| 415 | u32 value, u32 mask) | ||
| 416 | { | ||
| 417 | if (mask != 0xffff) { | ||
| 418 | u32 buf; | ||
| 419 | |||
| 420 | pci_read_config_dword(pdev, offset, &buf); | ||
| 421 | value &= mask; | ||
| 422 | buf &= ~mask; | ||
| 423 | value |= buf; | ||
| 424 | } | ||
| 425 | |||
| 426 | pci_write_config_dword(pdev, offset, value); | ||
| 427 | } | ||
| 428 | |||
| 429 | #endif /* CONFIG_PCI */ | ||
| 430 | |||
| 431 | extern struct mem_ctl_info * edac_mc_find(int idx); | ||
| 432 | extern int edac_mc_add_mc(struct mem_ctl_info *mci,int mc_idx); | ||
| 433 | extern struct mem_ctl_info * edac_mc_del_mc(struct device *dev); | ||
| 434 | extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, | ||
| 435 | unsigned long page); | ||
| 436 | |||
| 437 | /* | ||
| 438 | * The no info errors are used when error overflows are reported. | ||
| 439 | * There are a limited number of error logging registers that can | ||
| 440 | * be exausted. When all registers are exhausted and an additional | ||
| 441 | * error occurs then an error overflow register records that an | ||
| 442 | * error occured and the type of error, but doesn't have any | ||
| 443 | * further information. The ce/ue versions make for cleaner | ||
| 444 | * reporting logic and function interface - reduces conditional | ||
| 445 | * statement clutter and extra function arguments. | ||
| 446 | */ | ||
| 447 | extern void edac_mc_handle_ce(struct mem_ctl_info *mci, | ||
| 448 | unsigned long page_frame_number, unsigned long offset_in_page, | ||
| 449 | unsigned long syndrome, int row, int channel, | ||
| 450 | const char *msg); | ||
| 451 | extern void edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, | ||
| 452 | const char *msg); | ||
| 453 | extern void edac_mc_handle_ue(struct mem_ctl_info *mci, | ||
| 454 | unsigned long page_frame_number, unsigned long offset_in_page, | ||
| 455 | int row, const char *msg); | ||
| 456 | extern void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, | ||
| 457 | const char *msg); | ||
| 458 | extern void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci, | ||
| 459 | unsigned int csrow, | ||
| 460 | unsigned int channel0, | ||
| 461 | unsigned int channel1, | ||
| 462 | char *msg); | ||
| 463 | extern void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci, | ||
| 464 | unsigned int csrow, | ||
| 465 | unsigned int channel, | ||
| 466 | char *msg); | ||
| 467 | |||
| 468 | /* | ||
| 469 | * This kmalloc's and initializes all the structures. | ||
| 470 | * Can't be used if all structures don't have the same lifetime. | ||
| 471 | */ | ||
| 472 | extern struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows, | ||
| 473 | unsigned nr_chans); | ||
| 474 | |||
| 475 | /* Free an mc previously allocated by edac_mc_alloc() */ | ||
| 476 | extern void edac_mc_free(struct mem_ctl_info *mci); | ||
| 477 | |||
| 478 | #endif /* _EDAC_CORE_H_ */ | ||
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 88bee33e7ecf..3be5b7fe79cd 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c | |||
| @@ -27,1197 +27,17 @@ | |||
| 27 | #include <linux/list.h> | 27 | #include <linux/list.h> |
| 28 | #include <linux/sysdev.h> | 28 | #include <linux/sysdev.h> |
| 29 | #include <linux/ctype.h> | 29 | #include <linux/ctype.h> |
| 30 | #include <linux/kthread.h> | ||
| 31 | #include <linux/freezer.h> | ||
| 32 | #include <asm/uaccess.h> | 30 | #include <asm/uaccess.h> |
| 33 | #include <asm/page.h> | 31 | #include <asm/page.h> |
| 34 | #include <asm/edac.h> | 32 | #include <asm/edac.h> |
| 35 | #include "edac_mc.h" | 33 | #include "edac_mc.h" |
| 34 | #include "edac_module.h" | ||
| 36 | 35 | ||
| 37 | #define EDAC_MC_VERSION "Ver: 2.0.1 " __DATE__ | ||
| 38 | |||
| 39 | |||
| 40 | #ifdef CONFIG_EDAC_DEBUG | ||
| 41 | /* Values of 0 to 4 will generate output */ | ||
| 42 | int edac_debug_level = 1; | ||
| 43 | EXPORT_SYMBOL_GPL(edac_debug_level); | ||
| 44 | #endif | ||
| 45 | |||
| 46 | /* EDAC Controls, setable by module parameter, and sysfs */ | ||
| 47 | static int log_ue = 1; | ||
| 48 | static int log_ce = 1; | ||
| 49 | static int panic_on_ue; | ||
| 50 | static int poll_msec = 1000; | ||
| 51 | 36 | ||
| 52 | /* lock to memory controller's control array */ | 37 | /* lock to memory controller's control array */ |
| 53 | static DECLARE_MUTEX(mem_ctls_mutex); | 38 | static DECLARE_MUTEX(mem_ctls_mutex); |
| 54 | static struct list_head mc_devices = LIST_HEAD_INIT(mc_devices); | 39 | static struct list_head mc_devices = LIST_HEAD_INIT(mc_devices); |
| 55 | 40 | ||
| 56 | static struct task_struct *edac_thread; | ||
| 57 | |||
| 58 | #ifdef CONFIG_PCI | ||
| 59 | static int check_pci_parity = 0; /* default YES check PCI parity */ | ||
| 60 | static int panic_on_pci_parity; /* default no panic on PCI Parity */ | ||
| 61 | static atomic_t pci_parity_count = ATOMIC_INIT(0); | ||
| 62 | |||
| 63 | static struct kobject edac_pci_kobj; /* /sys/devices/system/edac/pci */ | ||
| 64 | static struct completion edac_pci_kobj_complete; | ||
| 65 | #endif /* CONFIG_PCI */ | ||
| 66 | |||
| 67 | /* START sysfs data and methods */ | ||
| 68 | |||
| 69 | |||
| 70 | static const char *mem_types[] = { | ||
| 71 | [MEM_EMPTY] = "Empty", | ||
| 72 | [MEM_RESERVED] = "Reserved", | ||
| 73 | [MEM_UNKNOWN] = "Unknown", | ||
| 74 | [MEM_FPM] = "FPM", | ||
| 75 | [MEM_EDO] = "EDO", | ||
| 76 | [MEM_BEDO] = "BEDO", | ||
| 77 | [MEM_SDR] = "Unbuffered-SDR", | ||
| 78 | [MEM_RDR] = "Registered-SDR", | ||
| 79 | [MEM_DDR] = "Unbuffered-DDR", | ||
| 80 | [MEM_RDDR] = "Registered-DDR", | ||
| 81 | [MEM_RMBS] = "RMBS" | ||
| 82 | }; | ||
| 83 | |||
| 84 | static const char *dev_types[] = { | ||
| 85 | [DEV_UNKNOWN] = "Unknown", | ||
| 86 | [DEV_X1] = "x1", | ||
| 87 | [DEV_X2] = "x2", | ||
| 88 | [DEV_X4] = "x4", | ||
| 89 | [DEV_X8] = "x8", | ||
| 90 | [DEV_X16] = "x16", | ||
| 91 | [DEV_X32] = "x32", | ||
| 92 | [DEV_X64] = "x64" | ||
| 93 | }; | ||
| 94 | |||
| 95 | static const char *edac_caps[] = { | ||
| 96 | [EDAC_UNKNOWN] = "Unknown", | ||
| 97 | [EDAC_NONE] = "None", | ||
| 98 | [EDAC_RESERVED] = "Reserved", | ||
| 99 | [EDAC_PARITY] = "PARITY", | ||
| 100 | [EDAC_EC] = "EC", | ||
| 101 | [EDAC_SECDED] = "SECDED", | ||
| 102 | [EDAC_S2ECD2ED] = "S2ECD2ED", | ||
| 103 | [EDAC_S4ECD4ED] = "S4ECD4ED", | ||
| 104 | [EDAC_S8ECD8ED] = "S8ECD8ED", | ||
| 105 | [EDAC_S16ECD16ED] = "S16ECD16ED" | ||
| 106 | }; | ||
| 107 | |||
| 108 | /* sysfs object: /sys/devices/system/edac */ | ||
| 109 | static struct sysdev_class edac_class = { | ||
| 110 | set_kset_name("edac"), | ||
| 111 | }; | ||
| 112 | |||
| 113 | /* sysfs object: | ||
| 114 | * /sys/devices/system/edac/mc | ||
| 115 | */ | ||
| 116 | static struct kobject edac_memctrl_kobj; | ||
| 117 | |||
| 118 | /* We use these to wait for the reference counts on edac_memctrl_kobj and | ||
| 119 | * edac_pci_kobj to reach 0. | ||
| 120 | */ | ||
| 121 | static struct completion edac_memctrl_kobj_complete; | ||
| 122 | |||
| 123 | /* | ||
| 124 | * /sys/devices/system/edac/mc; | ||
| 125 | * data structures and methods | ||
| 126 | */ | ||
| 127 | static ssize_t memctrl_int_show(void *ptr, char *buffer) | ||
| 128 | { | ||
| 129 | int *value = (int*) ptr; | ||
| 130 | return sprintf(buffer, "%u\n", *value); | ||
| 131 | } | ||
| 132 | |||
| 133 | static ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t count) | ||
| 134 | { | ||
| 135 | int *value = (int*) ptr; | ||
| 136 | |||
| 137 | if (isdigit(*buffer)) | ||
| 138 | *value = simple_strtoul(buffer, NULL, 0); | ||
| 139 | |||
| 140 | return count; | ||
| 141 | } | ||
| 142 | |||
| 143 | struct memctrl_dev_attribute { | ||
| 144 | struct attribute attr; | ||
| 145 | void *value; | ||
| 146 | ssize_t (*show)(void *,char *); | ||
| 147 | ssize_t (*store)(void *, const char *, size_t); | ||
| 148 | }; | ||
| 149 | |||
| 150 | /* Set of show/store abstract level functions for memory control object */ | ||
| 151 | static ssize_t memctrl_dev_show(struct kobject *kobj, | ||
| 152 | struct attribute *attr, char *buffer) | ||
| 153 | { | ||
| 154 | struct memctrl_dev_attribute *memctrl_dev; | ||
| 155 | memctrl_dev = (struct memctrl_dev_attribute*)attr; | ||
| 156 | |||
| 157 | if (memctrl_dev->show) | ||
| 158 | return memctrl_dev->show(memctrl_dev->value, buffer); | ||
| 159 | |||
| 160 | return -EIO; | ||
| 161 | } | ||
| 162 | |||
| 163 | static ssize_t memctrl_dev_store(struct kobject *kobj, struct attribute *attr, | ||
| 164 | const char *buffer, size_t count) | ||
| 165 | { | ||
| 166 | struct memctrl_dev_attribute *memctrl_dev; | ||
| 167 | memctrl_dev = (struct memctrl_dev_attribute*)attr; | ||
| 168 | |||
| 169 | if (memctrl_dev->store) | ||
| 170 | return memctrl_dev->store(memctrl_dev->value, buffer, count); | ||
| 171 | |||
| 172 | return -EIO; | ||
| 173 | } | ||
| 174 | |||
| 175 | static struct sysfs_ops memctrlfs_ops = { | ||
| 176 | .show = memctrl_dev_show, | ||
| 177 | .store = memctrl_dev_store | ||
| 178 | }; | ||
| 179 | |||
| 180 | #define MEMCTRL_ATTR(_name,_mode,_show,_store) \ | ||
| 181 | static struct memctrl_dev_attribute attr_##_name = { \ | ||
| 182 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
| 183 | .value = &_name, \ | ||
| 184 | .show = _show, \ | ||
| 185 | .store = _store, \ | ||
| 186 | }; | ||
| 187 | |||
| 188 | #define MEMCTRL_STRING_ATTR(_name,_data,_mode,_show,_store) \ | ||
| 189 | static struct memctrl_dev_attribute attr_##_name = { \ | ||
| 190 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
| 191 | .value = _data, \ | ||
| 192 | .show = _show, \ | ||
| 193 | .store = _store, \ | ||
| 194 | }; | ||
| 195 | |||
| 196 | /* csrow<id> control files */ | ||
| 197 | MEMCTRL_ATTR(panic_on_ue,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store); | ||
| 198 | MEMCTRL_ATTR(log_ue,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store); | ||
| 199 | MEMCTRL_ATTR(log_ce,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store); | ||
| 200 | MEMCTRL_ATTR(poll_msec,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store); | ||
| 201 | |||
| 202 | /* Base Attributes of the memory ECC object */ | ||
| 203 | static struct memctrl_dev_attribute *memctrl_attr[] = { | ||
| 204 | &attr_panic_on_ue, | ||
| 205 | &attr_log_ue, | ||
| 206 | &attr_log_ce, | ||
| 207 | &attr_poll_msec, | ||
| 208 | NULL, | ||
| 209 | }; | ||
| 210 | |||
| 211 | /* Main MC kobject release() function */ | ||
| 212 | static void edac_memctrl_master_release(struct kobject *kobj) | ||
| 213 | { | ||
| 214 | debugf1("%s()\n", __func__); | ||
| 215 | complete(&edac_memctrl_kobj_complete); | ||
| 216 | } | ||
| 217 | |||
| 218 | static struct kobj_type ktype_memctrl = { | ||
| 219 | .release = edac_memctrl_master_release, | ||
| 220 | .sysfs_ops = &memctrlfs_ops, | ||
| 221 | .default_attrs = (struct attribute **) memctrl_attr, | ||
| 222 | }; | ||
| 223 | |||
| 224 | /* Initialize the main sysfs entries for edac: | ||
| 225 | * /sys/devices/system/edac | ||
| 226 | * | ||
| 227 | * and children | ||
| 228 | * | ||
| 229 | * Return: 0 SUCCESS | ||
| 230 | * !0 FAILURE | ||
| 231 | */ | ||
| 232 | static int edac_sysfs_memctrl_setup(void) | ||
| 233 | { | ||
| 234 | int err = 0; | ||
| 235 | |||
| 236 | debugf1("%s()\n", __func__); | ||
| 237 | |||
| 238 | /* create the /sys/devices/system/edac directory */ | ||
| 239 | err = sysdev_class_register(&edac_class); | ||
| 240 | |||
| 241 | if (err) { | ||
| 242 | debugf1("%s() error=%d\n", __func__, err); | ||
| 243 | return err; | ||
| 244 | } | ||
| 245 | |||
| 246 | /* Init the MC's kobject */ | ||
| 247 | memset(&edac_memctrl_kobj, 0, sizeof (edac_memctrl_kobj)); | ||
| 248 | edac_memctrl_kobj.parent = &edac_class.kset.kobj; | ||
| 249 | edac_memctrl_kobj.ktype = &ktype_memctrl; | ||
| 250 | |||
| 251 | /* generate sysfs "..../edac/mc" */ | ||
| 252 | err = kobject_set_name(&edac_memctrl_kobj,"mc"); | ||
| 253 | |||
| 254 | if (err) | ||
| 255 | goto fail; | ||
| 256 | |||
| 257 | /* FIXME: maybe new sysdev_create_subdir() */ | ||
| 258 | err = kobject_register(&edac_memctrl_kobj); | ||
| 259 | |||
| 260 | if (err) { | ||
| 261 | debugf1("Failed to register '.../edac/mc'\n"); | ||
| 262 | goto fail; | ||
| 263 | } | ||
| 264 | |||
| 265 | debugf1("Registered '.../edac/mc' kobject\n"); | ||
| 266 | |||
| 267 | return 0; | ||
| 268 | |||
| 269 | fail: | ||
| 270 | sysdev_class_unregister(&edac_class); | ||
| 271 | return err; | ||
| 272 | } | ||
| 273 | |||
| 274 | /* | ||
| 275 | * MC teardown: | ||
| 276 | * the '..../edac/mc' kobject followed by '..../edac' itself | ||
| 277 | */ | ||
| 278 | static void edac_sysfs_memctrl_teardown(void) | ||
| 279 | { | ||
| 280 | debugf0("MC: " __FILE__ ": %s()\n", __func__); | ||
| 281 | |||
| 282 | /* Unregister the MC's kobject and wait for reference count to reach | ||
| 283 | * 0. | ||
| 284 | */ | ||
| 285 | init_completion(&edac_memctrl_kobj_complete); | ||
| 286 | kobject_unregister(&edac_memctrl_kobj); | ||
| 287 | wait_for_completion(&edac_memctrl_kobj_complete); | ||
| 288 | |||
| 289 | /* Unregister the 'edac' object */ | ||
| 290 | sysdev_class_unregister(&edac_class); | ||
| 291 | } | ||
| 292 | |||
| 293 | #ifdef CONFIG_PCI | ||
| 294 | static ssize_t edac_pci_int_show(void *ptr, char *buffer) | ||
| 295 | { | ||
| 296 | int *value = ptr; | ||
| 297 | return sprintf(buffer,"%d\n",*value); | ||
| 298 | } | ||
| 299 | |||
| 300 | static ssize_t edac_pci_int_store(void *ptr, const char *buffer, size_t count) | ||
| 301 | { | ||
| 302 | int *value = ptr; | ||
| 303 | |||
| 304 | if (isdigit(*buffer)) | ||
| 305 | *value = simple_strtoul(buffer,NULL,0); | ||
| 306 | |||
| 307 | return count; | ||
| 308 | } | ||
| 309 | |||
| 310 | struct edac_pci_dev_attribute { | ||
| 311 | struct attribute attr; | ||
| 312 | void *value; | ||
| 313 | ssize_t (*show)(void *,char *); | ||
| 314 | ssize_t (*store)(void *, const char *,size_t); | ||
| 315 | }; | ||
| 316 | |||
| 317 | /* Set of show/store abstract level functions for PCI Parity object */ | ||
| 318 | static ssize_t edac_pci_dev_show(struct kobject *kobj, struct attribute *attr, | ||
| 319 | char *buffer) | ||
| 320 | { | ||
| 321 | struct edac_pci_dev_attribute *edac_pci_dev; | ||
| 322 | edac_pci_dev= (struct edac_pci_dev_attribute*)attr; | ||
| 323 | |||
| 324 | if (edac_pci_dev->show) | ||
| 325 | return edac_pci_dev->show(edac_pci_dev->value, buffer); | ||
| 326 | return -EIO; | ||
| 327 | } | ||
| 328 | |||
| 329 | static ssize_t edac_pci_dev_store(struct kobject *kobj, | ||
| 330 | struct attribute *attr, const char *buffer, size_t count) | ||
| 331 | { | ||
| 332 | struct edac_pci_dev_attribute *edac_pci_dev; | ||
| 333 | edac_pci_dev= (struct edac_pci_dev_attribute*)attr; | ||
| 334 | |||
| 335 | if (edac_pci_dev->show) | ||
| 336 | return edac_pci_dev->store(edac_pci_dev->value, buffer, count); | ||
| 337 | return -EIO; | ||
| 338 | } | ||
| 339 | |||
| 340 | static struct sysfs_ops edac_pci_sysfs_ops = { | ||
| 341 | .show = edac_pci_dev_show, | ||
| 342 | .store = edac_pci_dev_store | ||
| 343 | }; | ||
| 344 | |||
| 345 | #define EDAC_PCI_ATTR(_name,_mode,_show,_store) \ | ||
| 346 | static struct edac_pci_dev_attribute edac_pci_attr_##_name = { \ | ||
| 347 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
| 348 | .value = &_name, \ | ||
| 349 | .show = _show, \ | ||
| 350 | .store = _store, \ | ||
| 351 | }; | ||
| 352 | |||
| 353 | #define EDAC_PCI_STRING_ATTR(_name,_data,_mode,_show,_store) \ | ||
| 354 | static struct edac_pci_dev_attribute edac_pci_attr_##_name = { \ | ||
| 355 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
| 356 | .value = _data, \ | ||
| 357 | .show = _show, \ | ||
| 358 | .store = _store, \ | ||
| 359 | }; | ||
| 360 | |||
| 361 | /* PCI Parity control files */ | ||
| 362 | EDAC_PCI_ATTR(check_pci_parity, S_IRUGO|S_IWUSR, edac_pci_int_show, | ||
| 363 | edac_pci_int_store); | ||
| 364 | EDAC_PCI_ATTR(panic_on_pci_parity, S_IRUGO|S_IWUSR, edac_pci_int_show, | ||
| 365 | edac_pci_int_store); | ||
| 366 | EDAC_PCI_ATTR(pci_parity_count, S_IRUGO, edac_pci_int_show, NULL); | ||
| 367 | |||
| 368 | /* Base Attributes of the memory ECC object */ | ||
| 369 | static struct edac_pci_dev_attribute *edac_pci_attr[] = { | ||
| 370 | &edac_pci_attr_check_pci_parity, | ||
| 371 | &edac_pci_attr_panic_on_pci_parity, | ||
| 372 | &edac_pci_attr_pci_parity_count, | ||
| 373 | NULL, | ||
| 374 | }; | ||
| 375 | |||
| 376 | /* No memory to release */ | ||
| 377 | static void edac_pci_release(struct kobject *kobj) | ||
| 378 | { | ||
| 379 | debugf1("%s()\n", __func__); | ||
| 380 | complete(&edac_pci_kobj_complete); | ||
| 381 | } | ||
| 382 | |||
| 383 | static struct kobj_type ktype_edac_pci = { | ||
| 384 | .release = edac_pci_release, | ||
| 385 | .sysfs_ops = &edac_pci_sysfs_ops, | ||
| 386 | .default_attrs = (struct attribute **) edac_pci_attr, | ||
| 387 | }; | ||
| 388 | |||
| 389 | /** | ||
| 390 | * edac_sysfs_pci_setup() | ||
| 391 | * | ||
| 392 | */ | ||
| 393 | static int edac_sysfs_pci_setup(void) | ||
| 394 | { | ||
| 395 | int err; | ||
| 396 | |||
| 397 | debugf1("%s()\n", __func__); | ||
| 398 | |||
| 399 | memset(&edac_pci_kobj, 0, sizeof(edac_pci_kobj)); | ||
| 400 | edac_pci_kobj.parent = &edac_class.kset.kobj; | ||
| 401 | edac_pci_kobj.ktype = &ktype_edac_pci; | ||
| 402 | err = kobject_set_name(&edac_pci_kobj, "pci"); | ||
| 403 | |||
| 404 | if (!err) { | ||
| 405 | /* Instanstiate the csrow object */ | ||
| 406 | /* FIXME: maybe new sysdev_create_subdir() */ | ||
| 407 | err = kobject_register(&edac_pci_kobj); | ||
| 408 | |||
| 409 | if (err) | ||
| 410 | debugf1("Failed to register '.../edac/pci'\n"); | ||
| 411 | else | ||
| 412 | debugf1("Registered '.../edac/pci' kobject\n"); | ||
| 413 | } | ||
| 414 | |||
| 415 | return err; | ||
| 416 | } | ||
| 417 | |||
| 418 | static void edac_sysfs_pci_teardown(void) | ||
| 419 | { | ||
| 420 | debugf0("%s()\n", __func__); | ||
| 421 | init_completion(&edac_pci_kobj_complete); | ||
| 422 | kobject_unregister(&edac_pci_kobj); | ||
| 423 | wait_for_completion(&edac_pci_kobj_complete); | ||
| 424 | } | ||
| 425 | |||
| 426 | |||
| 427 | static u16 get_pci_parity_status(struct pci_dev *dev, int secondary) | ||
| 428 | { | ||
| 429 | int where; | ||
| 430 | u16 status; | ||
| 431 | |||
| 432 | where = secondary ? PCI_SEC_STATUS : PCI_STATUS; | ||
| 433 | pci_read_config_word(dev, where, &status); | ||
| 434 | |||
| 435 | /* If we get back 0xFFFF then we must suspect that the card has been | ||
| 436 | * pulled but the Linux PCI layer has not yet finished cleaning up. | ||
| 437 | * We don't want to report on such devices | ||
| 438 | */ | ||
| 439 | |||
| 440 | if (status == 0xFFFF) { | ||
| 441 | u32 sanity; | ||
| 442 | |||
| 443 | pci_read_config_dword(dev, 0, &sanity); | ||
| 444 | |||
| 445 | if (sanity == 0xFFFFFFFF) | ||
| 446 | return 0; | ||
| 447 | } | ||
| 448 | |||
| 449 | status &= PCI_STATUS_DETECTED_PARITY | PCI_STATUS_SIG_SYSTEM_ERROR | | ||
| 450 | PCI_STATUS_PARITY; | ||
| 451 | |||
| 452 | if (status) | ||
| 453 | /* reset only the bits we are interested in */ | ||
| 454 | pci_write_config_word(dev, where, status); | ||
| 455 | |||
| 456 | return status; | ||
| 457 | } | ||
| 458 | |||
| 459 | typedef void (*pci_parity_check_fn_t) (struct pci_dev *dev); | ||
| 460 | |||
| 461 | /* Clear any PCI parity errors logged by this device. */ | ||
| 462 | static void edac_pci_dev_parity_clear(struct pci_dev *dev) | ||
| 463 | { | ||
| 464 | u8 header_type; | ||
| 465 | |||
| 466 | get_pci_parity_status(dev, 0); | ||
| 467 | |||
| 468 | /* read the device TYPE, looking for bridges */ | ||
| 469 | pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type); | ||
| 470 | |||
| 471 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) | ||
| 472 | get_pci_parity_status(dev, 1); | ||
| 473 | } | ||
| 474 | |||
| 475 | /* | ||
| 476 | * PCI Parity polling | ||
| 477 | * | ||
| 478 | */ | ||
| 479 | static void edac_pci_dev_parity_test(struct pci_dev *dev) | ||
| 480 | { | ||
| 481 | u16 status; | ||
| 482 | u8 header_type; | ||
| 483 | |||
| 484 | /* read the STATUS register on this device | ||
| 485 | */ | ||
| 486 | status = get_pci_parity_status(dev, 0); | ||
| 487 | |||
| 488 | debugf2("PCI STATUS= 0x%04x %s\n", status, dev->dev.bus_id ); | ||
| 489 | |||
| 490 | /* check the status reg for errors */ | ||
| 491 | if (status) { | ||
| 492 | if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) | ||
| 493 | edac_printk(KERN_CRIT, EDAC_PCI, | ||
| 494 | "Signaled System Error on %s\n", | ||
| 495 | pci_name(dev)); | ||
| 496 | |||
| 497 | if (status & (PCI_STATUS_PARITY)) { | ||
| 498 | edac_printk(KERN_CRIT, EDAC_PCI, | ||
| 499 | "Master Data Parity Error on %s\n", | ||
| 500 | pci_name(dev)); | ||
| 501 | |||
| 502 | atomic_inc(&pci_parity_count); | ||
| 503 | } | ||
| 504 | |||
| 505 | if (status & (PCI_STATUS_DETECTED_PARITY)) { | ||
| 506 | edac_printk(KERN_CRIT, EDAC_PCI, | ||
| 507 | "Detected Parity Error on %s\n", | ||
| 508 | pci_name(dev)); | ||
| 509 | |||
| 510 | atomic_inc(&pci_parity_count); | ||
| 511 | } | ||
| 512 | } | ||
| 513 | |||
| 514 | /* read the device TYPE, looking for bridges */ | ||
| 515 | pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type); | ||
| 516 | |||
| 517 | debugf2("PCI HEADER TYPE= 0x%02x %s\n", header_type, dev->dev.bus_id ); | ||
| 518 | |||
| 519 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { | ||
| 520 | /* On bridges, need to examine secondary status register */ | ||
| 521 | status = get_pci_parity_status(dev, 1); | ||
| 522 | |||
| 523 | debugf2("PCI SEC_STATUS= 0x%04x %s\n", | ||
| 524 | status, dev->dev.bus_id ); | ||
| 525 | |||
| 526 | /* check the secondary status reg for errors */ | ||
| 527 | if (status) { | ||
| 528 | if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) | ||
| 529 | edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " | ||
| 530 | "Signaled System Error on %s\n", | ||
| 531 | pci_name(dev)); | ||
| 532 | |||
| 533 | if (status & (PCI_STATUS_PARITY)) { | ||
| 534 | edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " | ||
| 535 | "Master Data Parity Error on " | ||
| 536 | "%s\n", pci_name(dev)); | ||
| 537 | |||
| 538 | atomic_inc(&pci_parity_count); | ||
| 539 | } | ||
| 540 | |||
| 541 | if (status & (PCI_STATUS_DETECTED_PARITY)) { | ||
| 542 | edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " | ||
| 543 | "Detected Parity Error on %s\n", | ||
| 544 | pci_name(dev)); | ||
| 545 | |||
| 546 | atomic_inc(&pci_parity_count); | ||
| 547 | } | ||
| 548 | } | ||
| 549 | } | ||
| 550 | } | ||
| 551 | |||
| 552 | /* | ||
| 553 | * pci_dev parity list iterator | ||
| 554 | * Scan the PCI device list for one iteration, looking for SERRORs | ||
| 555 | * Master Parity ERRORS or Parity ERRORs on primary or secondary devices | ||
| 556 | */ | ||
| 557 | static inline void edac_pci_dev_parity_iterator(pci_parity_check_fn_t fn) | ||
| 558 | { | ||
| 559 | struct pci_dev *dev = NULL; | ||
| 560 | |||
| 561 | /* request for kernel access to the next PCI device, if any, | ||
| 562 | * and while we are looking at it have its reference count | ||
| 563 | * bumped until we are done with it | ||
| 564 | */ | ||
| 565 | while((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { | ||
| 566 | fn(dev); | ||
| 567 | } | ||
| 568 | } | ||
| 569 | |||
| 570 | static void do_pci_parity_check(void) | ||
| 571 | { | ||
| 572 | unsigned long flags; | ||
| 573 | int before_count; | ||
| 574 | |||
| 575 | debugf3("%s()\n", __func__); | ||
| 576 | |||
| 577 | if (!check_pci_parity) | ||
| 578 | return; | ||
| 579 | |||
| 580 | before_count = atomic_read(&pci_parity_count); | ||
| 581 | |||
| 582 | /* scan all PCI devices looking for a Parity Error on devices and | ||
| 583 | * bridges | ||
| 584 | */ | ||
| 585 | local_irq_save(flags); | ||
| 586 | edac_pci_dev_parity_iterator(edac_pci_dev_parity_test); | ||
| 587 | local_irq_restore(flags); | ||
| 588 | |||
| 589 | /* Only if operator has selected panic on PCI Error */ | ||
| 590 | if (panic_on_pci_parity) { | ||
| 591 | /* If the count is different 'after' from 'before' */ | ||
| 592 | if (before_count != atomic_read(&pci_parity_count)) | ||
| 593 | panic("EDAC: PCI Parity Error"); | ||
| 594 | } | ||
| 595 | } | ||
| 596 | |||
| 597 | static inline void clear_pci_parity_errors(void) | ||
| 598 | { | ||
| 599 | /* Clear any PCI bus parity errors that devices initially have logged | ||
| 600 | * in their registers. | ||
| 601 | */ | ||
| 602 | edac_pci_dev_parity_iterator(edac_pci_dev_parity_clear); | ||
| 603 | } | ||
| 604 | |||
| 605 | #else /* CONFIG_PCI */ | ||
| 606 | |||
| 607 | /* pre-process these away */ | ||
| 608 | #define do_pci_parity_check() | ||
| 609 | #define clear_pci_parity_errors() | ||
| 610 | #define edac_sysfs_pci_teardown() | ||
| 611 | #define edac_sysfs_pci_setup() (0) | ||
| 612 | |||
| 613 | #endif /* CONFIG_PCI */ | ||
| 614 | |||
| 615 | /* EDAC sysfs CSROW data structures and methods | ||
| 616 | */ | ||
| 617 | |||
| 618 | /* Set of more default csrow<id> attribute show/store functions */ | ||
| 619 | static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data, int private) | ||
| 620 | { | ||
| 621 | return sprintf(data,"%u\n", csrow->ue_count); | ||
| 622 | } | ||
| 623 | |||
| 624 | static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data, int private) | ||
| 625 | { | ||
| 626 | return sprintf(data,"%u\n", csrow->ce_count); | ||
| 627 | } | ||
| 628 | |||
| 629 | static ssize_t csrow_size_show(struct csrow_info *csrow, char *data, int private) | ||
| 630 | { | ||
| 631 | return sprintf(data,"%u\n", PAGES_TO_MiB(csrow->nr_pages)); | ||
| 632 | } | ||
| 633 | |||
| 634 | static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data, int private) | ||
| 635 | { | ||
| 636 | return sprintf(data,"%s\n", mem_types[csrow->mtype]); | ||
| 637 | } | ||
| 638 | |||
| 639 | static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data, int private) | ||
| 640 | { | ||
| 641 | return sprintf(data,"%s\n", dev_types[csrow->dtype]); | ||
| 642 | } | ||
| 643 | |||
| 644 | static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data, int private) | ||
| 645 | { | ||
| 646 | return sprintf(data,"%s\n", edac_caps[csrow->edac_mode]); | ||
| 647 | } | ||
| 648 | |||
| 649 | /* show/store functions for DIMM Label attributes */ | ||
| 650 | static ssize_t channel_dimm_label_show(struct csrow_info *csrow, | ||
| 651 | char *data, int channel) | ||
| 652 | { | ||
| 653 | return snprintf(data, EDAC_MC_LABEL_LEN,"%s", | ||
| 654 | csrow->channels[channel].label); | ||
| 655 | } | ||
| 656 | |||
| 657 | static ssize_t channel_dimm_label_store(struct csrow_info *csrow, | ||
| 658 | const char *data, | ||
| 659 | size_t count, | ||
| 660 | int channel) | ||
| 661 | { | ||
| 662 | ssize_t max_size = 0; | ||
| 663 | |||
| 664 | max_size = min((ssize_t)count,(ssize_t)EDAC_MC_LABEL_LEN-1); | ||
| 665 | strncpy(csrow->channels[channel].label, data, max_size); | ||
| 666 | csrow->channels[channel].label[max_size] = '\0'; | ||
| 667 | |||
| 668 | return max_size; | ||
| 669 | } | ||
| 670 | |||
| 671 | /* show function for dynamic chX_ce_count attribute */ | ||
| 672 | static ssize_t channel_ce_count_show(struct csrow_info *csrow, | ||
| 673 | char *data, | ||
| 674 | int channel) | ||
| 675 | { | ||
| 676 | return sprintf(data, "%u\n", csrow->channels[channel].ce_count); | ||
| 677 | } | ||
| 678 | |||
| 679 | /* csrow specific attribute structure */ | ||
| 680 | struct csrowdev_attribute { | ||
| 681 | struct attribute attr; | ||
| 682 | ssize_t (*show)(struct csrow_info *,char *,int); | ||
| 683 | ssize_t (*store)(struct csrow_info *, const char *,size_t,int); | ||
| 684 | int private; | ||
| 685 | }; | ||
| 686 | |||
| 687 | #define to_csrow(k) container_of(k, struct csrow_info, kobj) | ||
| 688 | #define to_csrowdev_attr(a) container_of(a, struct csrowdev_attribute, attr) | ||
| 689 | |||
| 690 | /* Set of show/store higher level functions for default csrow attributes */ | ||
| 691 | static ssize_t csrowdev_show(struct kobject *kobj, | ||
| 692 | struct attribute *attr, | ||
| 693 | char *buffer) | ||
| 694 | { | ||
| 695 | struct csrow_info *csrow = to_csrow(kobj); | ||
| 696 | struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); | ||
| 697 | |||
| 698 | if (csrowdev_attr->show) | ||
| 699 | return csrowdev_attr->show(csrow, | ||
| 700 | buffer, | ||
| 701 | csrowdev_attr->private); | ||
| 702 | return -EIO; | ||
| 703 | } | ||
| 704 | |||
| 705 | static ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr, | ||
| 706 | const char *buffer, size_t count) | ||
| 707 | { | ||
| 708 | struct csrow_info *csrow = to_csrow(kobj); | ||
| 709 | struct csrowdev_attribute * csrowdev_attr = to_csrowdev_attr(attr); | ||
| 710 | |||
| 711 | if (csrowdev_attr->store) | ||
| 712 | return csrowdev_attr->store(csrow, | ||
| 713 | buffer, | ||
| 714 | count, | ||
| 715 | csrowdev_attr->private); | ||
| 716 | return -EIO; | ||
| 717 | } | ||
| 718 | |||
| 719 | static struct sysfs_ops csrowfs_ops = { | ||
| 720 | .show = csrowdev_show, | ||
| 721 | .store = csrowdev_store | ||
| 722 | }; | ||
| 723 | |||
| 724 | #define CSROWDEV_ATTR(_name,_mode,_show,_store,_private) \ | ||
| 725 | static struct csrowdev_attribute attr_##_name = { \ | ||
| 726 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
| 727 | .show = _show, \ | ||
| 728 | .store = _store, \ | ||
| 729 | .private = _private, \ | ||
| 730 | }; | ||
| 731 | |||
| 732 | /* default cwrow<id>/attribute files */ | ||
| 733 | CSROWDEV_ATTR(size_mb,S_IRUGO,csrow_size_show,NULL,0); | ||
| 734 | CSROWDEV_ATTR(dev_type,S_IRUGO,csrow_dev_type_show,NULL,0); | ||
| 735 | CSROWDEV_ATTR(mem_type,S_IRUGO,csrow_mem_type_show,NULL,0); | ||
| 736 | CSROWDEV_ATTR(edac_mode,S_IRUGO,csrow_edac_mode_show,NULL,0); | ||
| 737 | CSROWDEV_ATTR(ue_count,S_IRUGO,csrow_ue_count_show,NULL,0); | ||
| 738 | CSROWDEV_ATTR(ce_count,S_IRUGO,csrow_ce_count_show,NULL,0); | ||
| 739 | |||
| 740 | /* default attributes of the CSROW<id> object */ | ||
| 741 | static struct csrowdev_attribute *default_csrow_attr[] = { | ||
| 742 | &attr_dev_type, | ||
| 743 | &attr_mem_type, | ||
| 744 | &attr_edac_mode, | ||
| 745 | &attr_size_mb, | ||
| 746 | &attr_ue_count, | ||
| 747 | &attr_ce_count, | ||
| 748 | NULL, | ||
| 749 | }; | ||
| 750 | |||
| 751 | |||
| 752 | /* possible dynamic channel DIMM Label attribute files */ | ||
| 753 | CSROWDEV_ATTR(ch0_dimm_label,S_IRUGO|S_IWUSR, | ||
| 754 | channel_dimm_label_show, | ||
| 755 | channel_dimm_label_store, | ||
| 756 | 0 ); | ||
| 757 | CSROWDEV_ATTR(ch1_dimm_label,S_IRUGO|S_IWUSR, | ||
| 758 | channel_dimm_label_show, | ||
| 759 | channel_dimm_label_store, | ||
| 760 | 1 ); | ||
| 761 | CSROWDEV_ATTR(ch2_dimm_label,S_IRUGO|S_IWUSR, | ||
| 762 | channel_dimm_label_show, | ||
| 763 | channel_dimm_label_store, | ||
| 764 | 2 ); | ||
| 765 | CSROWDEV_ATTR(ch3_dimm_label,S_IRUGO|S_IWUSR, | ||
| 766 | channel_dimm_label_show, | ||
| 767 | channel_dimm_label_store, | ||
| 768 | 3 ); | ||
| 769 | CSROWDEV_ATTR(ch4_dimm_label,S_IRUGO|S_IWUSR, | ||
| 770 | channel_dimm_label_show, | ||
| 771 | channel_dimm_label_store, | ||
| 772 | 4 ); | ||
| 773 | CSROWDEV_ATTR(ch5_dimm_label,S_IRUGO|S_IWUSR, | ||
| 774 | channel_dimm_label_show, | ||
| 775 | channel_dimm_label_store, | ||
| 776 | 5 ); | ||
| 777 | |||
| 778 | /* Total possible dynamic DIMM Label attribute file table */ | ||
| 779 | static struct csrowdev_attribute *dynamic_csrow_dimm_attr[] = { | ||
| 780 | &attr_ch0_dimm_label, | ||
| 781 | &attr_ch1_dimm_label, | ||
| 782 | &attr_ch2_dimm_label, | ||
| 783 | &attr_ch3_dimm_label, | ||
| 784 | &attr_ch4_dimm_label, | ||
| 785 | &attr_ch5_dimm_label | ||
| 786 | }; | ||
| 787 | |||
| 788 | /* possible dynamic channel ce_count attribute files */ | ||
| 789 | CSROWDEV_ATTR(ch0_ce_count,S_IRUGO|S_IWUSR, | ||
| 790 | channel_ce_count_show, | ||
| 791 | NULL, | ||
| 792 | 0 ); | ||
| 793 | CSROWDEV_ATTR(ch1_ce_count,S_IRUGO|S_IWUSR, | ||
| 794 | channel_ce_count_show, | ||
| 795 | NULL, | ||
| 796 | 1 ); | ||
| 797 | CSROWDEV_ATTR(ch2_ce_count,S_IRUGO|S_IWUSR, | ||
| 798 | channel_ce_count_show, | ||
| 799 | NULL, | ||
| 800 | 2 ); | ||
| 801 | CSROWDEV_ATTR(ch3_ce_count,S_IRUGO|S_IWUSR, | ||
| 802 | channel_ce_count_show, | ||
| 803 | NULL, | ||
| 804 | 3 ); | ||
| 805 | CSROWDEV_ATTR(ch4_ce_count,S_IRUGO|S_IWUSR, | ||
| 806 | channel_ce_count_show, | ||
| 807 | NULL, | ||
| 808 | 4 ); | ||
| 809 | CSROWDEV_ATTR(ch5_ce_count,S_IRUGO|S_IWUSR, | ||
| 810 | channel_ce_count_show, | ||
| 811 | NULL, | ||
| 812 | 5 ); | ||
| 813 | |||
| 814 | /* Total possible dynamic ce_count attribute file table */ | ||
| 815 | static struct csrowdev_attribute *dynamic_csrow_ce_count_attr[] = { | ||
| 816 | &attr_ch0_ce_count, | ||
| 817 | &attr_ch1_ce_count, | ||
| 818 | &attr_ch2_ce_count, | ||
| 819 | &attr_ch3_ce_count, | ||
| 820 | &attr_ch4_ce_count, | ||
| 821 | &attr_ch5_ce_count | ||
| 822 | }; | ||
| 823 | |||
| 824 | |||
| 825 | #define EDAC_NR_CHANNELS 6 | ||
| 826 | |||
| 827 | /* Create dynamic CHANNEL files, indexed by 'chan', under specifed CSROW */ | ||
| 828 | static int edac_create_channel_files(struct kobject *kobj, int chan) | ||
| 829 | { | ||
| 830 | int err=-ENODEV; | ||
| 831 | |||
| 832 | if (chan >= EDAC_NR_CHANNELS) | ||
| 833 | return err; | ||
| 834 | |||
| 835 | /* create the DIMM label attribute file */ | ||
| 836 | err = sysfs_create_file(kobj, | ||
| 837 | (struct attribute *) dynamic_csrow_dimm_attr[chan]); | ||
| 838 | |||
| 839 | if (!err) { | ||
| 840 | /* create the CE Count attribute file */ | ||
| 841 | err = sysfs_create_file(kobj, | ||
| 842 | (struct attribute *) dynamic_csrow_ce_count_attr[chan]); | ||
| 843 | } else { | ||
| 844 | debugf1("%s() dimm labels and ce_count files created", __func__); | ||
| 845 | } | ||
| 846 | |||
| 847 | return err; | ||
| 848 | } | ||
| 849 | |||
| 850 | /* No memory to release for this kobj */ | ||
| 851 | static void edac_csrow_instance_release(struct kobject *kobj) | ||
| 852 | { | ||
| 853 | struct csrow_info *cs; | ||
| 854 | |||
| 855 | cs = container_of(kobj, struct csrow_info, kobj); | ||
| 856 | complete(&cs->kobj_complete); | ||
| 857 | } | ||
| 858 | |||
| 859 | /* the kobj_type instance for a CSROW */ | ||
| 860 | static struct kobj_type ktype_csrow = { | ||
| 861 | .release = edac_csrow_instance_release, | ||
| 862 | .sysfs_ops = &csrowfs_ops, | ||
| 863 | .default_attrs = (struct attribute **) default_csrow_attr, | ||
| 864 | }; | ||
| 865 | |||
| 866 | /* Create a CSROW object under specifed edac_mc_device */ | ||
| 867 | static int edac_create_csrow_object( | ||
| 868 | struct kobject *edac_mci_kobj, | ||
| 869 | struct csrow_info *csrow, | ||
| 870 | int index) | ||
| 871 | { | ||
| 872 | int err = 0; | ||
| 873 | int chan; | ||
| 874 | |||
| 875 | memset(&csrow->kobj, 0, sizeof(csrow->kobj)); | ||
| 876 | |||
| 877 | /* generate ..../edac/mc/mc<id>/csrow<index> */ | ||
| 878 | |||
| 879 | csrow->kobj.parent = edac_mci_kobj; | ||
| 880 | csrow->kobj.ktype = &ktype_csrow; | ||
| 881 | |||
| 882 | /* name this instance of csrow<id> */ | ||
| 883 | err = kobject_set_name(&csrow->kobj,"csrow%d",index); | ||
| 884 | if (err) | ||
| 885 | goto error_exit; | ||
| 886 | |||
| 887 | /* Instanstiate the csrow object */ | ||
| 888 | err = kobject_register(&csrow->kobj); | ||
| 889 | if (!err) { | ||
| 890 | /* Create the dyanmic attribute files on this csrow, | ||
| 891 | * namely, the DIMM labels and the channel ce_count | ||
| 892 | */ | ||
| 893 | for (chan = 0; chan < csrow->nr_channels; chan++) { | ||
| 894 | err = edac_create_channel_files(&csrow->kobj,chan); | ||
| 895 | if (err) | ||
| 896 | break; | ||
| 897 | } | ||
| 898 | } | ||
| 899 | |||
| 900 | error_exit: | ||
| 901 | return err; | ||
| 902 | } | ||
| 903 | |||
| 904 | /* default sysfs methods and data structures for the main MCI kobject */ | ||
| 905 | |||
| 906 | static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, | ||
| 907 | const char *data, size_t count) | ||
| 908 | { | ||
| 909 | int row, chan; | ||
| 910 | |||
| 911 | mci->ue_noinfo_count = 0; | ||
| 912 | mci->ce_noinfo_count = 0; | ||
| 913 | mci->ue_count = 0; | ||
| 914 | mci->ce_count = 0; | ||
| 915 | |||
| 916 | for (row = 0; row < mci->nr_csrows; row++) { | ||
| 917 | struct csrow_info *ri = &mci->csrows[row]; | ||
| 918 | |||
| 919 | ri->ue_count = 0; | ||
| 920 | ri->ce_count = 0; | ||
| 921 | |||
| 922 | for (chan = 0; chan < ri->nr_channels; chan++) | ||
| 923 | ri->channels[chan].ce_count = 0; | ||
| 924 | } | ||
| 925 | |||
| 926 | mci->start_time = jiffies; | ||
| 927 | return count; | ||
| 928 | } | ||
| 929 | |||
| 930 | /* memory scrubbing */ | ||
| 931 | static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, | ||
| 932 | const char *data, size_t count) | ||
| 933 | { | ||
| 934 | u32 bandwidth = -1; | ||
| 935 | |||
| 936 | if (mci->set_sdram_scrub_rate) { | ||
| 937 | |||
| 938 | memctrl_int_store(&bandwidth, data, count); | ||
| 939 | |||
| 940 | if (!(*mci->set_sdram_scrub_rate)(mci, &bandwidth)) { | ||
| 941 | edac_printk(KERN_DEBUG, EDAC_MC, | ||
| 942 | "Scrub rate set successfully, applied: %d\n", | ||
| 943 | bandwidth); | ||
| 944 | } else { | ||
| 945 | /* FIXME: error codes maybe? */ | ||
| 946 | edac_printk(KERN_DEBUG, EDAC_MC, | ||
| 947 | "Scrub rate set FAILED, could not apply: %d\n", | ||
| 948 | bandwidth); | ||
| 949 | } | ||
| 950 | } else { | ||
| 951 | /* FIXME: produce "not implemented" ERROR for user-side. */ | ||
| 952 | edac_printk(KERN_WARNING, EDAC_MC, | ||
| 953 | "Memory scrubbing 'set'control is not implemented!\n"); | ||
| 954 | } | ||
| 955 | return count; | ||
| 956 | } | ||
| 957 | |||
| 958 | static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) | ||
| 959 | { | ||
| 960 | u32 bandwidth = -1; | ||
| 961 | |||
| 962 | if (mci->get_sdram_scrub_rate) { | ||
| 963 | if (!(*mci->get_sdram_scrub_rate)(mci, &bandwidth)) { | ||
| 964 | edac_printk(KERN_DEBUG, EDAC_MC, | ||
| 965 | "Scrub rate successfully, fetched: %d\n", | ||
| 966 | bandwidth); | ||
| 967 | } else { | ||
| 968 | /* FIXME: error codes maybe? */ | ||
| 969 | edac_printk(KERN_DEBUG, EDAC_MC, | ||
| 970 | "Scrub rate fetch FAILED, got: %d\n", | ||
| 971 | bandwidth); | ||
| 972 | } | ||
| 973 | } else { | ||
| 974 | /* FIXME: produce "not implemented" ERROR for user-side. */ | ||
| 975 | edac_printk(KERN_WARNING, EDAC_MC, | ||
| 976 | "Memory scrubbing 'get' control is not implemented!\n"); | ||
| 977 | } | ||
| 978 | return sprintf(data, "%d\n", bandwidth); | ||
| 979 | } | ||
| 980 | |||
| 981 | /* default attribute files for the MCI object */ | ||
| 982 | static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data) | ||
| 983 | { | ||
| 984 | return sprintf(data,"%d\n", mci->ue_count); | ||
| 985 | } | ||
| 986 | |||
| 987 | static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data) | ||
| 988 | { | ||
| 989 | return sprintf(data,"%d\n", mci->ce_count); | ||
| 990 | } | ||
| 991 | |||
| 992 | static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data) | ||
| 993 | { | ||
| 994 | return sprintf(data,"%d\n", mci->ce_noinfo_count); | ||
| 995 | } | ||
| 996 | |||
| 997 | static ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data) | ||
| 998 | { | ||
| 999 | return sprintf(data,"%d\n", mci->ue_noinfo_count); | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | static ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data) | ||
| 1003 | { | ||
| 1004 | return sprintf(data,"%ld\n", (jiffies - mci->start_time) / HZ); | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data) | ||
| 1008 | { | ||
| 1009 | return sprintf(data,"%s\n", mci->ctl_name); | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) | ||
| 1013 | { | ||
| 1014 | int total_pages, csrow_idx; | ||
| 1015 | |||
| 1016 | for (total_pages = csrow_idx = 0; csrow_idx < mci->nr_csrows; | ||
| 1017 | csrow_idx++) { | ||
| 1018 | struct csrow_info *csrow = &mci->csrows[csrow_idx]; | ||
| 1019 | |||
| 1020 | if (!csrow->nr_pages) | ||
| 1021 | continue; | ||
| 1022 | |||
| 1023 | total_pages += csrow->nr_pages; | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | return sprintf(data,"%u\n", PAGES_TO_MiB(total_pages)); | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | struct mcidev_attribute { | ||
| 1030 | struct attribute attr; | ||
| 1031 | ssize_t (*show)(struct mem_ctl_info *,char *); | ||
| 1032 | ssize_t (*store)(struct mem_ctl_info *, const char *,size_t); | ||
| 1033 | }; | ||
| 1034 | |||
| 1035 | #define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj) | ||
| 1036 | #define to_mcidev_attr(a) container_of(a, struct mcidev_attribute, attr) | ||
| 1037 | |||
| 1038 | /* MCI show/store functions for top most object */ | ||
| 1039 | static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr, | ||
| 1040 | char *buffer) | ||
| 1041 | { | ||
| 1042 | struct mem_ctl_info *mem_ctl_info = to_mci(kobj); | ||
| 1043 | struct mcidev_attribute * mcidev_attr = to_mcidev_attr(attr); | ||
| 1044 | |||
| 1045 | if (mcidev_attr->show) | ||
| 1046 | return mcidev_attr->show(mem_ctl_info, buffer); | ||
| 1047 | |||
| 1048 | return -EIO; | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, | ||
| 1052 | const char *buffer, size_t count) | ||
| 1053 | { | ||
| 1054 | struct mem_ctl_info *mem_ctl_info = to_mci(kobj); | ||
| 1055 | struct mcidev_attribute * mcidev_attr = to_mcidev_attr(attr); | ||
| 1056 | |||
| 1057 | if (mcidev_attr->store) | ||
| 1058 | return mcidev_attr->store(mem_ctl_info, buffer, count); | ||
| 1059 | |||
| 1060 | return -EIO; | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | static struct sysfs_ops mci_ops = { | ||
| 1064 | .show = mcidev_show, | ||
| 1065 | .store = mcidev_store | ||
| 1066 | }; | ||
| 1067 | |||
| 1068 | #define MCIDEV_ATTR(_name,_mode,_show,_store) \ | ||
| 1069 | static struct mcidev_attribute mci_attr_##_name = { \ | ||
| 1070 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
| 1071 | .show = _show, \ | ||
| 1072 | .store = _store, \ | ||
| 1073 | }; | ||
| 1074 | |||
| 1075 | /* default Control file */ | ||
| 1076 | MCIDEV_ATTR(reset_counters,S_IWUSR,NULL,mci_reset_counters_store); | ||
| 1077 | |||
| 1078 | /* default Attribute files */ | ||
| 1079 | MCIDEV_ATTR(mc_name,S_IRUGO,mci_ctl_name_show,NULL); | ||
| 1080 | MCIDEV_ATTR(size_mb,S_IRUGO,mci_size_mb_show,NULL); | ||
| 1081 | MCIDEV_ATTR(seconds_since_reset,S_IRUGO,mci_seconds_show,NULL); | ||
| 1082 | MCIDEV_ATTR(ue_noinfo_count,S_IRUGO,mci_ue_noinfo_show,NULL); | ||
| 1083 | MCIDEV_ATTR(ce_noinfo_count,S_IRUGO,mci_ce_noinfo_show,NULL); | ||
| 1084 | MCIDEV_ATTR(ue_count,S_IRUGO,mci_ue_count_show,NULL); | ||
| 1085 | MCIDEV_ATTR(ce_count,S_IRUGO,mci_ce_count_show,NULL); | ||
| 1086 | |||
| 1087 | /* memory scrubber attribute file */ | ||
| 1088 | MCIDEV_ATTR(sdram_scrub_rate,S_IRUGO|S_IWUSR,mci_sdram_scrub_rate_show,mci_sdram_scrub_rate_store); | ||
| 1089 | |||
| 1090 | static struct mcidev_attribute *mci_attr[] = { | ||
| 1091 | &mci_attr_reset_counters, | ||
| 1092 | &mci_attr_mc_name, | ||
| 1093 | &mci_attr_size_mb, | ||
| 1094 | &mci_attr_seconds_since_reset, | ||
| 1095 | &mci_attr_ue_noinfo_count, | ||
| 1096 | &mci_attr_ce_noinfo_count, | ||
| 1097 | &mci_attr_ue_count, | ||
| 1098 | &mci_attr_ce_count, | ||
| 1099 | &mci_attr_sdram_scrub_rate, | ||
| 1100 | NULL | ||
| 1101 | }; | ||
| 1102 | |||
| 1103 | /* | ||
| 1104 | * Release of a MC controlling instance | ||
| 1105 | */ | ||
| 1106 | static void edac_mci_instance_release(struct kobject *kobj) | ||
| 1107 | { | ||
| 1108 | struct mem_ctl_info *mci; | ||
| 1109 | |||
| 1110 | mci = to_mci(kobj); | ||
| 1111 | debugf0("%s() idx=%d\n", __func__, mci->mc_idx); | ||
| 1112 | complete(&mci->kobj_complete); | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | static struct kobj_type ktype_mci = { | ||
| 1116 | .release = edac_mci_instance_release, | ||
| 1117 | .sysfs_ops = &mci_ops, | ||
| 1118 | .default_attrs = (struct attribute **) mci_attr, | ||
| 1119 | }; | ||
| 1120 | |||
| 1121 | |||
| 1122 | #define EDAC_DEVICE_SYMLINK "device" | ||
| 1123 | |||
| 1124 | /* | ||
| 1125 | * Create a new Memory Controller kobject instance, | ||
| 1126 | * mc<id> under the 'mc' directory | ||
| 1127 | * | ||
| 1128 | * Return: | ||
| 1129 | * 0 Success | ||
| 1130 | * !0 Failure | ||
| 1131 | */ | ||
| 1132 | static int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) | ||
| 1133 | { | ||
| 1134 | int i; | ||
| 1135 | int err; | ||
| 1136 | struct csrow_info *csrow; | ||
| 1137 | struct kobject *edac_mci_kobj=&mci->edac_mci_kobj; | ||
| 1138 | |||
| 1139 | debugf0("%s() idx=%d\n", __func__, mci->mc_idx); | ||
| 1140 | memset(edac_mci_kobj, 0, sizeof(*edac_mci_kobj)); | ||
| 1141 | |||
| 1142 | /* set the name of the mc<id> object */ | ||
| 1143 | err = kobject_set_name(edac_mci_kobj,"mc%d",mci->mc_idx); | ||
| 1144 | if (err) | ||
| 1145 | return err; | ||
| 1146 | |||
| 1147 | /* link to our parent the '..../edac/mc' object */ | ||
| 1148 | edac_mci_kobj->parent = &edac_memctrl_kobj; | ||
| 1149 | edac_mci_kobj->ktype = &ktype_mci; | ||
| 1150 | |||
| 1151 | /* register the mc<id> kobject */ | ||
| 1152 | err = kobject_register(edac_mci_kobj); | ||
| 1153 | if (err) | ||
| 1154 | return err; | ||
| 1155 | |||
| 1156 | /* create a symlink for the device */ | ||
| 1157 | err = sysfs_create_link(edac_mci_kobj, &mci->dev->kobj, | ||
| 1158 | EDAC_DEVICE_SYMLINK); | ||
| 1159 | if (err) | ||
| 1160 | goto fail0; | ||
| 1161 | |||
| 1162 | /* Make directories for each CSROW object | ||
| 1163 | * under the mc<id> kobject | ||
| 1164 | */ | ||
| 1165 | for (i = 0; i < mci->nr_csrows; i++) { | ||
| 1166 | csrow = &mci->csrows[i]; | ||
| 1167 | |||
| 1168 | /* Only expose populated CSROWs */ | ||
| 1169 | if (csrow->nr_pages > 0) { | ||
| 1170 | err = edac_create_csrow_object(edac_mci_kobj,csrow,i); | ||
| 1171 | if (err) | ||
| 1172 | goto fail1; | ||
| 1173 | } | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | return 0; | ||
| 1177 | |||
| 1178 | /* CSROW error: backout what has already been registered, */ | ||
| 1179 | fail1: | ||
| 1180 | for ( i--; i >= 0; i--) { | ||
| 1181 | if (csrow->nr_pages > 0) { | ||
| 1182 | init_completion(&csrow->kobj_complete); | ||
| 1183 | kobject_unregister(&mci->csrows[i].kobj); | ||
| 1184 | wait_for_completion(&csrow->kobj_complete); | ||
| 1185 | } | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | fail0: | ||
| 1189 | init_completion(&mci->kobj_complete); | ||
| 1190 | kobject_unregister(edac_mci_kobj); | ||
| 1191 | wait_for_completion(&mci->kobj_complete); | ||
| 1192 | return err; | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | /* | ||
| 1196 | * remove a Memory Controller instance | ||
| 1197 | */ | ||
| 1198 | static void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) | ||
| 1199 | { | ||
| 1200 | int i; | ||
| 1201 | |||
| 1202 | debugf0("%s()\n", __func__); | ||
| 1203 | |||
| 1204 | /* remove all csrow kobjects */ | ||
| 1205 | for (i = 0; i < mci->nr_csrows; i++) { | ||
| 1206 | if (mci->csrows[i].nr_pages > 0) { | ||
| 1207 | init_completion(&mci->csrows[i].kobj_complete); | ||
| 1208 | kobject_unregister(&mci->csrows[i].kobj); | ||
| 1209 | wait_for_completion(&mci->csrows[i].kobj_complete); | ||
| 1210 | } | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK); | ||
| 1214 | init_completion(&mci->kobj_complete); | ||
| 1215 | kobject_unregister(&mci->edac_mci_kobj); | ||
| 1216 | wait_for_completion(&mci->kobj_complete); | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | /* END OF sysfs data and methods */ | ||
| 1220 | |||
| 1221 | #ifdef CONFIG_EDAC_DEBUG | 41 | #ifdef CONFIG_EDAC_DEBUG |
| 1222 | 42 | ||
| 1223 | static void edac_mc_dump_channel(struct channel_info *chan) | 43 | static void edac_mc_dump_channel(struct channel_info *chan) |
| @@ -1672,7 +492,7 @@ void edac_mc_handle_ce(struct mem_ctl_info *mci, | |||
| 1672 | return; | 492 | return; |
| 1673 | } | 493 | } |
| 1674 | 494 | ||
| 1675 | if (log_ce) | 495 | if (edac_get_log_ce()) |
| 1676 | /* FIXME - put in DIMM location */ | 496 | /* FIXME - put in DIMM location */ |
| 1677 | edac_mc_printk(mci, KERN_WARNING, | 497 | edac_mc_printk(mci, KERN_WARNING, |
| 1678 | "CE page 0x%lx, offset 0x%lx, grain %d, syndrome " | 498 | "CE page 0x%lx, offset 0x%lx, grain %d, syndrome " |
| @@ -1707,7 +527,7 @@ EXPORT_SYMBOL_GPL(edac_mc_handle_ce); | |||
| 1707 | 527 | ||
| 1708 | void edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, const char *msg) | 528 | void edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, const char *msg) |
| 1709 | { | 529 | { |
| 1710 | if (log_ce) | 530 | if (edac_get_log_ce()) |
| 1711 | edac_mc_printk(mci, KERN_WARNING, | 531 | edac_mc_printk(mci, KERN_WARNING, |
| 1712 | "CE - no information available: %s\n", msg); | 532 | "CE - no information available: %s\n", msg); |
| 1713 | 533 | ||
| @@ -1751,14 +571,14 @@ void edac_mc_handle_ue(struct mem_ctl_info *mci, | |||
| 1751 | pos += chars; | 571 | pos += chars; |
| 1752 | } | 572 | } |
| 1753 | 573 | ||
| 1754 | if (log_ue) | 574 | if (edac_get_log_ue()) |
| 1755 | edac_mc_printk(mci, KERN_EMERG, | 575 | edac_mc_printk(mci, KERN_EMERG, |
| 1756 | "UE page 0x%lx, offset 0x%lx, grain %d, row %d, " | 576 | "UE page 0x%lx, offset 0x%lx, grain %d, row %d, " |
| 1757 | "labels \"%s\": %s\n", page_frame_number, | 577 | "labels \"%s\": %s\n", page_frame_number, |
| 1758 | offset_in_page, mci->csrows[row].grain, row, labels, | 578 | offset_in_page, mci->csrows[row].grain, row, labels, |
| 1759 | msg); | 579 | msg); |
| 1760 | 580 | ||
| 1761 | if (panic_on_ue) | 581 | if (edac_get_panic_on_ue()) |
| 1762 | panic("EDAC MC%d: UE page 0x%lx, offset 0x%lx, grain %d, " | 582 | panic("EDAC MC%d: UE page 0x%lx, offset 0x%lx, grain %d, " |
| 1763 | "row %d, labels \"%s\": %s\n", mci->mc_idx, | 583 | "row %d, labels \"%s\": %s\n", mci->mc_idx, |
| 1764 | page_frame_number, offset_in_page, | 584 | page_frame_number, offset_in_page, |
| @@ -1771,10 +591,10 @@ EXPORT_SYMBOL_GPL(edac_mc_handle_ue); | |||
| 1771 | 591 | ||
| 1772 | void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg) | 592 | void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg) |
| 1773 | { | 593 | { |
| 1774 | if (panic_on_ue) | 594 | if (edac_get_panic_on_ue()) |
| 1775 | panic("EDAC MC%d: Uncorrected Error", mci->mc_idx); | 595 | panic("EDAC MC%d: Uncorrected Error", mci->mc_idx); |
| 1776 | 596 | ||
| 1777 | if (log_ue) | 597 | if (edac_get_log_ue()) |
| 1778 | edac_mc_printk(mci, KERN_WARNING, | 598 | edac_mc_printk(mci, KERN_WARNING, |
| 1779 | "UE - no information available: %s\n", msg); | 599 | "UE - no information available: %s\n", msg); |
| 1780 | mci->ue_noinfo_count++; | 600 | mci->ue_noinfo_count++; |
| @@ -1837,13 +657,13 @@ void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci, | |||
| 1837 | chars = snprintf(pos, len + 1, "-%s", | 657 | chars = snprintf(pos, len + 1, "-%s", |
| 1838 | mci->csrows[csrow].channels[channelb].label); | 658 | mci->csrows[csrow].channels[channelb].label); |
| 1839 | 659 | ||
| 1840 | if (log_ue) | 660 | if (edac_get_log_ue()) |
| 1841 | edac_mc_printk(mci, KERN_EMERG, | 661 | edac_mc_printk(mci, KERN_EMERG, |
| 1842 | "UE row %d, channel-a= %d channel-b= %d " | 662 | "UE row %d, channel-a= %d channel-b= %d " |
| 1843 | "labels \"%s\": %s\n", csrow, channela, channelb, | 663 | "labels \"%s\": %s\n", csrow, channela, channelb, |
| 1844 | labels, msg); | 664 | labels, msg); |
| 1845 | 665 | ||
| 1846 | if (panic_on_ue) | 666 | if (edac_get_panic_on_ue()) |
| 1847 | panic("UE row %d, channel-a= %d channel-b= %d " | 667 | panic("UE row %d, channel-a= %d channel-b= %d " |
| 1848 | "labels \"%s\": %s\n", csrow, channela, | 668 | "labels \"%s\": %s\n", csrow, channela, |
| 1849 | channelb, labels, msg); | 669 | channelb, labels, msg); |
| @@ -1878,7 +698,7 @@ void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci, | |||
| 1878 | return; | 698 | return; |
| 1879 | } | 699 | } |
| 1880 | 700 | ||
| 1881 | if (log_ce) | 701 | if (edac_get_log_ce()) |
| 1882 | /* FIXME - put in DIMM location */ | 702 | /* FIXME - put in DIMM location */ |
| 1883 | edac_mc_printk(mci, KERN_WARNING, | 703 | edac_mc_printk(mci, KERN_WARNING, |
| 1884 | "CE row %d, channel %d, label \"%s\": %s\n", | 704 | "CE row %d, channel %d, label \"%s\": %s\n", |
| @@ -1896,7 +716,7 @@ EXPORT_SYMBOL(edac_mc_handle_fbd_ce); | |||
| 1896 | /* | 716 | /* |
| 1897 | * Iterate over all MC instances and check for ECC, et al, errors | 717 | * Iterate over all MC instances and check for ECC, et al, errors |
| 1898 | */ | 718 | */ |
| 1899 | static inline void check_mc_devices(void) | 719 | void edac_check_mc_devices(void) |
| 1900 | { | 720 | { |
| 1901 | struct list_head *item; | 721 | struct list_head *item; |
| 1902 | struct mem_ctl_info *mci; | 722 | struct mem_ctl_info *mci; |
| @@ -1913,118 +733,3 @@ static inline void check_mc_devices(void) | |||
| 1913 | 733 | ||
| 1914 | up(&mem_ctls_mutex); | 734 | up(&mem_ctls_mutex); |
| 1915 | } | 735 | } |
| 1916 | |||
| 1917 | /* | ||
| 1918 | * Check MC status every poll_msec. | ||
| 1919 | * Check PCI status every poll_msec as well. | ||
| 1920 | * | ||
| 1921 | * This where the work gets done for edac. | ||
| 1922 | * | ||
| 1923 | * SMP safe, doesn't use NMI, and auto-rate-limits. | ||
| 1924 | */ | ||
| 1925 | static void do_edac_check(void) | ||
| 1926 | { | ||
| 1927 | debugf3("%s()\n", __func__); | ||
| 1928 | check_mc_devices(); | ||
| 1929 | do_pci_parity_check(); | ||
| 1930 | } | ||
| 1931 | |||
| 1932 | static int edac_kernel_thread(void *arg) | ||
| 1933 | { | ||
| 1934 | set_freezable(); | ||
| 1935 | while (!kthread_should_stop()) { | ||
| 1936 | do_edac_check(); | ||
| 1937 | |||
| 1938 | /* goto sleep for the interval */ | ||
| 1939 | schedule_timeout_interruptible((HZ * poll_msec) / 1000); | ||
| 1940 | try_to_freeze(); | ||
| 1941 | } | ||
| 1942 | |||
| 1943 | return 0; | ||
| 1944 | } | ||
| 1945 | |||
| 1946 | /* | ||
| 1947 | * edac_mc_init | ||
| 1948 | * module initialization entry point | ||
| 1949 | */ | ||
| 1950 | static int __init edac_mc_init(void) | ||
| 1951 | { | ||
| 1952 | edac_printk(KERN_INFO, EDAC_MC, EDAC_MC_VERSION "\n"); | ||
| 1953 | |||
| 1954 | /* | ||
| 1955 | * Harvest and clear any boot/initialization PCI parity errors | ||
| 1956 | * | ||
| 1957 | * FIXME: This only clears errors logged by devices present at time of | ||
| 1958 | * module initialization. We should also do an initial clear | ||
| 1959 | * of each newly hotplugged device. | ||
| 1960 | */ | ||
| 1961 | clear_pci_parity_errors(); | ||
| 1962 | |||
| 1963 | /* Create the MC sysfs entries */ | ||
| 1964 | if (edac_sysfs_memctrl_setup()) { | ||
| 1965 | edac_printk(KERN_ERR, EDAC_MC, | ||
| 1966 | "Error initializing sysfs code\n"); | ||
| 1967 | return -ENODEV; | ||
| 1968 | } | ||
| 1969 | |||
| 1970 | /* Create the PCI parity sysfs entries */ | ||
| 1971 | if (edac_sysfs_pci_setup()) { | ||
| 1972 | edac_sysfs_memctrl_teardown(); | ||
| 1973 | edac_printk(KERN_ERR, EDAC_MC, | ||
| 1974 | "EDAC PCI: Error initializing sysfs code\n"); | ||
| 1975 | return -ENODEV; | ||
| 1976 | } | ||
| 1977 | |||
| 1978 | /* create our kernel thread */ | ||
| 1979 | edac_thread = kthread_run(edac_kernel_thread, NULL, "kedac"); | ||
| 1980 | |||
| 1981 | if (IS_ERR(edac_thread)) { | ||
| 1982 | /* remove the sysfs entries */ | ||
| 1983 | edac_sysfs_memctrl_teardown(); | ||
| 1984 | edac_sysfs_pci_teardown(); | ||
| 1985 | return PTR_ERR(edac_thread); | ||
| 1986 | } | ||
| 1987 | |||
| 1988 | return 0; | ||
| 1989 | } | ||
| 1990 | |||
| 1991 | /* | ||
| 1992 | * edac_mc_exit() | ||
| 1993 | * module exit/termination functioni | ||
| 1994 | */ | ||
| 1995 | static void __exit edac_mc_exit(void) | ||
| 1996 | { | ||
| 1997 | debugf0("%s()\n", __func__); | ||
| 1998 | kthread_stop(edac_thread); | ||
| 1999 | |||
| 2000 | /* tear down the sysfs device */ | ||
| 2001 | edac_sysfs_memctrl_teardown(); | ||
| 2002 | edac_sysfs_pci_teardown(); | ||
| 2003 | } | ||
| 2004 | |||
| 2005 | module_init(edac_mc_init); | ||
| 2006 | module_exit(edac_mc_exit); | ||
| 2007 | |||
| 2008 | MODULE_LICENSE("GPL"); | ||
| 2009 | MODULE_AUTHOR("Linux Networx (http://lnxi.com) Thayne Harbaugh et al\n" | ||
| 2010 | "Based on work by Dan Hollis et al"); | ||
| 2011 | MODULE_DESCRIPTION("Core library routines for MC reporting"); | ||
| 2012 | |||
| 2013 | module_param(panic_on_ue, int, 0644); | ||
| 2014 | MODULE_PARM_DESC(panic_on_ue, "Panic on uncorrected error: 0=off 1=on"); | ||
| 2015 | #ifdef CONFIG_PCI | ||
| 2016 | module_param(check_pci_parity, int, 0644); | ||
| 2017 | MODULE_PARM_DESC(check_pci_parity, "Check for PCI bus parity errors: 0=off 1=on"); | ||
| 2018 | module_param(panic_on_pci_parity, int, 0644); | ||
| 2019 | MODULE_PARM_DESC(panic_on_pci_parity, "Panic on PCI Bus Parity error: 0=off 1=on"); | ||
| 2020 | #endif | ||
| 2021 | module_param(log_ue, int, 0644); | ||
| 2022 | MODULE_PARM_DESC(log_ue, "Log uncorrectable error to console: 0=off 1=on"); | ||
| 2023 | module_param(log_ce, int, 0644); | ||
| 2024 | MODULE_PARM_DESC(log_ce, "Log correctable error to console: 0=off 1=on"); | ||
| 2025 | module_param(poll_msec, int, 0644); | ||
| 2026 | MODULE_PARM_DESC(poll_msec, "Polling period in milliseconds"); | ||
| 2027 | #ifdef CONFIG_EDAC_DEBUG | ||
| 2028 | module_param(edac_debug_level, int, 0644); | ||
| 2029 | MODULE_PARM_DESC(edac_debug_level, "Debug level"); | ||
| 2030 | #endif | ||
diff --git a/drivers/edac/edac_mc.h b/drivers/edac/edac_mc.h index fdc811d89679..b92d2720a4de 100644 --- a/drivers/edac/edac_mc.h +++ b/drivers/edac/edac_mc.h | |||
| @@ -1,476 +1,9 @@ | |||
| 1 | /* | ||
| 2 | * MC kernel module | ||
| 3 | * (C) 2003 Linux Networx (http://lnxi.com) | ||
| 4 | * This file may be distributed under the terms of the | ||
| 5 | * GNU General Public License. | ||
| 6 | * | ||
| 7 | * Written by Thayne Harbaugh | ||
| 8 | * Based on work by Dan Hollis <goemon at anime dot net> and others. | ||
| 9 | * http://www.anime.net/~goemon/linux-ecc/ | ||
| 10 | * | ||
| 11 | * NMI handling support added by | ||
| 12 | * Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com> | ||
| 13 | * | ||
| 14 | * $Id: edac_mc.h,v 1.4.2.10 2005/10/05 00:43:44 dsp_llnl Exp $ | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #ifndef _EDAC_MC_H_ | ||
| 19 | #define _EDAC_MC_H_ | ||
| 20 | |||
| 21 | #include <linux/kernel.h> | ||
| 22 | #include <linux/types.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/spinlock.h> | ||
| 25 | #include <linux/smp.h> | ||
| 26 | #include <linux/pci.h> | ||
| 27 | #include <linux/time.h> | ||
| 28 | #include <linux/nmi.h> | ||
| 29 | #include <linux/rcupdate.h> | ||
| 30 | #include <linux/completion.h> | ||
| 31 | #include <linux/kobject.h> | ||
| 32 | #include <linux/platform_device.h> | ||
| 33 | |||
| 34 | #define EDAC_MC_LABEL_LEN 31 | ||
| 35 | #define MC_PROC_NAME_MAX_LEN 7 | ||
| 36 | |||
| 37 | #if PAGE_SHIFT < 20 | ||
| 38 | #define PAGES_TO_MiB( pages ) ( ( pages ) >> ( 20 - PAGE_SHIFT ) ) | ||
| 39 | #else /* PAGE_SHIFT > 20 */ | ||
| 40 | #define PAGES_TO_MiB( pages ) ( ( pages ) << ( PAGE_SHIFT - 20 ) ) | ||
| 41 | #endif | ||
| 42 | |||
| 43 | #define edac_printk(level, prefix, fmt, arg...) \ | ||
| 44 | printk(level "EDAC " prefix ": " fmt, ##arg) | ||
| 45 | |||
| 46 | #define edac_mc_printk(mci, level, fmt, arg...) \ | ||
| 47 | printk(level "EDAC MC%d: " fmt, mci->mc_idx, ##arg) | ||
| 48 | |||
| 49 | #define edac_mc_chipset_printk(mci, level, prefix, fmt, arg...) \ | ||
| 50 | printk(level "EDAC " prefix " MC%d: " fmt, mci->mc_idx, ##arg) | ||
| 51 | |||
| 52 | /* prefixes for edac_printk() and edac_mc_printk() */ | ||
| 53 | #define EDAC_MC "MC" | ||
| 54 | #define EDAC_PCI "PCI" | ||
| 55 | #define EDAC_DEBUG "DEBUG" | ||
| 56 | |||
| 57 | #ifdef CONFIG_EDAC_DEBUG | ||
| 58 | extern int edac_debug_level; | ||
| 59 | |||
| 60 | #define edac_debug_printk(level, fmt, arg...) \ | ||
| 61 | do { \ | ||
| 62 | if (level <= edac_debug_level) \ | ||
| 63 | edac_printk(KERN_DEBUG, EDAC_DEBUG, fmt, ##arg); \ | ||
| 64 | } while(0) | ||
| 65 | |||
| 66 | #define debugf0( ... ) edac_debug_printk(0, __VA_ARGS__ ) | ||
| 67 | #define debugf1( ... ) edac_debug_printk(1, __VA_ARGS__ ) | ||
| 68 | #define debugf2( ... ) edac_debug_printk(2, __VA_ARGS__ ) | ||
| 69 | #define debugf3( ... ) edac_debug_printk(3, __VA_ARGS__ ) | ||
| 70 | #define debugf4( ... ) edac_debug_printk(4, __VA_ARGS__ ) | ||
| 71 | |||
| 72 | #else /* !CONFIG_EDAC_DEBUG */ | ||
| 73 | |||
| 74 | #define debugf0( ... ) | ||
| 75 | #define debugf1( ... ) | ||
| 76 | #define debugf2( ... ) | ||
| 77 | #define debugf3( ... ) | ||
| 78 | #define debugf4( ... ) | ||
| 79 | |||
| 80 | #endif /* !CONFIG_EDAC_DEBUG */ | ||
| 81 | 1 | ||
| 82 | #define BIT(x) (1 << (x)) | ||
| 83 | |||
| 84 | #define PCI_VEND_DEV(vend, dev) PCI_VENDOR_ID_ ## vend, \ | ||
| 85 | PCI_DEVICE_ID_ ## vend ## _ ## dev | ||
| 86 | |||
| 87 | #if defined(CONFIG_X86) && defined(CONFIG_PCI) | ||
| 88 | #define dev_name(dev) pci_name(to_pci_dev(dev)) | ||
| 89 | #else | ||
| 90 | #define dev_name(dev) to_platform_device(dev)->name | ||
| 91 | #endif | ||
| 92 | |||
| 93 | /* memory devices */ | ||
| 94 | enum dev_type { | ||
| 95 | DEV_UNKNOWN = 0, | ||
| 96 | DEV_X1, | ||
| 97 | DEV_X2, | ||
| 98 | DEV_X4, | ||
| 99 | DEV_X8, | ||
| 100 | DEV_X16, | ||
| 101 | DEV_X32, /* Do these parts exist? */ | ||
| 102 | DEV_X64 /* Do these parts exist? */ | ||
| 103 | }; | ||
| 104 | |||
| 105 | #define DEV_FLAG_UNKNOWN BIT(DEV_UNKNOWN) | ||
| 106 | #define DEV_FLAG_X1 BIT(DEV_X1) | ||
| 107 | #define DEV_FLAG_X2 BIT(DEV_X2) | ||
| 108 | #define DEV_FLAG_X4 BIT(DEV_X4) | ||
| 109 | #define DEV_FLAG_X8 BIT(DEV_X8) | ||
| 110 | #define DEV_FLAG_X16 BIT(DEV_X16) | ||
| 111 | #define DEV_FLAG_X32 BIT(DEV_X32) | ||
| 112 | #define DEV_FLAG_X64 BIT(DEV_X64) | ||
| 113 | |||
| 114 | /* memory types */ | ||
| 115 | enum mem_type { | ||
| 116 | MEM_EMPTY = 0, /* Empty csrow */ | ||
| 117 | MEM_RESERVED, /* Reserved csrow type */ | ||
| 118 | MEM_UNKNOWN, /* Unknown csrow type */ | ||
| 119 | MEM_FPM, /* Fast page mode */ | ||
| 120 | MEM_EDO, /* Extended data out */ | ||
| 121 | MEM_BEDO, /* Burst Extended data out */ | ||
| 122 | MEM_SDR, /* Single data rate SDRAM */ | ||
| 123 | MEM_RDR, /* Registered single data rate SDRAM */ | ||
| 124 | MEM_DDR, /* Double data rate SDRAM */ | ||
| 125 | MEM_RDDR, /* Registered Double data rate SDRAM */ | ||
| 126 | MEM_RMBS, /* Rambus DRAM */ | ||
| 127 | MEM_DDR2, /* DDR2 RAM */ | ||
| 128 | MEM_FB_DDR2, /* fully buffered DDR2 */ | ||
| 129 | MEM_RDDR2, /* Registered DDR2 RAM */ | ||
| 130 | }; | ||
| 131 | |||
| 132 | #define MEM_FLAG_EMPTY BIT(MEM_EMPTY) | ||
| 133 | #define MEM_FLAG_RESERVED BIT(MEM_RESERVED) | ||
| 134 | #define MEM_FLAG_UNKNOWN BIT(MEM_UNKNOWN) | ||
| 135 | #define MEM_FLAG_FPM BIT(MEM_FPM) | ||
| 136 | #define MEM_FLAG_EDO BIT(MEM_EDO) | ||
| 137 | #define MEM_FLAG_BEDO BIT(MEM_BEDO) | ||
| 138 | #define MEM_FLAG_SDR BIT(MEM_SDR) | ||
| 139 | #define MEM_FLAG_RDR BIT(MEM_RDR) | ||
| 140 | #define MEM_FLAG_DDR BIT(MEM_DDR) | ||
| 141 | #define MEM_FLAG_RDDR BIT(MEM_RDDR) | ||
| 142 | #define MEM_FLAG_RMBS BIT(MEM_RMBS) | ||
| 143 | #define MEM_FLAG_DDR2 BIT(MEM_DDR2) | ||
| 144 | #define MEM_FLAG_FB_DDR2 BIT(MEM_FB_DDR2) | ||
| 145 | #define MEM_FLAG_RDDR2 BIT(MEM_RDDR2) | ||
| 146 | |||
| 147 | /* chipset Error Detection and Correction capabilities and mode */ | ||
| 148 | enum edac_type { | ||
| 149 | EDAC_UNKNOWN = 0, /* Unknown if ECC is available */ | ||
| 150 | EDAC_NONE, /* Doesnt support ECC */ | ||
| 151 | EDAC_RESERVED, /* Reserved ECC type */ | ||
| 152 | EDAC_PARITY, /* Detects parity errors */ | ||
| 153 | EDAC_EC, /* Error Checking - no correction */ | ||
| 154 | EDAC_SECDED, /* Single bit error correction, Double detection */ | ||
| 155 | EDAC_S2ECD2ED, /* Chipkill x2 devices - do these exist? */ | ||
| 156 | EDAC_S4ECD4ED, /* Chipkill x4 devices */ | ||
| 157 | EDAC_S8ECD8ED, /* Chipkill x8 devices */ | ||
| 158 | EDAC_S16ECD16ED, /* Chipkill x16 devices */ | ||
| 159 | }; | ||
| 160 | |||
| 161 | #define EDAC_FLAG_UNKNOWN BIT(EDAC_UNKNOWN) | ||
| 162 | #define EDAC_FLAG_NONE BIT(EDAC_NONE) | ||
| 163 | #define EDAC_FLAG_PARITY BIT(EDAC_PARITY) | ||
| 164 | #define EDAC_FLAG_EC BIT(EDAC_EC) | ||
| 165 | #define EDAC_FLAG_SECDED BIT(EDAC_SECDED) | ||
| 166 | #define EDAC_FLAG_S2ECD2ED BIT(EDAC_S2ECD2ED) | ||
| 167 | #define EDAC_FLAG_S4ECD4ED BIT(EDAC_S4ECD4ED) | ||
| 168 | #define EDAC_FLAG_S8ECD8ED BIT(EDAC_S8ECD8ED) | ||
| 169 | #define EDAC_FLAG_S16ECD16ED BIT(EDAC_S16ECD16ED) | ||
| 170 | |||
| 171 | /* scrubbing capabilities */ | ||
| 172 | enum scrub_type { | ||
| 173 | SCRUB_UNKNOWN = 0, /* Unknown if scrubber is available */ | ||
| 174 | SCRUB_NONE, /* No scrubber */ | ||
| 175 | SCRUB_SW_PROG, /* SW progressive (sequential) scrubbing */ | ||
| 176 | SCRUB_SW_SRC, /* Software scrub only errors */ | ||
| 177 | SCRUB_SW_PROG_SRC, /* Progressive software scrub from an error */ | ||
| 178 | SCRUB_SW_TUNABLE, /* Software scrub frequency is tunable */ | ||
| 179 | SCRUB_HW_PROG, /* HW progressive (sequential) scrubbing */ | ||
| 180 | SCRUB_HW_SRC, /* Hardware scrub only errors */ | ||
| 181 | SCRUB_HW_PROG_SRC, /* Progressive hardware scrub from an error */ | ||
| 182 | SCRUB_HW_TUNABLE /* Hardware scrub frequency is tunable */ | ||
| 183 | }; | ||
| 184 | |||
| 185 | #define SCRUB_FLAG_SW_PROG BIT(SCRUB_SW_PROG) | ||
| 186 | #define SCRUB_FLAG_SW_SRC BIT(SCRUB_SW_SRC_CORR) | ||
| 187 | #define SCRUB_FLAG_SW_PROG_SRC BIT(SCRUB_SW_PROG_SRC_CORR) | ||
| 188 | #define SCRUB_FLAG_SW_TUN BIT(SCRUB_SW_SCRUB_TUNABLE) | ||
| 189 | #define SCRUB_FLAG_HW_PROG BIT(SCRUB_HW_PROG) | ||
| 190 | #define SCRUB_FLAG_HW_SRC BIT(SCRUB_HW_SRC_CORR) | ||
| 191 | #define SCRUB_FLAG_HW_PROG_SRC BIT(SCRUB_HW_PROG_SRC_CORR) | ||
| 192 | #define SCRUB_FLAG_HW_TUN BIT(SCRUB_HW_TUNABLE) | ||
| 193 | |||
| 194 | /* FIXME - should have notify capabilities: NMI, LOG, PROC, etc */ | ||
| 195 | 2 | ||
| 196 | /* | 3 | /* |
| 197 | * There are several things to be aware of that aren't at all obvious: | 4 | * Older .h file for edac, until all drivers are modified |
| 198 | * | ||
| 199 | * | ||
| 200 | * SOCKETS, SOCKET SETS, BANKS, ROWS, CHIP-SELECT ROWS, CHANNELS, etc.. | ||
| 201 | * | ||
| 202 | * These are some of the many terms that are thrown about that don't always | ||
| 203 | * mean what people think they mean (Inconceivable!). In the interest of | ||
| 204 | * creating a common ground for discussion, terms and their definitions | ||
| 205 | * will be established. | ||
| 206 | * | ||
| 207 | * Memory devices: The individual chip on a memory stick. These devices | ||
| 208 | * commonly output 4 and 8 bits each. Grouping several | ||
| 209 | * of these in parallel provides 64 bits which is common | ||
| 210 | * for a memory stick. | ||
| 211 | * | ||
| 212 | * Memory Stick: A printed circuit board that agregates multiple | ||
| 213 | * memory devices in parallel. This is the atomic | ||
| 214 | * memory component that is purchaseable by Joe consumer | ||
| 215 | * and loaded into a memory socket. | ||
| 216 | * | ||
| 217 | * Socket: A physical connector on the motherboard that accepts | ||
| 218 | * a single memory stick. | ||
| 219 | * | ||
| 220 | * Channel: Set of memory devices on a memory stick that must be | ||
| 221 | * grouped in parallel with one or more additional | ||
| 222 | * channels from other memory sticks. This parallel | ||
| 223 | * grouping of the output from multiple channels are | ||
| 224 | * necessary for the smallest granularity of memory access. | ||
| 225 | * Some memory controllers are capable of single channel - | ||
| 226 | * which means that memory sticks can be loaded | ||
| 227 | * individually. Other memory controllers are only | ||
| 228 | * capable of dual channel - which means that memory | ||
| 229 | * sticks must be loaded as pairs (see "socket set"). | ||
| 230 | * | ||
| 231 | * Chip-select row: All of the memory devices that are selected together. | ||
| 232 | * for a single, minimum grain of memory access. | ||
| 233 | * This selects all of the parallel memory devices across | ||
| 234 | * all of the parallel channels. Common chip-select rows | ||
| 235 | * for single channel are 64 bits, for dual channel 128 | ||
| 236 | * bits. | ||
| 237 | * | ||
| 238 | * Single-Ranked stick: A Single-ranked stick has 1 chip-select row of memmory. | ||
| 239 | * Motherboards commonly drive two chip-select pins to | ||
| 240 | * a memory stick. A single-ranked stick, will occupy | ||
| 241 | * only one of those rows. The other will be unused. | ||
| 242 | * | 5 | * |
| 243 | * Double-Ranked stick: A double-ranked stick has two chip-select rows which | ||
| 244 | * access different sets of memory devices. The two | ||
| 245 | * rows cannot be accessed concurrently. | ||
| 246 | * | ||
| 247 | * Double-sided stick: DEPRECATED TERM, see Double-Ranked stick. | ||
| 248 | * A double-sided stick has two chip-select rows which | ||
| 249 | * access different sets of memory devices. The two | ||
| 250 | * rows cannot be accessed concurrently. "Double-sided" | ||
| 251 | * is irrespective of the memory devices being mounted | ||
| 252 | * on both sides of the memory stick. | ||
| 253 | * | ||
| 254 | * Socket set: All of the memory sticks that are required for for | ||
| 255 | * a single memory access or all of the memory sticks | ||
| 256 | * spanned by a chip-select row. A single socket set | ||
| 257 | * has two chip-select rows and if double-sided sticks | ||
| 258 | * are used these will occupy those chip-select rows. | ||
| 259 | * | ||
| 260 | * Bank: This term is avoided because it is unclear when | ||
| 261 | * needing to distinguish between chip-select rows and | ||
| 262 | * socket sets. | ||
| 263 | * | ||
| 264 | * Controller pages: | ||
| 265 | * | ||
| 266 | * Physical pages: | ||
| 267 | * | ||
| 268 | * Virtual pages: | ||
| 269 | * | ||
| 270 | * | ||
| 271 | * STRUCTURE ORGANIZATION AND CHOICES | ||
| 272 | * | ||
| 273 | * | ||
| 274 | * | ||
| 275 | * PS - I enjoyed writing all that about as much as you enjoyed reading it. | ||
| 276 | */ | ||
| 277 | |||
| 278 | struct channel_info { | ||
| 279 | int chan_idx; /* channel index */ | ||
| 280 | u32 ce_count; /* Correctable Errors for this CHANNEL */ | ||
| 281 | char label[EDAC_MC_LABEL_LEN + 1]; /* DIMM label on motherboard */ | ||
| 282 | struct csrow_info *csrow; /* the parent */ | ||
| 283 | }; | ||
| 284 | |||
| 285 | struct csrow_info { | ||
| 286 | unsigned long first_page; /* first page number in dimm */ | ||
| 287 | unsigned long last_page; /* last page number in dimm */ | ||
| 288 | unsigned long page_mask; /* used for interleaving - | ||
| 289 | * 0UL for non intlv | ||
| 290 | */ | ||
| 291 | u32 nr_pages; /* number of pages in csrow */ | ||
| 292 | u32 grain; /* granularity of reported error in bytes */ | ||
| 293 | int csrow_idx; /* the chip-select row */ | ||
| 294 | enum dev_type dtype; /* memory device type */ | ||
| 295 | u32 ue_count; /* Uncorrectable Errors for this csrow */ | ||
| 296 | u32 ce_count; /* Correctable Errors for this csrow */ | ||
| 297 | enum mem_type mtype; /* memory csrow type */ | ||
| 298 | enum edac_type edac_mode; /* EDAC mode for this csrow */ | ||
| 299 | struct mem_ctl_info *mci; /* the parent */ | ||
| 300 | |||
| 301 | struct kobject kobj; /* sysfs kobject for this csrow */ | ||
| 302 | struct completion kobj_complete; | ||
| 303 | |||
| 304 | /* FIXME the number of CHANNELs might need to become dynamic */ | ||
| 305 | u32 nr_channels; | ||
| 306 | struct channel_info *channels; | ||
| 307 | }; | ||
| 308 | |||
| 309 | struct mem_ctl_info { | ||
| 310 | struct list_head link; /* for global list of mem_ctl_info structs */ | ||
| 311 | unsigned long mtype_cap; /* memory types supported by mc */ | ||
| 312 | unsigned long edac_ctl_cap; /* Mem controller EDAC capabilities */ | ||
| 313 | unsigned long edac_cap; /* configuration capabilities - this is | ||
| 314 | * closely related to edac_ctl_cap. The | ||
| 315 | * difference is that the controller may be | ||
| 316 | * capable of s4ecd4ed which would be listed | ||
| 317 | * in edac_ctl_cap, but if channels aren't | ||
| 318 | * capable of s4ecd4ed then the edac_cap would | ||
| 319 | * not have that capability. | ||
| 320 | */ | ||
| 321 | unsigned long scrub_cap; /* chipset scrub capabilities */ | ||
| 322 | enum scrub_type scrub_mode; /* current scrub mode */ | ||
| 323 | |||
| 324 | /* Translates sdram memory scrub rate given in bytes/sec to the | ||
| 325 | internal representation and configures whatever else needs | ||
| 326 | to be configured. | ||
| 327 | */ | ||
| 328 | int (*set_sdram_scrub_rate) (struct mem_ctl_info *mci, u32 *bw); | ||
| 329 | |||
| 330 | /* Get the current sdram memory scrub rate from the internal | ||
| 331 | representation and converts it to the closest matching | ||
| 332 | bandwith in bytes/sec. | ||
| 333 | */ | ||
| 334 | int (*get_sdram_scrub_rate) (struct mem_ctl_info *mci, u32 *bw); | ||
| 335 | |||
| 336 | /* pointer to edac checking routine */ | ||
| 337 | void (*edac_check) (struct mem_ctl_info * mci); | ||
| 338 | |||
| 339 | /* | ||
| 340 | * Remaps memory pages: controller pages to physical pages. | ||
| 341 | * For most MC's, this will be NULL. | ||
| 342 | */ | ||
| 343 | /* FIXME - why not send the phys page to begin with? */ | ||
| 344 | unsigned long (*ctl_page_to_phys) (struct mem_ctl_info * mci, | ||
| 345 | unsigned long page); | ||
| 346 | int mc_idx; | ||
| 347 | int nr_csrows; | ||
| 348 | struct csrow_info *csrows; | ||
| 349 | /* | ||
| 350 | * FIXME - what about controllers on other busses? - IDs must be | ||
| 351 | * unique. dev pointer should be sufficiently unique, but | ||
| 352 | * BUS:SLOT.FUNC numbers may not be unique. | ||
| 353 | */ | ||
| 354 | struct device *dev; | ||
| 355 | const char *mod_name; | ||
| 356 | const char *mod_ver; | ||
| 357 | const char *ctl_name; | ||
| 358 | char proc_name[MC_PROC_NAME_MAX_LEN + 1]; | ||
| 359 | void *pvt_info; | ||
| 360 | u32 ue_noinfo_count; /* Uncorrectable Errors w/o info */ | ||
| 361 | u32 ce_noinfo_count; /* Correctable Errors w/o info */ | ||
| 362 | u32 ue_count; /* Total Uncorrectable Errors for this MC */ | ||
| 363 | u32 ce_count; /* Total Correctable Errors for this MC */ | ||
| 364 | unsigned long start_time; /* mci load start time (in jiffies) */ | ||
| 365 | |||
| 366 | /* this stuff is for safe removal of mc devices from global list while | ||
| 367 | * NMI handlers may be traversing list | ||
| 368 | */ | ||
| 369 | struct rcu_head rcu; | ||
| 370 | struct completion complete; | ||
| 371 | |||
| 372 | /* edac sysfs device control */ | ||
| 373 | struct kobject edac_mci_kobj; | ||
| 374 | struct completion kobj_complete; | ||
| 375 | }; | ||
| 376 | |||
| 377 | #ifdef CONFIG_PCI | ||
| 378 | |||
| 379 | /* write all or some bits in a byte-register*/ | ||
| 380 | static inline void pci_write_bits8(struct pci_dev *pdev, int offset, u8 value, | ||
| 381 | u8 mask) | ||
| 382 | { | ||
| 383 | if (mask != 0xff) { | ||
| 384 | u8 buf; | ||
| 385 | |||
| 386 | pci_read_config_byte(pdev, offset, &buf); | ||
| 387 | value &= mask; | ||
| 388 | buf &= ~mask; | ||
| 389 | value |= buf; | ||
| 390 | } | ||
| 391 | |||
| 392 | pci_write_config_byte(pdev, offset, value); | ||
| 393 | } | ||
| 394 | |||
| 395 | /* write all or some bits in a word-register*/ | ||
| 396 | static inline void pci_write_bits16(struct pci_dev *pdev, int offset, | ||
| 397 | u16 value, u16 mask) | ||
| 398 | { | ||
| 399 | if (mask != 0xffff) { | ||
| 400 | u16 buf; | ||
| 401 | |||
| 402 | pci_read_config_word(pdev, offset, &buf); | ||
| 403 | value &= mask; | ||
| 404 | buf &= ~mask; | ||
| 405 | value |= buf; | ||
| 406 | } | ||
| 407 | |||
| 408 | pci_write_config_word(pdev, offset, value); | ||
| 409 | } | ||
| 410 | |||
| 411 | /* write all or some bits in a dword-register*/ | ||
| 412 | static inline void pci_write_bits32(struct pci_dev *pdev, int offset, | ||
| 413 | u32 value, u32 mask) | ||
| 414 | { | ||
| 415 | if (mask != 0xffff) { | ||
| 416 | u32 buf; | ||
| 417 | |||
| 418 | pci_read_config_dword(pdev, offset, &buf); | ||
| 419 | value &= mask; | ||
| 420 | buf &= ~mask; | ||
| 421 | value |= buf; | ||
| 422 | } | ||
| 423 | |||
| 424 | pci_write_config_dword(pdev, offset, value); | ||
| 425 | } | ||
| 426 | |||
| 427 | #endif /* CONFIG_PCI */ | ||
| 428 | |||
| 429 | extern struct mem_ctl_info * edac_mc_find(int idx); | ||
| 430 | extern int edac_mc_add_mc(struct mem_ctl_info *mci,int mc_idx); | ||
| 431 | extern struct mem_ctl_info * edac_mc_del_mc(struct device *dev); | ||
| 432 | extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, | ||
| 433 | unsigned long page); | ||
| 434 | |||
| 435 | /* | ||
| 436 | * The no info errors are used when error overflows are reported. | ||
| 437 | * There are a limited number of error logging registers that can | ||
| 438 | * be exausted. When all registers are exhausted and an additional | ||
| 439 | * error occurs then an error overflow register records that an | ||
| 440 | * error occured and the type of error, but doesn't have any | ||
| 441 | * further information. The ce/ue versions make for cleaner | ||
| 442 | * reporting logic and function interface - reduces conditional | ||
| 443 | * statement clutter and extra function arguments. | ||
| 444 | */ | ||
| 445 | extern void edac_mc_handle_ce(struct mem_ctl_info *mci, | ||
| 446 | unsigned long page_frame_number, unsigned long offset_in_page, | ||
| 447 | unsigned long syndrome, int row, int channel, | ||
| 448 | const char *msg); | ||
| 449 | extern void edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, | ||
| 450 | const char *msg); | ||
| 451 | extern void edac_mc_handle_ue(struct mem_ctl_info *mci, | ||
| 452 | unsigned long page_frame_number, unsigned long offset_in_page, | ||
| 453 | int row, const char *msg); | ||
| 454 | extern void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, | ||
| 455 | const char *msg); | ||
| 456 | extern void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci, | ||
| 457 | unsigned int csrow, | ||
| 458 | unsigned int channel0, | ||
| 459 | unsigned int channel1, | ||
| 460 | char *msg); | ||
| 461 | extern void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci, | ||
| 462 | unsigned int csrow, | ||
| 463 | unsigned int channel, | ||
| 464 | char *msg); | ||
| 465 | |||
| 466 | /* | ||
| 467 | * This kmalloc's and initializes all the structures. | ||
| 468 | * Can't be used if all structures don't have the same lifetime. | ||
| 469 | */ | 6 | */ |
| 470 | extern struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows, | ||
| 471 | unsigned nr_chans); | ||
| 472 | 7 | ||
| 473 | /* Free an mc previously allocated by edac_mc_alloc() */ | 8 | #include "edac_core.h" |
| 474 | extern void edac_mc_free(struct mem_ctl_info *mci); | ||
| 475 | 9 | ||
| 476 | #endif /* _EDAC_MC_H_ */ | ||
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c new file mode 100644 index 000000000000..4a5e335f61d3 --- /dev/null +++ b/drivers/edac/edac_mc_sysfs.c | |||
| @@ -0,0 +1,889 @@ | |||
| 1 | /* | ||
| 2 | * edac_mc kernel module | ||
| 3 | * (C) 2005, 2006 Linux Networx (http://lnxi.com) | ||
| 4 | * This file may be distributed under the terms of the | ||
| 5 | * GNU General Public License. | ||
| 6 | * | ||
| 7 | * Written Doug Thompson <norsk5@xmission.com> | ||
| 8 | * | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/sysdev.h> | ||
| 13 | #include <linux/ctype.h> | ||
| 14 | |||
| 15 | #include "edac_mc.h" | ||
| 16 | #include "edac_module.h" | ||
| 17 | |||
| 18 | /* MC EDAC Controls, setable by module parameter, and sysfs */ | ||
| 19 | static int log_ue = 1; | ||
| 20 | static int log_ce = 1; | ||
| 21 | static int panic_on_ue; | ||
| 22 | static int poll_msec = 1000; | ||
| 23 | |||
| 24 | /* Getter functions for above */ | ||
| 25 | int edac_get_log_ue() | ||
| 26 | { | ||
| 27 | return log_ue; | ||
| 28 | } | ||
| 29 | |||
| 30 | int edac_get_log_ce() | ||
| 31 | { | ||
| 32 | return log_ce; | ||
| 33 | } | ||
| 34 | |||
| 35 | int edac_get_panic_on_ue() | ||
| 36 | { | ||
| 37 | return panic_on_ue; | ||
| 38 | } | ||
| 39 | |||
| 40 | int edac_get_poll_msec() | ||
| 41 | { | ||
| 42 | return poll_msec; | ||
| 43 | } | ||
| 44 | |||
| 45 | /* Parameter declarations for above */ | ||
| 46 | module_param(panic_on_ue, int, 0644); | ||
| 47 | MODULE_PARM_DESC(panic_on_ue, "Panic on uncorrected error: 0=off 1=on"); | ||
| 48 | module_param(log_ue, int, 0644); | ||
| 49 | MODULE_PARM_DESC(log_ue, "Log uncorrectable error to console: 0=off 1=on"); | ||
| 50 | module_param(log_ce, int, 0644); | ||
| 51 | MODULE_PARM_DESC(log_ce, "Log correctable error to console: 0=off 1=on"); | ||
| 52 | module_param(poll_msec, int, 0644); | ||
| 53 | MODULE_PARM_DESC(poll_msec, "Polling period in milliseconds"); | ||
| 54 | |||
| 55 | |||
| 56 | /* | ||
| 57 | * various constants for Memory Controllers | ||
| 58 | */ | ||
| 59 | static const char *mem_types[] = { | ||
| 60 | [MEM_EMPTY] = "Empty", | ||
| 61 | [MEM_RESERVED] = "Reserved", | ||
| 62 | [MEM_UNKNOWN] = "Unknown", | ||
| 63 | [MEM_FPM] = "FPM", | ||
| 64 | [MEM_EDO] = "EDO", | ||
| 65 | [MEM_BEDO] = "BEDO", | ||
| 66 | [MEM_SDR] = "Unbuffered-SDR", | ||
| 67 | [MEM_RDR] = "Registered-SDR", | ||
| 68 | [MEM_DDR] = "Unbuffered-DDR", | ||
| 69 | [MEM_RDDR] = "Registered-DDR", | ||
| 70 | [MEM_RMBS] = "RMBS" | ||
| 71 | }; | ||
| 72 | |||
| 73 | static const char *dev_types[] = { | ||
| 74 | [DEV_UNKNOWN] = "Unknown", | ||
| 75 | [DEV_X1] = "x1", | ||
| 76 | [DEV_X2] = "x2", | ||
| 77 | [DEV_X4] = "x4", | ||
| 78 | [DEV_X8] = "x8", | ||
| 79 | [DEV_X16] = "x16", | ||
| 80 | [DEV_X32] = "x32", | ||
| 81 | [DEV_X64] = "x64" | ||
| 82 | }; | ||
| 83 | |||
| 84 | static const char *edac_caps[] = { | ||
| 85 | [EDAC_UNKNOWN] = "Unknown", | ||
| 86 | [EDAC_NONE] = "None", | ||
| 87 | [EDAC_RESERVED] = "Reserved", | ||
| 88 | [EDAC_PARITY] = "PARITY", | ||
| 89 | [EDAC_EC] = "EC", | ||
| 90 | [EDAC_SECDED] = "SECDED", | ||
| 91 | [EDAC_S2ECD2ED] = "S2ECD2ED", | ||
| 92 | [EDAC_S4ECD4ED] = "S4ECD4ED", | ||
| 93 | [EDAC_S8ECD8ED] = "S8ECD8ED", | ||
| 94 | [EDAC_S16ECD16ED] = "S16ECD16ED" | ||
| 95 | }; | ||
| 96 | |||
| 97 | /* | ||
| 98 | * sysfs object: /sys/devices/system/edac | ||
| 99 | * need to export to other files in this modules | ||
| 100 | */ | ||
| 101 | struct sysdev_class edac_class = { | ||
| 102 | set_kset_name("edac"), | ||
| 103 | }; | ||
| 104 | |||
| 105 | /* sysfs object: | ||
| 106 | * /sys/devices/system/edac/mc | ||
| 107 | */ | ||
| 108 | static struct kobject edac_memctrl_kobj; | ||
| 109 | |||
| 110 | /* We use these to wait for the reference counts on edac_memctrl_kobj and | ||
| 111 | * edac_pci_kobj to reach 0. | ||
| 112 | */ | ||
| 113 | static struct completion edac_memctrl_kobj_complete; | ||
| 114 | |||
| 115 | /* | ||
| 116 | * /sys/devices/system/edac/mc; | ||
| 117 | * data structures and methods | ||
| 118 | */ | ||
| 119 | static ssize_t memctrl_int_show(void *ptr, char *buffer) | ||
| 120 | { | ||
| 121 | int *value = (int*) ptr; | ||
| 122 | return sprintf(buffer, "%u\n", *value); | ||
| 123 | } | ||
| 124 | |||
| 125 | static ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t count) | ||
| 126 | { | ||
| 127 | int *value = (int*) ptr; | ||
| 128 | |||
| 129 | if (isdigit(*buffer)) | ||
| 130 | *value = simple_strtoul(buffer, NULL, 0); | ||
| 131 | |||
| 132 | return count; | ||
| 133 | } | ||
| 134 | |||
| 135 | struct memctrl_dev_attribute { | ||
| 136 | struct attribute attr; | ||
| 137 | void *value; | ||
| 138 | ssize_t (*show)(void *,char *); | ||
| 139 | ssize_t (*store)(void *, const char *, size_t); | ||
| 140 | }; | ||
| 141 | |||
| 142 | /* Set of show/store abstract level functions for memory control object */ | ||
| 143 | static ssize_t memctrl_dev_show(struct kobject *kobj, | ||
| 144 | struct attribute *attr, char *buffer) | ||
| 145 | { | ||
| 146 | struct memctrl_dev_attribute *memctrl_dev; | ||
| 147 | memctrl_dev = (struct memctrl_dev_attribute*)attr; | ||
| 148 | |||
| 149 | if (memctrl_dev->show) | ||
| 150 | return memctrl_dev->show(memctrl_dev->value, buffer); | ||
| 151 | |||
| 152 | return -EIO; | ||
| 153 | } | ||
| 154 | |||
| 155 | static ssize_t memctrl_dev_store(struct kobject *kobj, struct attribute *attr, | ||
| 156 | const char *buffer, size_t count) | ||
| 157 | { | ||
| 158 | struct memctrl_dev_attribute *memctrl_dev; | ||
| 159 | memctrl_dev = (struct memctrl_dev_attribute*)attr; | ||
| 160 | |||
| 161 | if (memctrl_dev->store) | ||
| 162 | return memctrl_dev->store(memctrl_dev->value, buffer, count); | ||
| 163 | |||
| 164 | return -EIO; | ||
| 165 | } | ||
| 166 | |||
| 167 | static struct sysfs_ops memctrlfs_ops = { | ||
| 168 | .show = memctrl_dev_show, | ||
| 169 | .store = memctrl_dev_store | ||
| 170 | }; | ||
| 171 | |||
| 172 | #define MEMCTRL_ATTR(_name,_mode,_show,_store) \ | ||
| 173 | static struct memctrl_dev_attribute attr_##_name = { \ | ||
| 174 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
| 175 | .value = &_name, \ | ||
| 176 | .show = _show, \ | ||
| 177 | .store = _store, \ | ||
| 178 | }; | ||
| 179 | |||
| 180 | #define MEMCTRL_STRING_ATTR(_name,_data,_mode,_show,_store) \ | ||
| 181 | static struct memctrl_dev_attribute attr_##_name = { \ | ||
| 182 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
| 183 | .value = _data, \ | ||
| 184 | .show = _show, \ | ||
| 185 | .store = _store, \ | ||
| 186 | }; | ||
| 187 | |||
| 188 | /* csrow<id> control files */ | ||
| 189 | MEMCTRL_ATTR(panic_on_ue,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store); | ||
| 190 | MEMCTRL_ATTR(log_ue,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store); | ||
| 191 | MEMCTRL_ATTR(log_ce,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store); | ||
| 192 | MEMCTRL_ATTR(poll_msec,S_IRUGO|S_IWUSR,memctrl_int_show,memctrl_int_store); | ||
| 193 | |||
| 194 | /* Base Attributes of the memory ECC object */ | ||
| 195 | static struct memctrl_dev_attribute *memctrl_attr[] = { | ||
| 196 | &attr_panic_on_ue, | ||
| 197 | &attr_log_ue, | ||
| 198 | &attr_log_ce, | ||
| 199 | &attr_poll_msec, | ||
| 200 | NULL, | ||
| 201 | }; | ||
| 202 | |||
| 203 | /* Main MC kobject release() function */ | ||
| 204 | static void edac_memctrl_master_release(struct kobject *kobj) | ||
| 205 | { | ||
| 206 | debugf1("%s()\n", __func__); | ||
| 207 | complete(&edac_memctrl_kobj_complete); | ||
| 208 | } | ||
| 209 | |||
| 210 | static struct kobj_type ktype_memctrl = { | ||
| 211 | .release = edac_memctrl_master_release, | ||
| 212 | .sysfs_ops = &memctrlfs_ops, | ||
| 213 | .default_attrs = (struct attribute **) memctrl_attr, | ||
| 214 | }; | ||
| 215 | |||
| 216 | /* Initialize the main sysfs entries for edac: | ||
| 217 | * /sys/devices/system/edac | ||
| 218 | * | ||
| 219 | * and children | ||
| 220 | * | ||
| 221 | * Return: 0 SUCCESS | ||
| 222 | * !0 FAILURE | ||
| 223 | */ | ||
| 224 | int edac_sysfs_memctrl_setup(void) | ||
| 225 | { | ||
| 226 | int err = 0; | ||
| 227 | |||
| 228 | debugf1("%s()\n", __func__); | ||
| 229 | |||
| 230 | /* create the /sys/devices/system/edac directory */ | ||
| 231 | err = sysdev_class_register(&edac_class); | ||
| 232 | |||
| 233 | if (err) { | ||
| 234 | debugf1("%s() error=%d\n", __func__, err); | ||
| 235 | return err; | ||
| 236 | } | ||
| 237 | |||
| 238 | /* Init the MC's kobject */ | ||
| 239 | memset(&edac_memctrl_kobj, 0, sizeof (edac_memctrl_kobj)); | ||
| 240 | edac_memctrl_kobj.parent = &edac_class.kset.kobj; | ||
| 241 | edac_memctrl_kobj.ktype = &ktype_memctrl; | ||
| 242 | |||
| 243 | /* generate sysfs "..../edac/mc" */ | ||
| 244 | err = kobject_set_name(&edac_memctrl_kobj,"mc"); | ||
| 245 | |||
| 246 | if (err) | ||
| 247 | goto fail; | ||
| 248 | |||
| 249 | /* FIXME: maybe new sysdev_create_subdir() */ | ||
| 250 | err = kobject_register(&edac_memctrl_kobj); | ||
| 251 | |||
| 252 | if (err) { | ||
| 253 | debugf1("Failed to register '.../edac/mc'\n"); | ||
| 254 | goto fail; | ||
| 255 | } | ||
| 256 | |||
| 257 | debugf1("Registered '.../edac/mc' kobject\n"); | ||
| 258 | |||
| 259 | return 0; | ||
| 260 | |||
| 261 | fail: | ||
| 262 | sysdev_class_unregister(&edac_class); | ||
| 263 | return err; | ||
| 264 | } | ||
| 265 | |||
| 266 | /* | ||
| 267 | * MC teardown: | ||
| 268 | * the '..../edac/mc' kobject followed by '..../edac' itself | ||
| 269 | */ | ||
| 270 | void edac_sysfs_memctrl_teardown(void) | ||
| 271 | { | ||
| 272 | debugf0("MC: " __FILE__ ": %s()\n", __func__); | ||
| 273 | |||
| 274 | /* Unregister the MC's kobject and wait for reference count to reach 0. | ||
| 275 | */ | ||
| 276 | init_completion(&edac_memctrl_kobj_complete); | ||
| 277 | kobject_unregister(&edac_memctrl_kobj); | ||
| 278 | wait_for_completion(&edac_memctrl_kobj_complete); | ||
| 279 | |||
| 280 | /* Unregister the 'edac' object */ | ||
| 281 | sysdev_class_unregister(&edac_class); | ||
| 282 | } | ||
| 283 | |||
| 284 | |||
| 285 | /* EDAC sysfs CSROW data structures and methods | ||
| 286 | */ | ||
| 287 | |||
| 288 | /* Set of more default csrow<id> attribute show/store functions */ | ||
| 289 | static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data, int private) | ||
| 290 | { | ||
| 291 | return sprintf(data,"%u\n", csrow->ue_count); | ||
| 292 | } | ||
| 293 | |||
| 294 | static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data, int private) | ||
| 295 | { | ||
| 296 | return sprintf(data,"%u\n", csrow->ce_count); | ||
| 297 | } | ||
| 298 | |||
| 299 | static ssize_t csrow_size_show(struct csrow_info *csrow, char *data, int private) | ||
| 300 | { | ||
| 301 | return sprintf(data,"%u\n", PAGES_TO_MiB(csrow->nr_pages)); | ||
| 302 | } | ||
| 303 | |||
| 304 | static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data, int private) | ||
| 305 | { | ||
| 306 | return sprintf(data,"%s\n", mem_types[csrow->mtype]); | ||
| 307 | } | ||
| 308 | |||
| 309 | static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data, int private) | ||
| 310 | { | ||
| 311 | return sprintf(data,"%s\n", dev_types[csrow->dtype]); | ||
| 312 | } | ||
| 313 | |||
| 314 | static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data, int private) | ||
| 315 | { | ||
| 316 | return sprintf(data,"%s\n", edac_caps[csrow->edac_mode]); | ||
| 317 | } | ||
| 318 | |||
| 319 | /* show/store functions for DIMM Label attributes */ | ||
| 320 | static ssize_t channel_dimm_label_show(struct csrow_info *csrow, | ||
| 321 | char *data, int channel) | ||
| 322 | { | ||
| 323 | return snprintf(data, EDAC_MC_LABEL_LEN,"%s", | ||
| 324 | csrow->channels[channel].label); | ||
| 325 | } | ||
| 326 | |||
| 327 | static ssize_t channel_dimm_label_store(struct csrow_info *csrow, | ||
| 328 | const char *data, | ||
| 329 | size_t count, | ||
| 330 | int channel) | ||
| 331 | { | ||
| 332 | ssize_t max_size = 0; | ||
| 333 | |||
| 334 | max_size = min((ssize_t)count,(ssize_t)EDAC_MC_LABEL_LEN-1); | ||
| 335 | strncpy(csrow->channels[channel].label, data, max_size); | ||
| 336 | csrow->channels[channel].label[max_size] = '\0'; | ||
| 337 | |||
| 338 | return max_size; | ||
| 339 | } | ||
| 340 | |||
| 341 | /* show function for dynamic chX_ce_count attribute */ | ||
| 342 | static ssize_t channel_ce_count_show(struct csrow_info *csrow, | ||
| 343 | char *data, | ||
| 344 | int channel) | ||
| 345 | { | ||
| 346 | return sprintf(data, "%u\n", csrow->channels[channel].ce_count); | ||
| 347 | } | ||
| 348 | |||
| 349 | /* csrow specific attribute structure */ | ||
| 350 | struct csrowdev_attribute { | ||
| 351 | struct attribute attr; | ||
| 352 | ssize_t (*show)(struct csrow_info *,char *,int); | ||
| 353 | ssize_t (*store)(struct csrow_info *, const char *,size_t,int); | ||
| 354 | int private; | ||
| 355 | }; | ||
| 356 | |||
| 357 | #define to_csrow(k) container_of(k, struct csrow_info, kobj) | ||
| 358 | #define to_csrowdev_attr(a) container_of(a, struct csrowdev_attribute, attr) | ||
| 359 | |||
| 360 | /* Set of show/store higher level functions for default csrow attributes */ | ||
| 361 | static ssize_t csrowdev_show(struct kobject *kobj, | ||
| 362 | struct attribute *attr, | ||
| 363 | char *buffer) | ||
| 364 | { | ||
| 365 | struct csrow_info *csrow = to_csrow(kobj); | ||
| 366 | struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); | ||
| 367 | |||
| 368 | if (csrowdev_attr->show) | ||
| 369 | return csrowdev_attr->show(csrow, | ||
| 370 | buffer, | ||
| 371 | csrowdev_attr->private); | ||
| 372 | return -EIO; | ||
| 373 | } | ||
| 374 | |||
| 375 | static ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr, | ||
| 376 | const char *buffer, size_t count) | ||
| 377 | { | ||
| 378 | struct csrow_info *csrow = to_csrow(kobj); | ||
| 379 | struct csrowdev_attribute * csrowdev_attr = to_csrowdev_attr(attr); | ||
| 380 | |||
| 381 | if (csrowdev_attr->store) | ||
| 382 | return csrowdev_attr->store(csrow, | ||
| 383 | buffer, | ||
| 384 | count, | ||
| 385 | csrowdev_attr->private); | ||
| 386 | return -EIO; | ||
| 387 | } | ||
| 388 | |||
| 389 | static struct sysfs_ops csrowfs_ops = { | ||
| 390 | .show = csrowdev_show, | ||
| 391 | .store = csrowdev_store | ||
| 392 | }; | ||
| 393 | |||
| 394 | #define CSROWDEV_ATTR(_name,_mode,_show,_store,_private) \ | ||
| 395 | static struct csrowdev_attribute attr_##_name = { \ | ||
| 396 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
| 397 | .show = _show, \ | ||
| 398 | .store = _store, \ | ||
| 399 | .private = _private, \ | ||
| 400 | }; | ||
| 401 | |||
| 402 | /* default cwrow<id>/attribute files */ | ||
| 403 | CSROWDEV_ATTR(size_mb,S_IRUGO,csrow_size_show,NULL,0); | ||
| 404 | CSROWDEV_ATTR(dev_type,S_IRUGO,csrow_dev_type_show,NULL,0); | ||
| 405 | CSROWDEV_ATTR(mem_type,S_IRUGO,csrow_mem_type_show,NULL,0); | ||
| 406 | CSROWDEV_ATTR(edac_mode,S_IRUGO,csrow_edac_mode_show,NULL,0); | ||
| 407 | CSROWDEV_ATTR(ue_count,S_IRUGO,csrow_ue_count_show,NULL,0); | ||
| 408 | CSROWDEV_ATTR(ce_count,S_IRUGO,csrow_ce_count_show,NULL,0); | ||
| 409 | |||
| 410 | /* default attributes of the CSROW<id> object */ | ||
| 411 | static struct csrowdev_attribute *default_csrow_attr[] = { | ||
| 412 | &attr_dev_type, | ||
| 413 | &attr_mem_type, | ||
| 414 | &attr_edac_mode, | ||
| 415 | &attr_size_mb, | ||
| 416 | &attr_ue_count, | ||
| 417 | &attr_ce_count, | ||
| 418 | NULL, | ||
| 419 | }; | ||
| 420 | |||
| 421 | |||
| 422 | /* possible dynamic channel DIMM Label attribute files */ | ||
| 423 | CSROWDEV_ATTR(ch0_dimm_label,S_IRUGO|S_IWUSR, | ||
| 424 | channel_dimm_label_show, | ||
| 425 | channel_dimm_label_store, | ||
| 426 | 0 ); | ||
| 427 | CSROWDEV_ATTR(ch1_dimm_label,S_IRUGO|S_IWUSR, | ||
| 428 | channel_dimm_label_show, | ||
| 429 | channel_dimm_label_store, | ||
| 430 | 1 ); | ||
| 431 | CSROWDEV_ATTR(ch2_dimm_label,S_IRUGO|S_IWUSR, | ||
| 432 | channel_dimm_label_show, | ||
| 433 | channel_dimm_label_store, | ||
| 434 | 2 ); | ||
| 435 | CSROWDEV_ATTR(ch3_dimm_label,S_IRUGO|S_IWUSR, | ||
| 436 | channel_dimm_label_show, | ||
| 437 | channel_dimm_label_store, | ||
| 438 | 3 ); | ||
| 439 | CSROWDEV_ATTR(ch4_dimm_label,S_IRUGO|S_IWUSR, | ||
| 440 | channel_dimm_label_show, | ||
| 441 | channel_dimm_label_store, | ||
| 442 | 4 ); | ||
| 443 | CSROWDEV_ATTR(ch5_dimm_label,S_IRUGO|S_IWUSR, | ||
| 444 | channel_dimm_label_show, | ||
| 445 | channel_dimm_label_store, | ||
| 446 | 5 ); | ||
| 447 | |||
| 448 | /* Total possible dynamic DIMM Label attribute file table */ | ||
| 449 | static struct csrowdev_attribute *dynamic_csrow_dimm_attr[] = { | ||
| 450 | &attr_ch0_dimm_label, | ||
| 451 | &attr_ch1_dimm_label, | ||
| 452 | &attr_ch2_dimm_label, | ||
| 453 | &attr_ch3_dimm_label, | ||
| 454 | &attr_ch4_dimm_label, | ||
| 455 | &attr_ch5_dimm_label | ||
| 456 | }; | ||
| 457 | |||
| 458 | /* possible dynamic channel ce_count attribute files */ | ||
| 459 | CSROWDEV_ATTR(ch0_ce_count,S_IRUGO|S_IWUSR, | ||
| 460 | channel_ce_count_show, | ||
| 461 | NULL, | ||
| 462 | 0 ); | ||
| 463 | CSROWDEV_ATTR(ch1_ce_count,S_IRUGO|S_IWUSR, | ||
| 464 | channel_ce_count_show, | ||
| 465 | NULL, | ||
| 466 | 1 ); | ||
| 467 | CSROWDEV_ATTR(ch2_ce_count,S_IRUGO|S_IWUSR, | ||
| 468 | channel_ce_count_show, | ||
| 469 | NULL, | ||
| 470 | 2 ); | ||
| 471 | CSROWDEV_ATTR(ch3_ce_count,S_IRUGO|S_IWUSR, | ||
| 472 | channel_ce_count_show, | ||
| 473 | NULL, | ||
| 474 | 3 ); | ||
| 475 | CSROWDEV_ATTR(ch4_ce_count,S_IRUGO|S_IWUSR, | ||
| 476 | channel_ce_count_show, | ||
| 477 | NULL, | ||
| 478 | 4 ); | ||
| 479 | CSROWDEV_ATTR(ch5_ce_count,S_IRUGO|S_IWUSR, | ||
| 480 | channel_ce_count_show, | ||
| 481 | NULL, | ||
| 482 | 5 ); | ||
| 483 | |||
| 484 | /* Total possible dynamic ce_count attribute file table */ | ||
| 485 | static struct csrowdev_attribute *dynamic_csrow_ce_count_attr[] = { | ||
| 486 | &attr_ch0_ce_count, | ||
| 487 | &attr_ch1_ce_count, | ||
| 488 | &attr_ch2_ce_count, | ||
| 489 | &attr_ch3_ce_count, | ||
| 490 | &attr_ch4_ce_count, | ||
| 491 | &attr_ch5_ce_count | ||
| 492 | }; | ||
| 493 | |||
| 494 | |||
| 495 | #define EDAC_NR_CHANNELS 6 | ||
| 496 | |||
| 497 | /* Create dynamic CHANNEL files, indexed by 'chan', under specifed CSROW */ | ||
| 498 | static int edac_create_channel_files(struct kobject *kobj, int chan) | ||
| 499 | { | ||
| 500 | int err=-ENODEV; | ||
| 501 | |||
| 502 | if (chan >= EDAC_NR_CHANNELS) | ||
| 503 | return err; | ||
| 504 | |||
| 505 | /* create the DIMM label attribute file */ | ||
| 506 | err = sysfs_create_file(kobj, | ||
| 507 | (struct attribute *) dynamic_csrow_dimm_attr[chan]); | ||
| 508 | |||
| 509 | if (!err) { | ||
| 510 | /* create the CE Count attribute file */ | ||
| 511 | err = sysfs_create_file(kobj, | ||
| 512 | (struct attribute *) dynamic_csrow_ce_count_attr[chan]); | ||
| 513 | } else { | ||
| 514 | debugf1("%s() dimm labels and ce_count files created", __func__); | ||
| 515 | } | ||
| 516 | |||
| 517 | return err; | ||
| 518 | } | ||
| 519 | |||
| 520 | /* No memory to release for this kobj */ | ||
| 521 | static void edac_csrow_instance_release(struct kobject *kobj) | ||
| 522 | { | ||
| 523 | struct csrow_info *cs; | ||
| 524 | |||
| 525 | cs = container_of(kobj, struct csrow_info, kobj); | ||
| 526 | complete(&cs->kobj_complete); | ||
| 527 | } | ||
| 528 | |||
| 529 | /* the kobj_type instance for a CSROW */ | ||
| 530 | static struct kobj_type ktype_csrow = { | ||
| 531 | .release = edac_csrow_instance_release, | ||
| 532 | .sysfs_ops = &csrowfs_ops, | ||
| 533 | .default_attrs = (struct attribute **) default_csrow_attr, | ||
| 534 | }; | ||
| 535 | |||
| 536 | /* Create a CSROW object under specifed edac_mc_device */ | ||
| 537 | static int edac_create_csrow_object( | ||
| 538 | struct kobject *edac_mci_kobj, | ||
| 539 | struct csrow_info *csrow, | ||
| 540 | int index) | ||
| 541 | { | ||
| 542 | int err = 0; | ||
| 543 | int chan; | ||
| 544 | |||
| 545 | memset(&csrow->kobj, 0, sizeof(csrow->kobj)); | ||
| 546 | |||
| 547 | /* generate ..../edac/mc/mc<id>/csrow<index> */ | ||
| 548 | |||
| 549 | csrow->kobj.parent = edac_mci_kobj; | ||
| 550 | csrow->kobj.ktype = &ktype_csrow; | ||
| 551 | |||
| 552 | /* name this instance of csrow<id> */ | ||
| 553 | err = kobject_set_name(&csrow->kobj,"csrow%d",index); | ||
| 554 | if (err) | ||
| 555 | goto error_exit; | ||
| 556 | |||
| 557 | /* Instanstiate the csrow object */ | ||
| 558 | err = kobject_register(&csrow->kobj); | ||
| 559 | if (!err) { | ||
| 560 | /* Create the dyanmic attribute files on this csrow, | ||
| 561 | * namely, the DIMM labels and the channel ce_count | ||
| 562 | */ | ||
| 563 | for (chan = 0; chan < csrow->nr_channels; chan++) { | ||
| 564 | err = edac_create_channel_files(&csrow->kobj,chan); | ||
| 565 | if (err) | ||
| 566 | break; | ||
| 567 | } | ||
| 568 | } | ||
| 569 | |||
| 570 | error_exit: | ||
| 571 | return err; | ||
| 572 | } | ||
| 573 | |||
| 574 | /* default sysfs methods and data structures for the main MCI kobject */ | ||
| 575 | |||
| 576 | static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, | ||
| 577 | const char *data, size_t count) | ||
| 578 | { | ||
| 579 | int row, chan; | ||
| 580 | |||
| 581 | mci->ue_noinfo_count = 0; | ||
| 582 | mci->ce_noinfo_count = 0; | ||
| 583 | mci->ue_count = 0; | ||
| 584 | mci->ce_count = 0; | ||
| 585 | |||
| 586 | for (row = 0; row < mci->nr_csrows; row++) { | ||
| 587 | struct csrow_info *ri = &mci->csrows[row]; | ||
| 588 | |||
| 589 | ri->ue_count = 0; | ||
| 590 | ri->ce_count = 0; | ||
| 591 | |||
| 592 | for (chan = 0; chan < ri->nr_channels; chan++) | ||
| 593 | ri->channels[chan].ce_count = 0; | ||
| 594 | } | ||
| 595 | |||
| 596 | mci->start_time = jiffies; | ||
| 597 | return count; | ||
| 598 | } | ||
| 599 | |||
| 600 | /* memory scrubbing */ | ||
| 601 | static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, | ||
| 602 | const char *data, size_t count) | ||
| 603 | { | ||
| 604 | u32 bandwidth = -1; | ||
| 605 | |||
| 606 | if (mci->set_sdram_scrub_rate) { | ||
| 607 | |||
| 608 | memctrl_int_store(&bandwidth, data, count); | ||
| 609 | |||
| 610 | if (!(*mci->set_sdram_scrub_rate)(mci, &bandwidth)) { | ||
| 611 | edac_printk(KERN_DEBUG, EDAC_MC, | ||
| 612 | "Scrub rate set successfully, applied: %d\n", | ||
| 613 | bandwidth); | ||
| 614 | } else { | ||
| 615 | /* FIXME: error codes maybe? */ | ||
| 616 | edac_printk(KERN_DEBUG, EDAC_MC, | ||
| 617 | "Scrub rate set FAILED, could not apply: %d\n", | ||
| 618 | bandwidth); | ||
| 619 | } | ||
| 620 | } else { | ||
| 621 | /* FIXME: produce "not implemented" ERROR for user-side. */ | ||
| 622 | edac_printk(KERN_WARNING, EDAC_MC, | ||
| 623 | "Memory scrubbing 'set'control is not implemented!\n"); | ||
| 624 | } | ||
| 625 | return count; | ||
| 626 | } | ||
| 627 | |||
| 628 | static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) | ||
| 629 | { | ||
| 630 | u32 bandwidth = -1; | ||
| 631 | |||
| 632 | if (mci->get_sdram_scrub_rate) { | ||
| 633 | if (!(*mci->get_sdram_scrub_rate)(mci, &bandwidth)) { | ||
| 634 | edac_printk(KERN_DEBUG, EDAC_MC, | ||
| 635 | "Scrub rate successfully, fetched: %d\n", | ||
| 636 | bandwidth); | ||
| 637 | } else { | ||
| 638 | /* FIXME: error codes maybe? */ | ||
| 639 | edac_printk(KERN_DEBUG, EDAC_MC, | ||
| 640 | "Scrub rate fetch FAILED, got: %d\n", | ||
| 641 | bandwidth); | ||
| 642 | } | ||
| 643 | } else { | ||
| 644 | /* FIXME: produce "not implemented" ERROR for user-side. */ | ||
| 645 | edac_printk(KERN_WARNING, EDAC_MC, | ||
| 646 | "Memory scrubbing 'get' control is not implemented!\n"); | ||
| 647 | } | ||
| 648 | return sprintf(data, "%d\n", bandwidth); | ||
| 649 | } | ||
| 650 | |||
| 651 | /* default attribute files for the MCI object */ | ||
| 652 | static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data) | ||
| 653 | { | ||
| 654 | return sprintf(data,"%d\n", mci->ue_count); | ||
| 655 | } | ||
| 656 | |||
| 657 | static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data) | ||
| 658 | { | ||
| 659 | return sprintf(data,"%d\n", mci->ce_count); | ||
| 660 | } | ||
| 661 | |||
| 662 | static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data) | ||
| 663 | { | ||
| 664 | return sprintf(data,"%d\n", mci->ce_noinfo_count); | ||
| 665 | } | ||
| 666 | |||
| 667 | static ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data) | ||
| 668 | { | ||
| 669 | return sprintf(data,"%d\n", mci->ue_noinfo_count); | ||
| 670 | } | ||
| 671 | |||
| 672 | static ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data) | ||
| 673 | { | ||
| 674 | return sprintf(data,"%ld\n", (jiffies - mci->start_time) / HZ); | ||
| 675 | } | ||
| 676 | |||
| 677 | static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data) | ||
| 678 | { | ||
| 679 | return sprintf(data,"%s\n", mci->ctl_name); | ||
| 680 | } | ||
| 681 | |||
| 682 | static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) | ||
| 683 | { | ||
| 684 | int total_pages, csrow_idx; | ||
| 685 | |||
| 686 | for (total_pages = csrow_idx = 0; csrow_idx < mci->nr_csrows; | ||
| 687 | csrow_idx++) { | ||
| 688 | struct csrow_info *csrow = &mci->csrows[csrow_idx]; | ||
| 689 | |||
| 690 | if (!csrow->nr_pages) | ||
| 691 | continue; | ||
| 692 | |||
| 693 | total_pages += csrow->nr_pages; | ||
| 694 | } | ||
| 695 | |||
| 696 | return sprintf(data,"%u\n", PAGES_TO_MiB(total_pages)); | ||
| 697 | } | ||
| 698 | |||
| 699 | struct mcidev_attribute { | ||
| 700 | struct attribute attr; | ||
| 701 | ssize_t (*show)(struct mem_ctl_info *,char *); | ||
| 702 | ssize_t (*store)(struct mem_ctl_info *, const char *,size_t); | ||
| 703 | }; | ||
| 704 | |||
| 705 | #define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj) | ||
| 706 | #define to_mcidev_attr(a) container_of(a, struct mcidev_attribute, attr) | ||
| 707 | |||
| 708 | /* MCI show/store functions for top most object */ | ||
| 709 | static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr, | ||
| 710 | char *buffer) | ||
| 711 | { | ||
| 712 | struct mem_ctl_info *mem_ctl_info = to_mci(kobj); | ||
| 713 | struct mcidev_attribute * mcidev_attr = to_mcidev_attr(attr); | ||
| 714 | |||
| 715 | if (mcidev_attr->show) | ||
| 716 | return mcidev_attr->show(mem_ctl_info, buffer); | ||
| 717 | |||
| 718 | return -EIO; | ||
| 719 | } | ||
| 720 | |||
| 721 | static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, | ||
| 722 | const char *buffer, size_t count) | ||
| 723 | { | ||
| 724 | struct mem_ctl_info *mem_ctl_info = to_mci(kobj); | ||
| 725 | struct mcidev_attribute * mcidev_attr = to_mcidev_attr(attr); | ||
| 726 | |||
| 727 | if (mcidev_attr->store) | ||
| 728 | return mcidev_attr->store(mem_ctl_info, buffer, count); | ||
| 729 | |||
| 730 | return -EIO; | ||
| 731 | } | ||
| 732 | |||
| 733 | static struct sysfs_ops mci_ops = { | ||
| 734 | .show = mcidev_show, | ||
| 735 | .store = mcidev_store | ||
| 736 | }; | ||
| 737 | |||
| 738 | #define MCIDEV_ATTR(_name,_mode,_show,_store) \ | ||
| 739 | static struct mcidev_attribute mci_attr_##_name = { \ | ||
| 740 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
| 741 | .show = _show, \ | ||
| 742 | .store = _store, \ | ||
| 743 | }; | ||
| 744 | |||
| 745 | /* default Control file */ | ||
| 746 | MCIDEV_ATTR(reset_counters,S_IWUSR,NULL,mci_reset_counters_store); | ||
| 747 | |||
| 748 | /* default Attribute files */ | ||
| 749 | MCIDEV_ATTR(mc_name,S_IRUGO,mci_ctl_name_show,NULL); | ||
| 750 | MCIDEV_ATTR(size_mb,S_IRUGO,mci_size_mb_show,NULL); | ||
| 751 | MCIDEV_ATTR(seconds_since_reset,S_IRUGO,mci_seconds_show,NULL); | ||
| 752 | MCIDEV_ATTR(ue_noinfo_count,S_IRUGO,mci_ue_noinfo_show,NULL); | ||
| 753 | MCIDEV_ATTR(ce_noinfo_count,S_IRUGO,mci_ce_noinfo_show,NULL); | ||
| 754 | MCIDEV_ATTR(ue_count,S_IRUGO,mci_ue_count_show,NULL); | ||
| 755 | MCIDEV_ATTR(ce_count,S_IRUGO,mci_ce_count_show,NULL); | ||
| 756 | |||
| 757 | /* memory scrubber attribute file */ | ||
| 758 | MCIDEV_ATTR(sdram_scrub_rate,S_IRUGO|S_IWUSR,mci_sdram_scrub_rate_show,mci_sdram_scrub_rate_store); | ||
| 759 | |||
| 760 | static struct mcidev_attribute *mci_attr[] = { | ||
| 761 | &mci_attr_reset_counters, | ||
| 762 | &mci_attr_mc_name, | ||
| 763 | &mci_attr_size_mb, | ||
| 764 | &mci_attr_seconds_since_reset, | ||
| 765 | &mci_attr_ue_noinfo_count, | ||
| 766 | &mci_attr_ce_noinfo_count, | ||
| 767 | &mci_attr_ue_count, | ||
| 768 | &mci_attr_ce_count, | ||
| 769 | &mci_attr_sdram_scrub_rate, | ||
| 770 | NULL | ||
| 771 | }; | ||
| 772 | |||
| 773 | /* | ||
| 774 | * Release of a MC controlling instance | ||
| 775 | */ | ||
| 776 | static void edac_mci_instance_release(struct kobject *kobj) | ||
| 777 | { | ||
| 778 | struct mem_ctl_info *mci; | ||
| 779 | |||
| 780 | mci = to_mci(kobj); | ||
| 781 | debugf0("%s() idx=%d\n", __func__, mci->mc_idx); | ||
| 782 | complete(&mci->kobj_complete); | ||
| 783 | } | ||
| 784 | |||
| 785 | static struct kobj_type ktype_mci = { | ||
| 786 | .release = edac_mci_instance_release, | ||
| 787 | .sysfs_ops = &mci_ops, | ||
| 788 | .default_attrs = (struct attribute **) mci_attr, | ||
| 789 | }; | ||
| 790 | |||
| 791 | |||
| 792 | #define EDAC_DEVICE_SYMLINK "device" | ||
| 793 | |||
| 794 | /* | ||
| 795 | * Create a new Memory Controller kobject instance, | ||
| 796 | * mc<id> under the 'mc' directory | ||
| 797 | * | ||
| 798 | * Return: | ||
| 799 | * 0 Success | ||
| 800 | * !0 Failure | ||
| 801 | */ | ||
| 802 | int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) | ||
| 803 | { | ||
| 804 | int i; | ||
| 805 | int err; | ||
| 806 | struct csrow_info *csrow; | ||
| 807 | struct kobject *edac_mci_kobj=&mci->edac_mci_kobj; | ||
| 808 | |||
| 809 | debugf0("%s() idx=%d\n", __func__, mci->mc_idx); | ||
| 810 | memset(edac_mci_kobj, 0, sizeof(*edac_mci_kobj)); | ||
| 811 | |||
| 812 | /* set the name of the mc<id> object */ | ||
| 813 | err = kobject_set_name(edac_mci_kobj,"mc%d",mci->mc_idx); | ||
| 814 | if (err) | ||
| 815 | return err; | ||
| 816 | |||
| 817 | /* link to our parent the '..../edac/mc' object */ | ||
| 818 | edac_mci_kobj->parent = &edac_memctrl_kobj; | ||
| 819 | edac_mci_kobj->ktype = &ktype_mci; | ||
| 820 | |||
| 821 | /* register the mc<id> kobject */ | ||
| 822 | err = kobject_register(edac_mci_kobj); | ||
| 823 | if (err) | ||
| 824 | return err; | ||
| 825 | |||
| 826 | /* create a symlink for the device */ | ||
| 827 | err = sysfs_create_link(edac_mci_kobj, &mci->dev->kobj, | ||
| 828 | EDAC_DEVICE_SYMLINK); | ||
| 829 | if (err) | ||
| 830 | goto fail0; | ||
| 831 | |||
| 832 | /* Make directories for each CSROW object | ||
| 833 | * under the mc<id> kobject | ||
| 834 | */ | ||
| 835 | for (i = 0; i < mci->nr_csrows; i++) { | ||
| 836 | csrow = &mci->csrows[i]; | ||
| 837 | |||
| 838 | /* Only expose populated CSROWs */ | ||
| 839 | if (csrow->nr_pages > 0) { | ||
| 840 | err = edac_create_csrow_object(edac_mci_kobj,csrow,i); | ||
| 841 | if (err) | ||
| 842 | goto fail1; | ||
| 843 | } | ||
| 844 | } | ||
| 845 | |||
| 846 | return 0; | ||
| 847 | |||
| 848 | /* CSROW error: backout what has already been registered, */ | ||
| 849 | fail1: | ||
| 850 | for ( i--; i >= 0; i--) { | ||
| 851 | if (csrow->nr_pages > 0) { | ||
| 852 | init_completion(&csrow->kobj_complete); | ||
| 853 | kobject_unregister(&mci->csrows[i].kobj); | ||
| 854 | wait_for_completion(&csrow->kobj_complete); | ||
| 855 | } | ||
| 856 | } | ||
| 857 | |||
| 858 | fail0: | ||
| 859 | init_completion(&mci->kobj_complete); | ||
| 860 | kobject_unregister(edac_mci_kobj); | ||
| 861 | wait_for_completion(&mci->kobj_complete); | ||
| 862 | return err; | ||
| 863 | } | ||
| 864 | |||
| 865 | /* | ||
| 866 | * remove a Memory Controller instance | ||
| 867 | */ | ||
| 868 | void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) | ||
| 869 | { | ||
| 870 | int i; | ||
| 871 | |||
| 872 | debugf0("%s()\n", __func__); | ||
| 873 | |||
| 874 | /* remove all csrow kobjects */ | ||
| 875 | for (i = 0; i < mci->nr_csrows; i++) { | ||
| 876 | if (mci->csrows[i].nr_pages > 0) { | ||
| 877 | init_completion(&mci->csrows[i].kobj_complete); | ||
| 878 | kobject_unregister(&mci->csrows[i].kobj); | ||
| 879 | wait_for_completion(&mci->csrows[i].kobj_complete); | ||
| 880 | } | ||
| 881 | } | ||
| 882 | |||
| 883 | sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK); | ||
| 884 | init_completion(&mci->kobj_complete); | ||
| 885 | kobject_unregister(&mci->edac_mci_kobj); | ||
| 886 | wait_for_completion(&mci->kobj_complete); | ||
| 887 | } | ||
| 888 | |||
| 889 | |||
diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c new file mode 100644 index 000000000000..8db0471a9476 --- /dev/null +++ b/drivers/edac/edac_module.c | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | |||
| 2 | #include <linux/freezer.h> | ||
| 3 | #include <linux/kthread.h> | ||
| 4 | |||
| 5 | #include "edac_mc.h" | ||
| 6 | #include "edac_module.h" | ||
| 7 | |||
| 8 | #define EDAC_MC_VERSION "Ver: 2.0.3" __DATE__ | ||
| 9 | |||
| 10 | #ifdef CONFIG_EDAC_DEBUG | ||
| 11 | /* Values of 0 to 4 will generate output */ | ||
| 12 | int edac_debug_level = 1; | ||
| 13 | EXPORT_SYMBOL_GPL(edac_debug_level); | ||
| 14 | #endif | ||
| 15 | |||
| 16 | static struct task_struct *edac_thread; | ||
| 17 | |||
| 18 | /* | ||
| 19 | * Check MC status every edac_get_poll_msec(). | ||
| 20 | * Check PCI status every edac_get_poll_msec() as well. | ||
| 21 | * | ||
| 22 | * This where the work gets done for edac. | ||
| 23 | * | ||
| 24 | * SMP safe, doesn't use NMI, and auto-rate-limits. | ||
| 25 | */ | ||
| 26 | static void do_edac_check(void) | ||
| 27 | { | ||
| 28 | debugf3("%s()\n", __func__); | ||
| 29 | |||
| 30 | /* perform the poll activities */ | ||
| 31 | edac_check_mc_devices(); | ||
| 32 | edac_pci_do_parity_check(); | ||
| 33 | } | ||
| 34 | |||
| 35 | /* | ||
| 36 | * Action thread for EDAC to perform the POLL operations | ||
| 37 | */ | ||
| 38 | static int edac_kernel_thread(void *arg) | ||
| 39 | { | ||
| 40 | int msec; | ||
| 41 | |||
| 42 | while (!kthread_should_stop()) { | ||
| 43 | |||
| 44 | do_edac_check(); | ||
| 45 | |||
| 46 | /* goto sleep for the interval */ | ||
| 47 | msec = (HZ * edac_get_poll_msec()) / 1000; | ||
| 48 | schedule_timeout_interruptible(msec); | ||
| 49 | try_to_freeze(); | ||
| 50 | } | ||
| 51 | |||
| 52 | return 0; | ||
| 53 | } | ||
| 54 | |||
| 55 | /* | ||
| 56 | * edac_init | ||
| 57 | * module initialization entry point | ||
| 58 | */ | ||
| 59 | static int __init edac_init(void) | ||
| 60 | { | ||
| 61 | edac_printk(KERN_INFO, EDAC_MC, EDAC_MC_VERSION "\n"); | ||
| 62 | |||
| 63 | /* | ||
| 64 | * Harvest and clear any boot/initialization PCI parity errors | ||
| 65 | * | ||
| 66 | * FIXME: This only clears errors logged by devices present at time of | ||
| 67 | * module initialization. We should also do an initial clear | ||
| 68 | * of each newly hotplugged device. | ||
| 69 | */ | ||
| 70 | edac_pci_clear_parity_errors(); | ||
| 71 | |||
| 72 | /* Create the MC sysfs entries */ | ||
| 73 | if (edac_sysfs_memctrl_setup()) { | ||
| 74 | edac_printk(KERN_ERR, EDAC_MC, | ||
| 75 | "Error initializing sysfs code\n"); | ||
| 76 | return -ENODEV; | ||
| 77 | } | ||
| 78 | |||
| 79 | /* Create the PCI parity sysfs entries */ | ||
| 80 | if (edac_sysfs_pci_setup()) { | ||
| 81 | edac_sysfs_memctrl_teardown(); | ||
| 82 | edac_printk(KERN_ERR, EDAC_MC, | ||
| 83 | "PCI: Error initializing sysfs code\n"); | ||
| 84 | return -ENODEV; | ||
| 85 | } | ||
| 86 | |||
| 87 | /* create our kernel thread */ | ||
| 88 | edac_thread = kthread_run(edac_kernel_thread, NULL, "kedac"); | ||
| 89 | |||
| 90 | if (IS_ERR(edac_thread)) { | ||
| 91 | /* remove the sysfs entries */ | ||
| 92 | edac_sysfs_memctrl_teardown(); | ||
| 93 | edac_sysfs_pci_teardown(); | ||
| 94 | return PTR_ERR(edac_thread); | ||
| 95 | } | ||
| 96 | |||
| 97 | return 0; | ||
| 98 | } | ||
| 99 | |||
| 100 | /* | ||
| 101 | * edac_exit() | ||
| 102 | * module exit/termination function | ||
| 103 | */ | ||
| 104 | static void __exit edac_exit(void) | ||
| 105 | { | ||
| 106 | debugf0("%s()\n", __func__); | ||
| 107 | kthread_stop(edac_thread); | ||
| 108 | |||
| 109 | /* tear down the sysfs device */ | ||
| 110 | edac_sysfs_memctrl_teardown(); | ||
| 111 | edac_sysfs_pci_teardown(); | ||
| 112 | } | ||
| 113 | |||
| 114 | /* | ||
| 115 | * Inform the kernel of our entry and exit points | ||
| 116 | */ | ||
| 117 | module_init(edac_init); | ||
| 118 | module_exit(edac_exit); | ||
| 119 | |||
| 120 | MODULE_LICENSE("GPL"); | ||
| 121 | MODULE_AUTHOR("Doug Thompson www.softwarebitmaker.com, et al"); | ||
| 122 | MODULE_DESCRIPTION("Core library routines for EDAC reporting"); | ||
| 123 | |||
| 124 | /* refer to *_sysfs.c files for parameters that are exported via sysfs */ | ||
| 125 | |||
| 126 | #ifdef CONFIG_EDAC_DEBUG | ||
| 127 | module_param(edac_debug_level, int, 0644); | ||
| 128 | MODULE_PARM_DESC(edac_debug_level, "Debug level"); | ||
| 129 | #endif | ||
| 130 | |||
diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h new file mode 100644 index 000000000000..69c77f85bcd4 --- /dev/null +++ b/drivers/edac/edac_module.h | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | |||
| 2 | /* | ||
| 3 | * edac_module.h | ||
| 4 | * | ||
| 5 | * For defining functions/data for within the EDAC_CORE module only | ||
| 6 | * | ||
| 7 | * written by doug thompson <norsk5@xmission.h> | ||
| 8 | */ | ||
| 9 | |||
| 10 | #ifndef __EDAC_MODULE_H__ | ||
| 11 | #define __EDAC_MODULE_H__ | ||
| 12 | |||
| 13 | #include <linux/sysdev.h> | ||
| 14 | |||
| 15 | #include "edac_core.h" | ||
| 16 | |||
| 17 | /* | ||
| 18 | * INTERNAL EDAC MODULE: | ||
| 19 | * EDAC memory controller sysfs create/remove functions | ||
| 20 | * and setup/teardown functions | ||
| 21 | */ | ||
| 22 | extern int edac_create_sysfs_mci_device(struct mem_ctl_info *mci); | ||
| 23 | extern void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci); | ||
| 24 | extern int edac_sysfs_memctrl_setup(void); | ||
| 25 | extern void edac_sysfs_memctrl_teardown(void); | ||
| 26 | extern void edac_check_mc_devices(void); | ||
| 27 | extern int edac_get_log_ue(void); | ||
| 28 | extern int edac_get_log_ce(void); | ||
| 29 | extern int edac_get_panic_on_ue(void); | ||
| 30 | extern int edac_get_poll_msec(void); | ||
| 31 | |||
| 32 | extern int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev); | ||
| 33 | extern void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev); | ||
| 34 | extern struct sysdev_class *edac_get_edac_class(void); | ||
| 35 | |||
| 36 | |||
| 37 | /* | ||
| 38 | * EDAC PCI functions | ||
| 39 | */ | ||
| 40 | #ifdef CONFIG_PCI | ||
| 41 | extern void edac_pci_do_parity_check(void); | ||
| 42 | extern void edac_pci_clear_parity_errors(void); | ||
| 43 | extern int edac_sysfs_pci_setup(void); | ||
| 44 | extern void edac_sysfs_pci_teardown(void); | ||
| 45 | #else /* CONFIG_PCI */ | ||
| 46 | /* pre-process these away */ | ||
| 47 | #define edac_pci_do_parity_check() | ||
| 48 | #define edac_pci_clear_parity_errors() | ||
| 49 | #define edac_sysfs_pci_setup() (0) | ||
| 50 | #define edac_sysfs_pci_teardown() | ||
| 51 | #endif /* CONFIG_PCI */ | ||
| 52 | |||
| 53 | |||
| 54 | #endif /* __EDAC_MODULE_H__ */ | ||
| 55 | |||
diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c new file mode 100644 index 000000000000..db23fec522e2 --- /dev/null +++ b/drivers/edac/edac_pci_sysfs.c | |||
| @@ -0,0 +1,361 @@ | |||
| 1 | /* edac_mc kernel module | ||
| 2 | * (C) 2005, 2006 Linux Networx (http://lnxi.com) | ||
| 3 | * This file may be distributed under the terms of the | ||
| 4 | * GNU General Public License. | ||
| 5 | * | ||
| 6 | * Written Doug Thompson <norsk5@xmission.com> | ||
| 7 | * | ||
| 8 | */ | ||
| 9 | #include <linux/module.h> | ||
| 10 | #include <linux/sysdev.h> | ||
| 11 | #include <linux/ctype.h> | ||
| 12 | |||
| 13 | #include "edac_mc.h" | ||
| 14 | #include "edac_module.h" | ||
| 15 | |||
| 16 | |||
| 17 | #ifdef CONFIG_PCI | ||
| 18 | static int check_pci_parity = 0; /* default YES check PCI parity */ | ||
| 19 | static int panic_on_pci_parity; /* default no panic on PCI Parity */ | ||
| 20 | static atomic_t pci_parity_count = ATOMIC_INIT(0); | ||
| 21 | |||
| 22 | static struct kobject edac_pci_kobj; /* /sys/devices/system/edac/pci */ | ||
| 23 | static struct completion edac_pci_kobj_complete; | ||
| 24 | |||
| 25 | |||
| 26 | static ssize_t edac_pci_int_show(void *ptr, char *buffer) | ||
| 27 | { | ||
| 28 | int *value = ptr; | ||
| 29 | return sprintf(buffer,"%d\n",*value); | ||
| 30 | } | ||
| 31 | |||
| 32 | static ssize_t edac_pci_int_store(void *ptr, const char *buffer, size_t count) | ||
| 33 | { | ||
| 34 | int *value = ptr; | ||
| 35 | |||
| 36 | if (isdigit(*buffer)) | ||
| 37 | *value = simple_strtoul(buffer,NULL,0); | ||
| 38 | |||
| 39 | return count; | ||
| 40 | } | ||
| 41 | |||
| 42 | struct edac_pci_dev_attribute { | ||
| 43 | struct attribute attr; | ||
| 44 | void *value; | ||
| 45 | ssize_t (*show)(void *,char *); | ||
| 46 | ssize_t (*store)(void *, const char *,size_t); | ||
| 47 | }; | ||
| 48 | |||
| 49 | /* Set of show/store abstract level functions for PCI Parity object */ | ||
| 50 | static ssize_t edac_pci_dev_show(struct kobject *kobj, struct attribute *attr, | ||
| 51 | char *buffer) | ||
| 52 | { | ||
| 53 | struct edac_pci_dev_attribute *edac_pci_dev; | ||
| 54 | edac_pci_dev= (struct edac_pci_dev_attribute*)attr; | ||
| 55 | |||
| 56 | if (edac_pci_dev->show) | ||
| 57 | return edac_pci_dev->show(edac_pci_dev->value, buffer); | ||
| 58 | return -EIO; | ||
| 59 | } | ||
| 60 | |||
| 61 | static ssize_t edac_pci_dev_store(struct kobject *kobj, | ||
| 62 | struct attribute *attr, const char *buffer, size_t count) | ||
| 63 | { | ||
| 64 | struct edac_pci_dev_attribute *edac_pci_dev; | ||
| 65 | edac_pci_dev= (struct edac_pci_dev_attribute*)attr; | ||
| 66 | |||
| 67 | if (edac_pci_dev->show) | ||
| 68 | return edac_pci_dev->store(edac_pci_dev->value, buffer, count); | ||
| 69 | return -EIO; | ||
| 70 | } | ||
| 71 | |||
| 72 | static struct sysfs_ops edac_pci_sysfs_ops = { | ||
| 73 | .show = edac_pci_dev_show, | ||
| 74 | .store = edac_pci_dev_store | ||
| 75 | }; | ||
| 76 | |||
| 77 | #define EDAC_PCI_ATTR(_name,_mode,_show,_store) \ | ||
| 78 | static struct edac_pci_dev_attribute edac_pci_attr_##_name = { \ | ||
| 79 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
| 80 | .value = &_name, \ | ||
| 81 | .show = _show, \ | ||
| 82 | .store = _store, \ | ||
| 83 | }; | ||
| 84 | |||
| 85 | #define EDAC_PCI_STRING_ATTR(_name,_data,_mode,_show,_store) \ | ||
| 86 | static struct edac_pci_dev_attribute edac_pci_attr_##_name = { \ | ||
| 87 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
| 88 | .value = _data, \ | ||
| 89 | .show = _show, \ | ||
| 90 | .store = _store, \ | ||
| 91 | }; | ||
| 92 | |||
| 93 | /* PCI Parity control files */ | ||
| 94 | EDAC_PCI_ATTR(check_pci_parity, S_IRUGO|S_IWUSR, edac_pci_int_show, | ||
| 95 | edac_pci_int_store); | ||
| 96 | EDAC_PCI_ATTR(panic_on_pci_parity, S_IRUGO|S_IWUSR, edac_pci_int_show, | ||
| 97 | edac_pci_int_store); | ||
| 98 | EDAC_PCI_ATTR(pci_parity_count, S_IRUGO, edac_pci_int_show, NULL); | ||
| 99 | |||
| 100 | /* Base Attributes of the memory ECC object */ | ||
| 101 | static struct edac_pci_dev_attribute *edac_pci_attr[] = { | ||
| 102 | &edac_pci_attr_check_pci_parity, | ||
| 103 | &edac_pci_attr_panic_on_pci_parity, | ||
| 104 | &edac_pci_attr_pci_parity_count, | ||
| 105 | NULL, | ||
| 106 | }; | ||
| 107 | |||
| 108 | /* No memory to release */ | ||
| 109 | static void edac_pci_release(struct kobject *kobj) | ||
| 110 | { | ||
| 111 | debugf1("%s()\n", __func__); | ||
| 112 | complete(&edac_pci_kobj_complete); | ||
| 113 | } | ||
| 114 | |||
| 115 | static struct kobj_type ktype_edac_pci = { | ||
| 116 | .release = edac_pci_release, | ||
| 117 | .sysfs_ops = &edac_pci_sysfs_ops, | ||
| 118 | .default_attrs = (struct attribute **) edac_pci_attr, | ||
| 119 | }; | ||
| 120 | |||
| 121 | /** | ||
| 122 | * edac_sysfs_pci_setup() | ||
| 123 | * | ||
| 124 | * setup the sysfs for EDAC PCI attributes | ||
| 125 | * assumes edac_class has already been initialized | ||
| 126 | */ | ||
| 127 | int edac_sysfs_pci_setup(void) | ||
| 128 | { | ||
| 129 | int err; | ||
| 130 | struct sysdev_class *edac_class; | ||
| 131 | |||
| 132 | debugf1("%s()\n", __func__); | ||
| 133 | |||
| 134 | edac_class = edac_get_edac_class(); | ||
| 135 | |||
| 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; | ||
| 139 | err = kobject_set_name(&edac_pci_kobj, "pci"); | ||
| 140 | |||
| 141 | if (!err) { | ||
| 142 | /* Instanstiate the pci object */ | ||
| 143 | /* FIXME: maybe new sysdev_create_subdir() */ | ||
| 144 | err = kobject_register(&edac_pci_kobj); | ||
| 145 | |||
| 146 | if (err) | ||
| 147 | debugf1("Failed to register '.../edac/pci'\n"); | ||
| 148 | else | ||
| 149 | debugf1("Registered '.../edac/pci' kobject\n"); | ||
| 150 | } | ||
| 151 | |||
| 152 | return err; | ||
| 153 | } | ||
| 154 | |||
| 155 | /* | ||
| 156 | * edac_sysfs_pci_teardown | ||
| 157 | * | ||
| 158 | * perform the sysfs teardown for the PCI attributes | ||
| 159 | */ | ||
| 160 | void edac_sysfs_pci_teardown(void) | ||
| 161 | { | ||
| 162 | debugf0("%s()\n", __func__); | ||
| 163 | init_completion(&edac_pci_kobj_complete); | ||
| 164 | kobject_unregister(&edac_pci_kobj); | ||
| 165 | wait_for_completion(&edac_pci_kobj_complete); | ||
| 166 | } | ||
| 167 | |||
| 168 | |||
| 169 | static u16 get_pci_parity_status(struct pci_dev *dev, int secondary) | ||
| 170 | { | ||
| 171 | int where; | ||
| 172 | u16 status; | ||
| 173 | |||
| 174 | where = secondary ? PCI_SEC_STATUS : PCI_STATUS; | ||
| 175 | pci_read_config_word(dev, where, &status); | ||
| 176 | |||
| 177 | /* If we get back 0xFFFF then we must suspect that the card has been | ||
| 178 | * pulled but the Linux PCI layer has not yet finished cleaning up. | ||
| 179 | * We don't want to report on such devices | ||
| 180 | */ | ||
| 181 | |||
| 182 | if (status == 0xFFFF) { | ||
| 183 | u32 sanity; | ||
| 184 | |||
| 185 | pci_read_config_dword(dev, 0, &sanity); | ||
| 186 | |||
| 187 | if (sanity == 0xFFFFFFFF) | ||
| 188 | return 0; | ||
| 189 | } | ||
| 190 | |||
| 191 | status &= PCI_STATUS_DETECTED_PARITY | PCI_STATUS_SIG_SYSTEM_ERROR | | ||
| 192 | PCI_STATUS_PARITY; | ||
| 193 | |||
| 194 | if (status) | ||
| 195 | /* reset only the bits we are interested in */ | ||
| 196 | pci_write_config_word(dev, where, status); | ||
| 197 | |||
| 198 | return status; | ||
| 199 | } | ||
| 200 | |||
| 201 | typedef void (*pci_parity_check_fn_t) (struct pci_dev *dev); | ||
| 202 | |||
| 203 | /* Clear any PCI parity errors logged by this device. */ | ||
| 204 | static void edac_pci_dev_parity_clear(struct pci_dev *dev) | ||
| 205 | { | ||
| 206 | u8 header_type; | ||
| 207 | |||
| 208 | get_pci_parity_status(dev, 0); | ||
| 209 | |||
| 210 | /* read the device TYPE, looking for bridges */ | ||
| 211 | pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type); | ||
| 212 | |||
| 213 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) | ||
| 214 | get_pci_parity_status(dev, 1); | ||
| 215 | } | ||
| 216 | |||
| 217 | /* | ||
| 218 | * PCI Parity polling | ||
| 219 | * | ||
| 220 | */ | ||
| 221 | static void edac_pci_dev_parity_test(struct pci_dev *dev) | ||
| 222 | { | ||
| 223 | u16 status; | ||
| 224 | u8 header_type; | ||
| 225 | |||
| 226 | /* read the STATUS register on this device | ||
| 227 | */ | ||
| 228 | status = get_pci_parity_status(dev, 0); | ||
| 229 | |||
| 230 | debugf2("PCI STATUS= 0x%04x %s\n", status, dev->dev.bus_id ); | ||
| 231 | |||
| 232 | /* check the status reg for errors */ | ||
| 233 | if (status) { | ||
| 234 | if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) | ||
| 235 | edac_printk(KERN_CRIT, EDAC_PCI, | ||
| 236 | "Signaled System Error on %s\n", | ||
| 237 | pci_name(dev)); | ||
| 238 | |||
| 239 | if (status & (PCI_STATUS_PARITY)) { | ||
| 240 | edac_printk(KERN_CRIT, EDAC_PCI, | ||
| 241 | "Master Data Parity Error on %s\n", | ||
| 242 | pci_name(dev)); | ||
| 243 | |||
| 244 | atomic_inc(&pci_parity_count); | ||
| 245 | } | ||
| 246 | |||
| 247 | if (status & (PCI_STATUS_DETECTED_PARITY)) { | ||
| 248 | edac_printk(KERN_CRIT, EDAC_PCI, | ||
| 249 | "Detected Parity Error on %s\n", | ||
| 250 | pci_name(dev)); | ||
| 251 | |||
| 252 | atomic_inc(&pci_parity_count); | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | /* read the device TYPE, looking for bridges */ | ||
| 257 | pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type); | ||
| 258 | |||
| 259 | debugf2("PCI HEADER TYPE= 0x%02x %s\n", header_type, dev->dev.bus_id ); | ||
| 260 | |||
| 261 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { | ||
| 262 | /* On bridges, need to examine secondary status register */ | ||
| 263 | status = get_pci_parity_status(dev, 1); | ||
| 264 | |||
| 265 | debugf2("PCI SEC_STATUS= 0x%04x %s\n", | ||
| 266 | status, dev->dev.bus_id ); | ||
| 267 | |||
| 268 | /* check the secondary status reg for errors */ | ||
| 269 | if (status) { | ||
| 270 | if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) | ||
| 271 | edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " | ||
| 272 | "Signaled System Error on %s\n", | ||
| 273 | pci_name(dev)); | ||
| 274 | |||
| 275 | if (status & (PCI_STATUS_PARITY)) { | ||
| 276 | edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " | ||
| 277 | "Master Data Parity Error on " | ||
| 278 | "%s\n", pci_name(dev)); | ||
| 279 | |||
| 280 | atomic_inc(&pci_parity_count); | ||
| 281 | } | ||
| 282 | |||
| 283 | if (status & (PCI_STATUS_DETECTED_PARITY)) { | ||
| 284 | edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " | ||
| 285 | "Detected Parity Error on %s\n", | ||
| 286 | pci_name(dev)); | ||
| 287 | |||
| 288 | atomic_inc(&pci_parity_count); | ||
| 289 | } | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | /* | ||
| 295 | * pci_dev parity list iterator | ||
| 296 | * Scan the PCI device list for one iteration, looking for SERRORs | ||
| 297 | * Master Parity ERRORS or Parity ERRORs on primary or secondary devices | ||
| 298 | */ | ||
| 299 | static inline void edac_pci_dev_parity_iterator(pci_parity_check_fn_t fn) | ||
| 300 | { | ||
| 301 | struct pci_dev *dev = NULL; | ||
| 302 | |||
| 303 | /* request for kernel access to the next PCI device, if any, | ||
| 304 | * and while we are looking at it have its reference count | ||
| 305 | * bumped until we are done with it | ||
| 306 | */ | ||
| 307 | while((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { | ||
| 308 | fn(dev); | ||
| 309 | } | ||
| 310 | } | ||
| 311 | |||
| 312 | /* | ||
| 313 | * edac_pci_do_parity_check | ||
| 314 | * | ||
| 315 | * performs the actual PCI parity check operation | ||
| 316 | */ | ||
| 317 | void edac_pci_do_parity_check(void) | ||
| 318 | { | ||
| 319 | unsigned long flags; | ||
| 320 | int before_count; | ||
| 321 | |||
| 322 | debugf3("%s()\n", __func__); | ||
| 323 | |||
| 324 | if (!check_pci_parity) | ||
| 325 | return; | ||
| 326 | |||
| 327 | before_count = atomic_read(&pci_parity_count); | ||
| 328 | |||
| 329 | /* scan all PCI devices looking for a Parity Error on devices and | ||
| 330 | * bridges | ||
| 331 | */ | ||
| 332 | local_irq_save(flags); | ||
| 333 | edac_pci_dev_parity_iterator(edac_pci_dev_parity_test); | ||
| 334 | local_irq_restore(flags); | ||
| 335 | |||
| 336 | /* Only if operator has selected panic on PCI Error */ | ||
| 337 | if (panic_on_pci_parity) { | ||
| 338 | /* If the count is different 'after' from 'before' */ | ||
| 339 | if (before_count != atomic_read(&pci_parity_count)) | ||
| 340 | panic("EDAC: PCI Parity Error"); | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | void edac_pci_clear_parity_errors(void) | ||
| 345 | { | ||
| 346 | /* Clear any PCI bus parity errors that devices initially have logged | ||
| 347 | * in their registers. | ||
| 348 | */ | ||
| 349 | edac_pci_dev_parity_iterator(edac_pci_dev_parity_clear); | ||
| 350 | } | ||
| 351 | |||
| 352 | |||
| 353 | /* | ||
| 354 | * Define the PCI parameter to the module | ||
| 355 | */ | ||
| 356 | module_param(check_pci_parity, int, 0644); | ||
| 357 | MODULE_PARM_DESC(check_pci_parity, "Check for PCI bus parity errors: 0=off 1=on"); | ||
| 358 | 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"); | ||
| 360 | |||
| 361 | #endif /* CONFIG_PCI */ | ||
