From a0c36a1f0fbab42590dab3c13c10fa7d20e6c2cd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:41:15 -0300 Subject: [PATCH 01/83] i7core_edac: Add an EDAC memory controller driver for Nehalem chipsets This driver is meant to support i7 core/i7core extreme desktop processors and Xeon 35xx/55xx series with integrated memory controller. It is likely that it can be expanded in the future to work with other processor series based at the same Memory Controller design. For now, it has just a few MCH status reads. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/Kconfig | 7 + drivers/edac/Makefile | 1 + drivers/edac/i7core_edac.c | 462 +++++++++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 16 ++ 4 files changed, 486 insertions(+) create mode 100644 drivers/edac/i7core_edac.c diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 55c9c59b3f71..391ddbfb2a34 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -166,6 +166,13 @@ config EDAC_I5400 Support for error detection and correction the Intel i5400 MCH chipset (Seaburg). +config EDAC_I7CORE + tristate "Intel i7 Core (Nehalem) processors" + depends on EDAC_MM_EDAC && PCI && X86 + help + Support for error detection and correction the Intel + i7 Core (Nehalem) Integrated Memory Controller + config EDAC_I82860 tristate "Intel 82860" depends on EDAC_MM_EDAC && PCI && X86_32 diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index bc5dc232a0fb..b9996195b233 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_EDAC_CPC925) += cpc925_edac.o obj-$(CONFIG_EDAC_I5000) += i5000_edac.o obj-$(CONFIG_EDAC_I5100) += i5100_edac.o obj-$(CONFIG_EDAC_I5400) += i5400_edac.o +obj-$(CONFIG_EDAC_I7CORE) += i7core_edac.o obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o obj-$(CONFIG_EDAC_E752X) += e752x_edac.o obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c new file mode 100644 index 000000000000..7ecf15e66a3f --- /dev/null +++ b/drivers/edac/i7core_edac.c @@ -0,0 +1,462 @@ +/* Intel 7 core Memory Controller kernel module (Nehalem) + * + * This file may be distributed under the terms of the + * GNU General Public License version 2 only. + * + * Copyright (c) 2009 by: + * Mauro Carvalho Chehab + * + * Red Hat Inc. http://www.redhat.com + * + * Forked and adapted from the i5400_edac driver + * + * Based on the following public Intel datasheets: + * Intel Core i7 Processor Extreme Edition and Intel Core i7 Processor + * Datasheet, Volume 2: + * http://download.intel.com/design/processor/datashts/320835.pdf + * Intel Xeon Processor 5500 Series Datasheet Volume 2 + * http://www.intel.com/Assets/PDF/datasheet/321322.pdf + * also available at: + * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "edac_core.h" + + +/* + * Alter this version for the module when modifications are made + */ +#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__ +#define EDAC_MOD_STR "i7core_edac" + +/* HACK: temporary, just to enable all logs, for now */ +#undef debugf0 +#define debugf0(fmt, arg...) edac_printk(KERN_INFO, "i7core", fmt, ##arg) + +/* + * Debug macros + */ +#define i7core_printk(level, fmt, arg...) \ + edac_printk(level, "i7core", fmt, ##arg) + +#define i7core_mc_printk(mci, level, fmt, arg...) \ + edac_mc_chipset_printk(mci, level, "i7core", fmt, ##arg) + +/* + * i7core Memory Controller Registers + */ + + /* OFFSETS for Device 3 Function 0 */ + +#define MC_CONTROL 0x48 +#define MC_STATUS 0x4c +#define MC_MAX_DOD 0x64 + + /* OFFSETS for Devices 4,5 and 6 Function 0 */ + +#define MC_CHANNEL_ADDR_MATCH 0xf0 + +#define MC_MASK_DIMM (1 << 41) +#define MC_MASK_RANK (1 << 40) +#define MC_MASK_BANK (1 << 39) +#define MC_MASK_PAGE (1 << 38) +#define MC_MASK_COL (1 << 37) + +/* + * i7core structs + */ + +#define NUM_CHANS 3 +#define NUM_FUNCS 1 + +struct i7core_info { + u32 mc_control; + u32 mc_status; + u32 max_dod; +}; + +struct i7core_pvt { + struct pci_dev *pci_mcr; /* Dev 3:0 */ + struct pci_dev *pci_ch[NUM_CHANS][NUM_FUNCS]; + struct i7core_info info; +}; + +/* Device name and register DID (Device ID) */ +struct i7core_dev_info { + const char *ctl_name; /* name for this device */ + u16 fsb_mapping_errors; /* DID for the branchmap,control */ +}; + +static int chan_pci_ids[NUM_CHANS] = { + PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL, /* Dev 4 */ + PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL, /* Dev 5 */ + PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL, /* Dev 6 */ +}; + +/* Table of devices attributes supported by this driver */ +static const struct i7core_dev_info i7core_devs[] = { + { + .ctl_name = "i7 Core", + .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR, + }, +}; + +static struct edac_pci_ctl_info *i7core_pci; + +/**************************************************************************** + Anciliary status routines + ****************************************************************************/ + + /* MC_CONTROL bits */ +#define CH2_ACTIVE(pvt) ((pvt)->info.mc_control & 1 << 10) +#define CH1_ACTIVE(pvt) ((pvt)->info.mc_control & 1 << 9) +#define CH0_ACTIVE(pvt) ((pvt)->info.mc_control & 1 << 8) +#define ECCx8(pvt) ((pvt)->info.mc_control & 1 << 1) + + /* MC_STATUS bits */ +#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & 1 << 3) +#define CH2_DISABLED(pvt) ((pvt)->info.mc_status & 1 << 2) +#define CH1_DISABLED(pvt) ((pvt)->info.mc_status & 1 << 1) +#define CH0_DISABLED(pvt) ((pvt)->info.mc_status & 1 << 0) + + /* MC_MAX_DOD read functions */ +static inline int maxnumdimms(struct i7core_pvt *pvt) +{ + return (pvt->info.max_dod & 0x3) + 1; +} + +static inline int maxnumrank(struct i7core_pvt *pvt) +{ + static int ranks[4] = { 1, 2, 4, -EINVAL }; + + return ranks[(pvt->info.max_dod >> 2) & 0x3]; +} + +static inline int maxnumbank(struct i7core_pvt *pvt) +{ + static int banks[4] = { 4, 8, 16, -EINVAL }; + + return banks[(pvt->info.max_dod >> 4) & 0x3]; +} + +static inline int maxnumrow(struct i7core_pvt *pvt) +{ + static int rows[8] = { + 1 << 12, 1 << 13, 1 << 14, 1 << 15, + 1 << 16, -EINVAL, -EINVAL, -EINVAL, + }; + + return rows[((pvt->info.max_dod >> 6) & 0x7)]; +} + +static inline int maxnumcol(struct i7core_pvt *pvt) +{ + static int cols[8] = { + 1 << 10, 1 << 11, 1 << 12, -EINVAL, + }; + return cols[((pvt->info.max_dod >> 9) & 0x3) << 12]; +} + +/**************************************************************************** + Memory check routines + ****************************************************************************/ +static int get_dimm_config(struct mem_ctl_info *mci) +{ + struct i7core_pvt *pvt = mci->pvt_info; + + pci_read_config_dword(pvt->pci_mcr, MC_CONTROL, &pvt->info.mc_control); + pci_read_config_dword(pvt->pci_mcr, MC_STATUS, &pvt->info.mc_status); + pci_read_config_dword(pvt->pci_mcr, MC_MAX_DOD, &pvt->info.max_dod); + + debugf0("Channels active [%c][%c][%c] - enabled [%c][%c][%c]\n", + CH0_ACTIVE(pvt)?'0':'-', + CH1_ACTIVE(pvt)?'1':'-', + CH2_ACTIVE(pvt)?'2':'-', + CH0_DISABLED(pvt)?'-':'0', + CH1_DISABLED(pvt)?'-':'1', + CH2_DISABLED(pvt)?'-':'2'); + + if (ECC_ENABLED(pvt)) + debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt)?8:4); + else + debugf0("ECC disabled\n"); + + /* FIXME: need to handle the error codes */ + debugf0("DOD Maximum limits: DIMMS: %d, %d-ranked, %d-banked\n", + maxnumdimms(pvt), maxnumrank(pvt), maxnumbank(pvt)); + debugf0("DOD Maximum rows x colums = 0x%x x 0x%x\n", + maxnumrow(pvt), maxnumcol(pvt)); + + return 0; +} + +/**************************************************************************** + Device initialization routines: put/get, init/exit + ****************************************************************************/ + +/* + * i7core_put_devices 'put' all the devices that we have + * reserved via 'get' + */ +static void i7core_put_devices(struct mem_ctl_info *mci) +{ + struct i7core_pvt *pvt = mci->pvt_info; + int i, n; + + pci_dev_put(pvt->pci_mcr); + + /* Release all PCI device functions at MTR channel controllers */ + for (i = 0; i < NUM_CHANS; i++) + for (n = 0; n < NUM_FUNCS; n++) + pci_dev_put(pvt->pci_ch[i][n]); +} + +/* + * i7core_get_devices Find and perform 'get' operation on the MCH's + * device/functions we want to reference for this driver + * + * Need to 'get' device 16 func 1 and func 2 + */ +static int i7core_get_devices(struct mem_ctl_info *mci, int dev_idx) +{ + struct i7core_pvt *pvt; + struct pci_dev *pdev; + int i, n, func; + + pvt = mci->pvt_info; + memset(pvt, 0, sizeof(*pvt)); + + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR, + NULL); + if (!pdev) { + i7core_printk(KERN_ERR, + "Couldn't get PCI ID %04x:%04x function 0\n", + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR); + return -ENODEV; + } + pvt->pci_mcr=pdev; + + /* Get dimm basic config */ + get_dimm_config(mci); + + /* Retrieve all needed functions at MTR channel controllers */ + for (i = 0; i < NUM_CHANS; i++) { + pdev = NULL; + for (n = 0; n < NUM_FUNCS; n++) { + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, + chan_pci_ids[i], pdev); + if (!pdev) { + /* End of list, leave */ + i7core_printk(KERN_ERR, + "Device not found: PCI ID %04x:%04x " + "found only %d functions " + "(broken BIOS?)\n", + PCI_VENDOR_ID_INTEL, + chan_pci_ids[i], n); + i7core_put_devices(mci); + return -ENODEV; + } + func = PCI_FUNC(pdev->devfn); + pvt->pci_ch[i][func] = pdev; + } + } + i7core_printk(KERN_INFO, "Driver loaded.\n"); + + return 0; +} + +/* + * i7core_probe Probe for ONE instance of device to see if it is + * present. + * return: + * 0 for FOUND a device + * < 0 for error code + */ +static int __devinit i7core_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct mem_ctl_info *mci; + struct i7core_pvt *pvt; + int rc; + int num_channels; + int num_csrows; + int num_dimms_per_channel; + int dev_idx = id->driver_data; + + if (dev_idx >= ARRAY_SIZE(i7core_devs)) + return -EINVAL; + + /* wake up device */ + rc = pci_enable_device(pdev); + if (rc == -EIO) + return rc; + + debugf0("MC: " __FILE__ ": %s(), pdev bus %u dev=0x%x fn=0x%x\n", + __func__, + pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + + /* We only are looking for func 0 of the set */ + if (PCI_FUNC(pdev->devfn) != 0) + return -ENODEV; + + num_channels = NUM_CHANS; + + /* FIXME: FAKE data, since we currently don't now how to get this */ + num_dimms_per_channel = 4; + num_csrows = num_dimms_per_channel; + + /* allocate a new MC control structure */ + mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0); + if (mci == NULL) + return -ENOMEM; + + debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); + + mci->dev = &pdev->dev; /* record ptr to the generic device */ + dev_set_drvdata(mci->dev, mci); + + pvt = mci->pvt_info; +// pvt->system_address = pdev; /* Record this device in our private */ +// pvt->maxch = num_channels; +// pvt->maxdimmperch = num_dimms_per_channel; + + /* 'get' the pci devices we want to reserve for our use */ + if (i7core_get_devices(mci, dev_idx)) + goto fail0; + + mci->mc_idx = 0; + mci->mtype_cap = MEM_FLAG_FB_DDR2; /* FIXME: it uses DDR3 */ + mci->edac_ctl_cap = EDAC_FLAG_NONE; + mci->edac_cap = EDAC_FLAG_NONE; + mci->mod_name = "i7core_edac.c"; + mci->mod_ver = I7CORE_REVISION; + mci->ctl_name = i7core_devs[dev_idx].ctl_name; + mci->dev_name = pci_name(pdev); + mci->ctl_page_to_phys = NULL; + + /* add this new MC control structure to EDAC's list of MCs */ + if (edac_mc_add_mc(mci)) { + debugf0("MC: " __FILE__ + ": %s(): failed edac_mc_add_mc()\n", __func__); + /* FIXME: perhaps some code should go here that disables error + * reporting if we just enabled it + */ + goto fail1; + } + + /* allocating generic PCI control info */ + i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR); + if (!i7core_pci) { + printk(KERN_WARNING + "%s(): Unable to create PCI control\n", + __func__); + printk(KERN_WARNING + "%s(): PCI error report via EDAC not setup\n", + __func__); + } + + return 0; + +fail1: + i7core_put_devices(mci); + +fail0: + edac_mc_free(mci); + return -ENODEV; +} + +/* + * i7core_remove destructor for one instance of device + * + */ +static void __devexit i7core_remove(struct pci_dev *pdev) +{ + struct mem_ctl_info *mci; + + debugf0(__FILE__ ": %s()\n", __func__); + + if (i7core_pci) + edac_pci_release_generic_ctl(i7core_pci); + + mci = edac_mc_del_mc(&pdev->dev); + if (!mci) + return; + + /* retrieve references to resources, and free those resources */ + i7core_put_devices(mci); + + edac_mc_free(mci); +} + +/* + * pci_device_id table for which devices we are looking for + * + * The "E500P" device is the first device supported. + */ +static const struct pci_device_id i7core_pci_tbl[] __devinitdata = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR)}, + {0,} /* 0 terminated list. */ +}; + +MODULE_DEVICE_TABLE(pci, i7core_pci_tbl); + +/* + * i7core_driver pci_driver structure for this module + * + */ +static struct pci_driver i7core_driver = { + .name = "i7core_edac", + .probe = i7core_probe, + .remove = __devexit_p(i7core_remove), + .id_table = i7core_pci_tbl, +}; + +/* + * i7core_init Module entry function + * Try to initialize this module for its devices + */ +static int __init i7core_init(void) +{ + int pci_rc; + + debugf2("MC: " __FILE__ ": %s()\n", __func__); + + /* Ensure that the OPSTATE is set correctly for POLL or NMI */ + opstate_init(); + + pci_rc = pci_register_driver(&i7core_driver); + + return (pci_rc < 0) ? pci_rc : 0; +} + +/* + * i7core_exit() Module exit function + * Unregister the driver + */ +static void __exit i7core_exit(void) +{ + debugf2("MC: " __FILE__ ": %s()\n", __func__); + pci_unregister_driver(&i7core_driver); +} + +module_init(i7core_init); +module_exit(i7core_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); +MODULE_DESCRIPTION("MC Driver for Intel i7 Core memory controllers - " + I7CORE_REVISION); + +module_param(edac_op_state, int, 0444); +MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 9f688d243b86..c5dd0994bd7c 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2532,6 +2532,22 @@ #define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 #define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 #define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 +#define PCI_DEVICE_ID_INTEL_I7_MCR 0x2c18 +#define PCI_DEVICE_ID_INTEL_I7_MC_TAD 0x2c19 +#define PCI_DEVICE_ID_INTEL_I7_MC_RAS 0x2c1a +#define PCI_DEVICE_ID_INTEL_I7_MC_TEST 0x2c1c +#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL 0x2c20 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR 0x2c21 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK 0x2c22 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC 0x2c23 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL 0x2c28 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR 0x2c29 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK 0x2c2a +#define PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC 0x2c2b +#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL 0x2c30 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR 0x2c31 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK 0x2c32 +#define PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC 0x2c33 #define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 #define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 #define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a From 194a40feabb7cab38911a357c86e968e98024281 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:28 -0300 Subject: [PATCH 02/83] i7core_edac: Add error insertion code for Nehalem Implements set_inject_error() with the low-level code needed to inject memory errors at Nehalem, and adds some sysfs nodes to allow error injection The next patch will add an API for error injection. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 427 ++++++++++++++++++++++++++++++++++++- 1 file changed, 419 insertions(+), 8 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 7ecf15e66a3f..b590f8468693 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -20,7 +20,6 @@ * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf */ - #include #include #include @@ -64,12 +63,16 @@ /* OFFSETS for Devices 4,5 and 6 Function 0 */ #define MC_CHANNEL_ADDR_MATCH 0xf0 - -#define MC_MASK_DIMM (1 << 41) -#define MC_MASK_RANK (1 << 40) -#define MC_MASK_BANK (1 << 39) -#define MC_MASK_PAGE (1 << 38) -#define MC_MASK_COL (1 << 37) +#define MC_CHANNEL_ERROR_MASK 0xf8 +#define MC_CHANNEL_ERROR_INJECT 0xfc + #define INJECT_ADDR_PARITY 0x10 + #define INJECT_ECC 0x08 + #define MASK_CACHELINE 0x06 + #define MASK_FULL_CACHELINE 0x06 + #define MASK_MSB32_CACHELINE 0x04 + #define MASK_LSB32_CACHELINE 0x02 + #define NO_MASK_CACHELINE 0x00 + #define REPEAT_EN 0x01 /* * i7core structs @@ -84,10 +87,23 @@ struct i7core_info { u32 max_dod; }; + +struct i7core_inject { + int enable; + + u32 section; + u32 type; + u32 eccmask; + + /* Error address mask */ + int channel, dimm, rank, bank, page, col; +}; + struct i7core_pvt { struct pci_dev *pci_mcr; /* Dev 3:0 */ struct pci_dev *pci_ch[NUM_CHANS][NUM_FUNCS]; struct i7core_info info; + struct i7core_inject inject; }; /* Device name and register DID (Device ID) */ @@ -166,6 +182,7 @@ static inline int maxnumcol(struct i7core_pvt *pvt) return cols[((pvt->info.max_dod >> 9) & 0x3) << 12]; } + /**************************************************************************** Memory check routines ****************************************************************************/ @@ -199,6 +216,390 @@ static int get_dimm_config(struct mem_ctl_info *mci) return 0; } +/**************************************************************************** + Error insertion routines + ****************************************************************************/ + +/* The i7core has independent error injection features per channel. + However, to have a simpler code, we don't allow enabling error injection + on more than one channel. + Also, since a change at an inject parameter will be applied only at enable, + we're disabling error injection on all write calls to the sysfs nodes that + controls the error code injection. + */ +static void disable_inject(struct mem_ctl_info *mci) +{ + struct i7core_pvt *pvt = mci->pvt_info; + + pvt->inject.enable = 0; + + pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], + MC_CHANNEL_ERROR_MASK, 0); +} + +/* + * i7core inject inject.section + * + * accept and store error injection inject.section value + * bit 0 - refers to the lower 32-byte half cacheline + * bit 1 - refers to the upper 32-byte half cacheline + */ +static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci, + const char *data, size_t count) +{ + struct i7core_pvt *pvt = mci->pvt_info; + unsigned long value; + int rc; + + if (pvt->inject.enable) + disable_inject(mci); + + rc = strict_strtoul(data, 10, &value); + if ((rc < 0) || (value > 3)) + return 0; + + pvt->inject.section = (u32) value; + return count; +} + +static ssize_t i7core_inject_section_show(struct mem_ctl_info *mci, + char *data) +{ + struct i7core_pvt *pvt = mci->pvt_info; + return sprintf(data, "0x%08x\n", pvt->inject.section); +} + +/* + * i7core inject.type + * + * accept and store error injection inject.section value + * bit 0 - repeat enable - Enable error repetition + * bit 1 - inject ECC error + * bit 2 - inject parity error + */ +static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci, + const char *data, size_t count) +{ + struct i7core_pvt *pvt = mci->pvt_info; + unsigned long value; + int rc; + + if (pvt->inject.enable) + disable_inject(mci); + + rc = strict_strtoul(data, 10, &value); + if ((rc < 0) || (value > 7)) + return 0; + + pvt->inject.type = (u32) value; + return count; +} + +static ssize_t i7core_inject_type_show(struct mem_ctl_info *mci, + char *data) +{ + struct i7core_pvt *pvt = mci->pvt_info; + return sprintf(data, "0x%08x\n", pvt->inject.type); +} + +/* + * i7core_inject_inject.eccmask_store + * + * The type of error (UE/CE) will depend on the inject.eccmask value: + * Any bits set to a 1 will flip the corresponding ECC bit + * Correctable errors can be injected by flipping 1 bit or the bits within + * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or + * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an + * uncorrectable error to be injected. + */ +static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci, + const char *data, size_t count) +{ + struct i7core_pvt *pvt = mci->pvt_info; + unsigned long value; + int rc; + + if (pvt->inject.enable) + disable_inject(mci); + + rc = strict_strtoul(data, 10, &value); + if (rc < 0) + return 0; + + pvt->inject.eccmask = (u32) value; + return count; +} + +static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci, + char *data) +{ + struct i7core_pvt *pvt = mci->pvt_info; + return sprintf(data, "0x%08x\n", pvt->inject.eccmask); +} + +/* + * i7core_addrmatch + * + * The type of error (UE/CE) will depend on the inject.eccmask value: + * Any bits set to a 1 will flip the corresponding ECC bit + * Correctable errors can be injected by flipping 1 bit or the bits within + * a symbol pair (2 consecutive aligned 8-bit pairs - i.e. 7:0 and 15:8 or + * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an + * uncorrectable error to be injected. + */ +static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci, + const char *data, size_t count) +{ + struct i7core_pvt *pvt = mci->pvt_info; + char *cmd, *val; + long value; + int rc; + + if (pvt->inject.enable) + disable_inject(mci); + + do { + cmd = strsep((char **) &data, ":"); + if (!cmd) + break; + val = strsep((char **) &data, " \n\t"); + if (!val) + return cmd - data; + + if (!strcasecmp(val,"any")) + value = -1; + else { + rc = strict_strtol(val, 10, &value); + if ((rc < 0) || (value < 0)) + return cmd - data; + } + + if (!strcasecmp(cmd,"channel")) { + if (value < 3) + pvt->inject.channel = value; + else + return cmd - data; + } else if (!strcasecmp(cmd,"dimm")) { + if (value < 4) + pvt->inject.dimm = value; + else + return cmd - data; + } else if (!strcasecmp(cmd,"rank")) { + if (value < 4) + pvt->inject.rank = value; + else + return cmd - data; + } else if (!strcasecmp(cmd,"bank")) { + if (value < 4) + pvt->inject.bank = value; + else + return cmd - data; + } else if (!strcasecmp(cmd,"page")) { + if (value <= 0xffff) + pvt->inject.page = value; + else + return cmd - data; + } else if (!strcasecmp(cmd,"col") || + !strcasecmp(cmd,"column")) { + if (value <= 0x3fff) + pvt->inject.col = value; + else + return cmd - data; + } + } while (1); + + return count; +} + +static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci, + char *data) +{ + struct i7core_pvt *pvt = mci->pvt_info; + char channel[4], dimm[4], bank[4], rank[4], page[7], col[7]; + + if (pvt->inject.channel < 0) + sprintf(channel, "any"); + else + sprintf(channel, "%d", pvt->inject.channel); + if (pvt->inject.dimm < 0) + sprintf(dimm, "any"); + else + sprintf(dimm, "%d", pvt->inject.dimm); + if (pvt->inject.bank < 0) + sprintf(bank, "any"); + else + sprintf(bank, "%d", pvt->inject.bank); + if (pvt->inject.rank < 0) + sprintf(rank, "any"); + else + sprintf(rank, "%d", pvt->inject.rank); + if (pvt->inject.page < 0) + sprintf(page, "any"); + else + sprintf(page, "0x%04x", pvt->inject.page); + if (pvt->inject.col < 0) + sprintf(col, "any"); + else + sprintf(col, "0x%04x", pvt->inject.col); + + return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n" + "rank: %s\npage: %s\ncolumn: %s\n", + channel, dimm, bank, rank, page, col); +} + +/* + * This routine prepares the Memory Controller for error injection. + * The error will be injected when some process tries to write to the + * memory that matches the given criteria. + * The criteria can be set in terms of a mask where dimm, rank, bank, page + * and col can be specified. + * A -1 value for any of the mask items will make the MCU to ignore + * that matching criteria for error injection. + * + * It should be noticed that the error will only happen after a write operation + * on a memory that matches the condition. if REPEAT_EN is not enabled at + * inject mask, then it will produce just one error. Otherwise, it will repeat + * until the injectmask would be cleaned. + * + * FIXME: This routine assumes that MAXNUMDIMMS value of MC_MAX_DOD + * is reliable enough to check if the MC is using the + * three channels. However, this is not clear at the datasheet. + */ +static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, + const char *data, size_t count) +{ + struct i7core_pvt *pvt = mci->pvt_info; + u32 injectmask; + u64 mask = 0; + int rc; + long enable; + + rc = strict_strtoul(data, 10, &enable); + if ((rc < 0)) + return 0; + + if (enable) { + pvt->inject.enable = 1; + } else { + disable_inject(mci); + return count; + } + + /* Sets pvt->inject.dimm mask */ + if (pvt->inject.dimm < 0) + mask |= 1l << 41; + else { + if (maxnumdimms(pvt) > 2) + mask |= (pvt->inject.dimm & 0x3l) << 35; + else + mask |= (pvt->inject.dimm & 0x1l) << 36; + } + + /* Sets pvt->inject.rank mask */ + if (pvt->inject.rank < 0) + mask |= 1l << 40; + else { + if (maxnumdimms(pvt) > 2) + mask |= (pvt->inject.rank & 0x1l) << 34; + else + mask |= (pvt->inject.rank & 0x3l) << 34; + } + + /* Sets pvt->inject.bank mask */ + if (pvt->inject.bank < 0) + mask |= 1l << 39; + else + mask |= (pvt->inject.bank & 0x15l) << 30; + + /* Sets pvt->inject.page mask */ + if (pvt->inject.page < 0) + mask |= 1l << 38; + else + mask |= (pvt->inject.page & 0xffffl) << 14; + + /* Sets pvt->inject.column mask */ + if (pvt->inject.col < 0) + mask |= 1l << 37; + else + mask |= (pvt->inject.col & 0x3fffl); + + pci_write_config_qword(pvt->pci_ch[pvt->inject.channel][0], + MC_CHANNEL_ADDR_MATCH, mask); + + pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], + MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask); + + /* + * bit 0: REPEAT_EN + * bits 1-2: MASK_HALF_CACHELINE + * bit 3: INJECT_ECC + * bit 4: INJECT_ADDR_PARITY + */ + + injectmask = (pvt->inject.type & 1) && + (pvt->inject.section & 0x3) << 1 && + (pvt->inject.type & 0x6) << (3 - 1); + + pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], + MC_CHANNEL_ERROR_MASK, injectmask); + + + debugf0("Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n", + mask, pvt->inject.eccmask, injectmask); + + return count; +} + +static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, + char *data) +{ + struct i7core_pvt *pvt = mci->pvt_info; + return sprintf(data, "%d\n", pvt->inject.enable); +} + +/* + * Sysfs struct + */ +static struct mcidev_sysfs_attribute i7core_inj_attrs[] = { + + { + .attr = { + .name = "inject_section", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = i7core_inject_section_show, + .store = i7core_inject_section_store, + }, { + .attr = { + .name = "inject_type", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = i7core_inject_type_show, + .store = i7core_inject_type_store, + }, { + .attr = { + .name = "inject_eccmask", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = i7core_inject_eccmask_show, + .store = i7core_inject_eccmask_store, + }, { + .attr = { + .name = "inject_addrmatch", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = i7core_inject_addrmatch_show, + .store = i7core_inject_addrmatch_store, + }, { + .attr = { + .name = "inject_enable", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = i7core_inject_enable_show, + .store = i7core_inject_enable_store, + }, +}; + /**************************************************************************** Device initialization routines: put/get, init/exit ****************************************************************************/ @@ -322,10 +723,11 @@ static int __devinit i7core_probe(struct pci_dev *pdev, debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); - mci->dev = &pdev->dev; /* record ptr to the generic device */ + mci->dev = &pdev->dev; /* record ptr to the generic device */ dev_set_drvdata(mci->dev, mci); pvt = mci->pvt_info; + // pvt->system_address = pdev; /* Record this device in our private */ // pvt->maxch = num_channels; // pvt->maxdimmperch = num_dimms_per_channel; @@ -343,6 +745,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, mci->ctl_name = i7core_devs[dev_idx].ctl_name; mci->dev_name = pci_name(pdev); mci->ctl_page_to_phys = NULL; + mci->mc_driver_sysfs_attributes = i7core_inj_attrs; /* add this new MC control structure to EDAC's list of MCs */ if (edac_mc_add_mc(mci)) { @@ -365,6 +768,14 @@ static int __devinit i7core_probe(struct pci_dev *pdev, __func__); } + /* Default error mask is any memory */ + pvt->inject.channel = -1; + pvt->inject.dimm = -1; + pvt->inject.rank = -1; + pvt->inject.bank = -1; + pvt->inject.page = -1; + pvt->inject.col = -1; + return 0; fail1: From 0b2b7b7ec06ce615acd11374bf9a512e166dabb0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:29 -0300 Subject: [PATCH 03/83] i7core_edac: Add more status functions to EDAC driver This patch were co-authored with Aristeu Rozanski. Signed-off-by: Aristeu Sergio Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 114 ++++++++++++++++++++++++++++++------- 1 file changed, 95 insertions(+), 19 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index b590f8468693..da40730bf9c9 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -62,6 +62,15 @@ /* OFFSETS for Devices 4,5 and 6 Function 0 */ +#define MC_CHANNEL_DIMM_INIT_PARAMS 0x58 + #define THREE_DIMMS_PRESENT (1 << 24) + #define SINGLE_QUAD_RANK_PRESENT (1 << 23) + #define QUAD_RANK_PRESENT (1 << 22) + #define REGISTERED_DIMM (1 << 15) + +#define MC_CHANNEL_RANK_PRESENT 0x7c + #define RANK_PRESENT_MASK 0xffff + #define MC_CHANNEL_ADDR_MATCH 0xf0 #define MC_CHANNEL_ERROR_MASK 0xf8 #define MC_CHANNEL_ERROR_INJECT 0xfc @@ -74,6 +83,46 @@ #define NO_MASK_CACHELINE 0x00 #define REPEAT_EN 0x01 + /* OFFSETS for Devices 4,5 and 6 Function 1 */ +#define MC_DOD_CH_DIMM0 0x48 +#define MC_DOD_CH_DIMM1 0x4c +#define MC_DOD_CH_DIMM2 0x50 + #define RANKOFFSET_MASK ((1 << 12) | (1 << 11) | (1 << 10)) + #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10) + #define DIMM_PRESENT_MASK (1 << 9) + #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9) + #define NUMBANK_MASK ((1 << 8) | (1 << 7)) + #define NUMBANK(x) (((x) & NUMBANK_MASK) >> 7) + #define NUMRANK_MASK ((1 << 6) | (1 << 5)) + #define NUMRANK(x) (((x) & NUMRANK_MASK) >> 5) + #define NUMROW_MASK ((1 << 4) | (1 << 3)) + #define NUMROW(x) (((x) & NUMROW_MASK) >> 3) + #define NUMCOL_MASK 3 + #define NUMCOL(x) ((x) & NUMCOL_MASK) + +#define MC_SAG_CH_0 0x80 +#define MC_SAG_CH_1 0x84 +#define MC_SAG_CH_2 0x88 +#define MC_SAG_CH_3 0x8c +#define MC_SAG_CH_4 0x90 +#define MC_SAG_CH_5 0x94 +#define MC_SAG_CH_6 0x98 +#define MC_SAG_CH_7 0x9c + +#define MC_RIR_LIMIT_CH_0 0x40 +#define MC_RIR_LIMIT_CH_1 0x44 +#define MC_RIR_LIMIT_CH_2 0x48 +#define MC_RIR_LIMIT_CH_3 0x4C +#define MC_RIR_LIMIT_CH_4 0x50 +#define MC_RIR_LIMIT_CH_5 0x54 +#define MC_RIR_LIMIT_CH_6 0x58 +#define MC_RIR_LIMIT_CH_7 0x5C +#define MC_RIR_LIMIT_MASK ((1 << 10) - 1) + +#define MC_RIR_WAY_CH 0x80 + #define MC_RIR_WAY_OFFSET_MASK (((1 << 14) - 1) & ~0x7) + #define MC_RIR_WAY_RANK_MASK 0x7 + /* * i7core structs */ @@ -99,11 +148,17 @@ struct i7core_inject { int channel, dimm, rank, bank, page, col; }; +struct i7core_channel { + u32 ranks; + u32 dimms; +}; + struct i7core_pvt { struct pci_dev *pci_mcr; /* Dev 3:0 */ struct pci_dev *pci_ch[NUM_CHANS][NUM_FUNCS]; struct i7core_info info; struct i7core_inject inject; + struct i7core_channel channel[NUM_CHANS]; }; /* Device name and register DID (Device ID) */ @@ -133,16 +188,12 @@ static struct edac_pci_ctl_info *i7core_pci; ****************************************************************************/ /* MC_CONTROL bits */ -#define CH2_ACTIVE(pvt) ((pvt)->info.mc_control & 1 << 10) -#define CH1_ACTIVE(pvt) ((pvt)->info.mc_control & 1 << 9) -#define CH0_ACTIVE(pvt) ((pvt)->info.mc_control & 1 << 8) +#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & 1 << (8 + ch)) #define ECCx8(pvt) ((pvt)->info.mc_control & 1 << 1) /* MC_STATUS bits */ #define ECC_ENABLED(pvt) ((pvt)->info.mc_status & 1 << 3) -#define CH2_DISABLED(pvt) ((pvt)->info.mc_status & 1 << 2) -#define CH1_DISABLED(pvt) ((pvt)->info.mc_status & 1 << 1) -#define CH0_DISABLED(pvt) ((pvt)->info.mc_status & 1 << 0) +#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & 1 << ch) /* MC_MAX_DOD read functions */ static inline int maxnumdimms(struct i7core_pvt *pvt) @@ -189,19 +240,12 @@ static inline int maxnumcol(struct i7core_pvt *pvt) static int get_dimm_config(struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; + int i; pci_read_config_dword(pvt->pci_mcr, MC_CONTROL, &pvt->info.mc_control); pci_read_config_dword(pvt->pci_mcr, MC_STATUS, &pvt->info.mc_status); pci_read_config_dword(pvt->pci_mcr, MC_MAX_DOD, &pvt->info.max_dod); - debugf0("Channels active [%c][%c][%c] - enabled [%c][%c][%c]\n", - CH0_ACTIVE(pvt)?'0':'-', - CH1_ACTIVE(pvt)?'1':'-', - CH2_ACTIVE(pvt)?'2':'-', - CH0_DISABLED(pvt)?'-':'0', - CH1_DISABLED(pvt)?'-':'1', - CH2_DISABLED(pvt)?'-':'2'); - if (ECC_ENABLED(pvt)) debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt)?8:4); else @@ -213,6 +257,38 @@ static int get_dimm_config(struct mem_ctl_info *mci) debugf0("DOD Maximum rows x colums = 0x%x x 0x%x\n", maxnumrow(pvt), maxnumcol(pvt)); + debugf0("Memory channel configuration:\n"); + + for (i = 0; i < NUM_CHANS; i++) { + u32 data; + + if (!CH_ACTIVE(pvt, i)) { + debugf0("Channel %i is not active\n", i); + continue; + } + if (CH_DISABLED(pvt, i)) { + debugf0("Channel %i is disabled\n", i); + continue; + } + + pci_read_config_dword(pvt->pci_ch[i][0], + MC_CHANNEL_DIMM_INIT_PARAMS, &data); + + pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT)? 4 : 2; + + if (data & THREE_DIMMS_PRESENT) + pvt->channel[i].dimms = 3; + else if (data & SINGLE_QUAD_RANK_PRESENT) + pvt->channel[i].dimms = 1; + else + pvt->channel[i].dimms = 2; + + debugf0("Channel %d (0x%08x): %d ranks, %d dimms " + "(%sregistered)\n", i, data, + pvt->channel[i].ranks, pvt->channel[i].dimms, + (data & REGISTERED_DIMM)? "" : "un" ); + } + return 0; } @@ -489,7 +565,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, if (pvt->inject.dimm < 0) mask |= 1l << 41; else { - if (maxnumdimms(pvt) > 2) + if (pvt->channel[pvt->inject.channel].dimms > 2) mask |= (pvt->inject.dimm & 0x3l) << 35; else mask |= (pvt->inject.dimm & 0x1l) << 36; @@ -499,7 +575,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, if (pvt->inject.rank < 0) mask |= 1l << 40; else { - if (maxnumdimms(pvt) > 2) + if (pvt->channel[pvt->inject.channel].dimms > 2) mask |= (pvt->inject.rank & 0x1l) << 34; else mask |= (pvt->inject.rank & 0x3l) << 34; @@ -646,9 +722,6 @@ static int i7core_get_devices(struct mem_ctl_info *mci, int dev_idx) } pvt->pci_mcr=pdev; - /* Get dimm basic config */ - get_dimm_config(mci); - /* Retrieve all needed functions at MTR channel controllers */ for (i = 0; i < NUM_CHANS; i++) { pdev = NULL; @@ -672,6 +745,9 @@ static int i7core_get_devices(struct mem_ctl_info *mci, int dev_idx) } i7core_printk(KERN_INFO, "Driver loaded.\n"); + /* Get dimm basic config */ + get_dimm_config(mci); + return 0; } From 8f331907578623f90a134261a559fa3249142caa Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:29 -0300 Subject: [PATCH 04/83] i7core_edac: Registers all supported MC functions Now, it will try to register on all supported Memory Controller functions. It should be noticed that dev3, function 2 is present only on chips with Registered DIMM's, according to the datasheet. So, the driver doesn't return -ENODEV is all functions but this one were successfully registered and enabled: EDAC i7core: Registered device 8086:2c18 fn=3 0 EDAC i7core: Registered device 8086:2c19 fn=3 1 EDAC i7core: Device not found: PCI ID 8086:2c1a (dev 3, func 2) EDAC i7core: Registered device 8086:2c1c fn=3 4 EDAC i7core: Registered device 8086:2c20 fn=4 0 EDAC i7core: Registered device 8086:2c21 fn=4 1 EDAC i7core: Registered device 8086:2c22 fn=4 2 EDAC i7core: Registered device 8086:2c23 fn=4 3 EDAC i7core: Registered device 8086:2c28 fn=5 0 EDAC i7core: Registered device 8086:2c29 fn=5 1 EDAC i7core: Registered device 8086:2c2a fn=5 2 EDAC i7core: Registered device 8086:2c2b fn=5 3 EDAC i7core: Registered device 8086:2c30 fn=6 0 EDAC i7core: Registered device 8086:2c31 fn=6 1 EDAC i7core: Registered device 8086:2c32 fn=6 2 EDAC i7core: Registered device 8086:2c33 fn=6 3 EDAC i7core: Driver loaded. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 217 ++++++++++++++++++++++--------------- 1 file changed, 131 insertions(+), 86 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index da40730bf9c9..79aa84eaa12d 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -128,7 +128,8 @@ */ #define NUM_CHANS 3 -#define NUM_FUNCS 1 +#define NUM_MCR_FUNCS 4 +#define NUM_CHAN_FUNCS 3 struct i7core_info { u32 mc_control; @@ -153,9 +154,16 @@ struct i7core_channel { u32 dimms; }; +struct pci_id_descr { + int dev; + int func; + int dev_id; + struct pci_dev *pdev; +}; + struct i7core_pvt { - struct pci_dev *pci_mcr; /* Dev 3:0 */ - struct pci_dev *pci_ch[NUM_CHANS][NUM_FUNCS]; + struct pci_dev *pci_mcr[NUM_MCR_FUNCS]; + struct pci_dev *pci_ch[NUM_CHANS][NUM_CHAN_FUNCS]; struct i7core_info info; struct i7core_inject inject; struct i7core_channel channel[NUM_CHANS]; @@ -167,11 +175,47 @@ struct i7core_dev_info { u16 fsb_mapping_errors; /* DID for the branchmap,control */ }; -static int chan_pci_ids[NUM_CHANS] = { - PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL, /* Dev 4 */ - PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL, /* Dev 5 */ - PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL, /* Dev 6 */ +#define PCI_DESCR(device, function, device_id) \ + .dev = (device), \ + .func = (function), \ + .dev_id = (device_id) + +struct pci_id_descr pci_devs[] = { + /* Memory controller */ + { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) }, + { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) }, + { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */ + { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) }, + + /* Channel 0 */ + { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) }, + { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) }, + { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH0_RANK) }, + { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH0_TC) }, + + /* Channel 1 */ + { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL) }, + { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR) }, + { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH1_RANK) }, + { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH1_TC) }, + + /* Channel 2 */ + { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL) }, + { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) }, + { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) }, + { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) }, }; +#define N_DEVS ARRAY_SIZE(pci_devs) + +/* + * pci_device_id table for which devices we are looking for + * This should match the first device at pci_devs table + */ +static const struct pci_device_id i7core_pci_tbl[] __devinitdata = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR)}, + {0,} /* 0 terminated list. */ +}; + /* Table of devices attributes supported by this driver */ static const struct i7core_dev_info i7core_devs[] = { @@ -242,9 +286,12 @@ static int get_dimm_config(struct mem_ctl_info *mci) struct i7core_pvt *pvt = mci->pvt_info; int i; - pci_read_config_dword(pvt->pci_mcr, MC_CONTROL, &pvt->info.mc_control); - pci_read_config_dword(pvt->pci_mcr, MC_STATUS, &pvt->info.mc_status); - pci_read_config_dword(pvt->pci_mcr, MC_MAX_DOD, &pvt->info.max_dod); + if (!pvt->pci_mcr[0]) + return -ENODEV; + + pci_read_config_dword(pvt->pci_mcr[0], MC_CONTROL, &pvt->info.mc_control); + pci_read_config_dword(pvt->pci_mcr[0], MC_STATUS, &pvt->info.mc_status); + pci_read_config_dword(pvt->pci_mcr[0], MC_MAX_DOD, &pvt->info.max_dod); if (ECC_ENABLED(pvt)) debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt)?8:4); @@ -303,14 +350,19 @@ static int get_dimm_config(struct mem_ctl_info *mci) we're disabling error injection on all write calls to the sysfs nodes that controls the error code injection. */ -static void disable_inject(struct mem_ctl_info *mci) +static int disable_inject(struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; pvt->inject.enable = 0; + if (!pvt->pci_ch[pvt->inject.channel][0]) + return -ENODEV; + pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ERROR_MASK, 0); + + return 0; } /* @@ -550,6 +602,9 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, int rc; long enable; + if (!pvt->pci_ch[pvt->inject.channel][0]) + return 0; + rc = strict_strtoul(data, 10, &enable); if ((rc < 0)) return 0; @@ -684,17 +739,12 @@ static struct mcidev_sysfs_attribute i7core_inj_attrs[] = { * i7core_put_devices 'put' all the devices that we have * reserved via 'get' */ -static void i7core_put_devices(struct mem_ctl_info *mci) +static void i7core_put_devices(void) { - struct i7core_pvt *pvt = mci->pvt_info; - int i, n; + int i; - pci_dev_put(pvt->pci_mcr); - - /* Release all PCI device functions at MTR channel controllers */ - for (i = 0; i < NUM_CHANS; i++) - for (n = 0; n < NUM_FUNCS; n++) - pci_dev_put(pvt->pci_ch[i][n]); + for (i = 0; i < N_DEVS; i++) + pci_dev_put(pci_devs[i].pdev); } /* @@ -703,50 +753,67 @@ static void i7core_put_devices(struct mem_ctl_info *mci) * * Need to 'get' device 16 func 1 and func 2 */ -static int i7core_get_devices(struct mem_ctl_info *mci, int dev_idx) +static int i7core_get_devices(struct mem_ctl_info *mci, struct pci_dev *mcidev) { - struct i7core_pvt *pvt; - struct pci_dev *pdev; - int i, n, func; + struct i7core_pvt *pvt = mci->pvt_info; + int rc, i,func; + struct pci_dev *pdev = NULL; pvt = mci->pvt_info; memset(pvt, 0, sizeof(*pvt)); - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR, - NULL); - if (!pdev) { - i7core_printk(KERN_ERR, - "Couldn't get PCI ID %04x:%04x function 0\n", - PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR); - return -ENODEV; - } - pvt->pci_mcr=pdev; + for (i = 0; i < N_DEVS; i++) { + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, + pci_devs[i].dev_id, NULL); + if (!pdev) { + /* End of list, leave */ + i7core_printk(KERN_ERR, + "Device not found: PCI ID %04x:%04x " + "(dev %d, func %d)\n", + PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, + pci_devs[i].dev,pci_devs[i].func); + if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2)) + continue; /* Only on chips with RDIMMs */ + else + i7core_put_devices(); + } + pci_devs[i].pdev = pdev; - /* Retrieve all needed functions at MTR channel controllers */ - for (i = 0; i < NUM_CHANS; i++) { - pdev = NULL; - for (n = 0; n < NUM_FUNCS; n++) { - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, - chan_pci_ids[i], pdev); - if (!pdev) { - /* End of list, leave */ - i7core_printk(KERN_ERR, - "Device not found: PCI ID %04x:%04x " - "found only %d functions " - "(broken BIOS?)\n", - PCI_VENDOR_ID_INTEL, - chan_pci_ids[i], n); - i7core_put_devices(mci); - return -ENODEV; - } - func = PCI_FUNC(pdev->devfn); - pvt->pci_ch[i][func] = pdev; + rc = pci_enable_device(pdev); + if (rc < 0) { + i7core_printk(KERN_ERR, + "Couldn't enable PCI ID %04x:%04x " + "(dev %d, func %d)\n", + PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, + pci_devs[i].dev, pci_devs[i].func); + i7core_put_devices(); + return rc; + } + /* Sanity check */ + if (PCI_FUNC(pdev->devfn) != pci_devs[i].func) { + i7core_printk(KERN_ERR, + "Device PCI ID %04x:%04x " + "has function %d instead of %d\n", + PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, + PCI_FUNC(pdev->devfn), pci_devs[i].func); + i7core_put_devices(); + return -EINVAL; + } + + i7core_printk(KERN_INFO, + "Registered device %0x:%0x fn=%0x %0x\n", + PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + + func = PCI_FUNC(pdev->devfn); + if (pci_devs[i].dev < 4) { + pvt->pci_mcr[func] = pdev; + } else { + pvt->pci_ch[pci_devs[i].dev - 4][func] = pdev; } } - i7core_printk(KERN_INFO, "Driver loaded.\n"); - /* Get dimm basic config */ - get_dimm_config(mci); + i7core_printk(KERN_INFO, "Driver loaded.\n"); return 0; } @@ -763,7 +830,6 @@ static int __devinit i7core_probe(struct pci_dev *pdev, { struct mem_ctl_info *mci; struct i7core_pvt *pvt; - int rc; int num_channels; int num_csrows; int num_dimms_per_channel; @@ -772,20 +838,6 @@ static int __devinit i7core_probe(struct pci_dev *pdev, if (dev_idx >= ARRAY_SIZE(i7core_devs)) return -EINVAL; - /* wake up device */ - rc = pci_enable_device(pdev); - if (rc == -EIO) - return rc; - - debugf0("MC: " __FILE__ ": %s(), pdev bus %u dev=0x%x fn=0x%x\n", - __func__, - pdev->bus->number, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - - /* We only are looking for func 0 of the set */ - if (PCI_FUNC(pdev->devfn) != 0) - return -ENODEV; - num_channels = NUM_CHANS; /* FIXME: FAKE data, since we currently don't now how to get this */ @@ -808,10 +860,6 @@ static int __devinit i7core_probe(struct pci_dev *pdev, // pvt->maxch = num_channels; // pvt->maxdimmperch = num_dimms_per_channel; - /* 'get' the pci devices we want to reserve for our use */ - if (i7core_get_devices(mci, dev_idx)) - goto fail0; - mci->mc_idx = 0; mci->mtype_cap = MEM_FLAG_FB_DDR2; /* FIXME: it uses DDR3 */ mci->edac_ctl_cap = EDAC_FLAG_NONE; @@ -823,6 +871,10 @@ static int __devinit i7core_probe(struct pci_dev *pdev, mci->ctl_page_to_phys = NULL; mci->mc_driver_sysfs_attributes = i7core_inj_attrs; + /* 'get' the pci devices we want to reserve for our use */ + if (i7core_get_devices(mci, pdev)) + goto fail0; + /* add this new MC control structure to EDAC's list of MCs */ if (edac_mc_add_mc(mci)) { debugf0("MC: " __FILE__ @@ -852,10 +904,13 @@ static int __devinit i7core_probe(struct pci_dev *pdev, pvt->inject.page = -1; pvt->inject.col = -1; + /* Get dimm basic config */ + get_dimm_config(mci); + return 0; fail1: - i7core_put_devices(mci); + i7core_put_devices(); fail0: edac_mc_free(mci); @@ -880,21 +935,11 @@ static void __devexit i7core_remove(struct pci_dev *pdev) return; /* retrieve references to resources, and free those resources */ - i7core_put_devices(mci); + i7core_put_devices(); edac_mc_free(mci); } -/* - * pci_device_id table for which devices we are looking for - * - * The "E500P" device is the first device supported. - */ -static const struct pci_device_id i7core_pci_tbl[] __devinitdata = { - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR)}, - {0,} /* 0 terminated list. */ -}; - MODULE_DEVICE_TABLE(pci, i7core_pci_tbl); /* From f122a89222510e8f57e8e0b9b5cdd3ec8863fe4c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:29 -0300 Subject: [PATCH 05/83] i7core_edac: Show read/write virtual/physical channel association Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 79aa84eaa12d..09a0998e1c3f 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -68,6 +68,10 @@ #define QUAD_RANK_PRESENT (1 << 22) #define REGISTERED_DIMM (1 << 15) +#define MC_CHANNEL_MAPPER 0x60 + #define RDLCH(r, ch) ((((r) >> (3 + (ch * 6))) & 0x07) - 1) + #define WRLCH(r, ch) ((((r) >> (ch * 6)) & 0x07) - 1) + #define MC_CHANNEL_RANK_PRESENT 0x7c #define RANK_PRESENT_MASK 0xffff @@ -100,6 +104,8 @@ #define NUMCOL_MASK 3 #define NUMCOL(x) ((x) & NUMCOL_MASK) +#define MC_RANK_PRESENT 0x7c + #define MC_SAG_CH_0 0x80 #define MC_SAG_CH_1 0x84 #define MC_SAG_CH_2 0x88 @@ -135,6 +141,7 @@ struct i7core_info { u32 mc_control; u32 mc_status; u32 max_dod; + u32 ch_map; }; @@ -289,9 +296,19 @@ static int get_dimm_config(struct mem_ctl_info *mci) if (!pvt->pci_mcr[0]) return -ENODEV; - pci_read_config_dword(pvt->pci_mcr[0], MC_CONTROL, &pvt->info.mc_control); - pci_read_config_dword(pvt->pci_mcr[0], MC_STATUS, &pvt->info.mc_status); - pci_read_config_dword(pvt->pci_mcr[0], MC_MAX_DOD, &pvt->info.max_dod); + /* Device 3 function 0 reads */ + pci_read_config_dword(pvt->pci_mcr[0], MC_CONTROL, + &pvt->info.mc_control); + pci_read_config_dword(pvt->pci_mcr[0], MC_STATUS, + &pvt->info.mc_status); + pci_read_config_dword(pvt->pci_mcr[0], MC_MAX_DOD, + &pvt->info.max_dod); + pci_read_config_dword(pvt->pci_mcr[0], MC_CHANNEL_MAPPER, + &pvt->info.ch_map); + + debugf0("MC control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n", + pvt->info.mc_control, pvt->info.mc_status, + pvt->info.max_dod, pvt->info.ch_map); if (ECC_ENABLED(pvt)) debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt)?8:4); @@ -318,6 +335,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) continue; } + /* Devices 4-6 function 0 */ pci_read_config_dword(pvt->pci_ch[i][0], MC_CHANNEL_DIMM_INIT_PARAMS, &data); @@ -330,10 +348,13 @@ static int get_dimm_config(struct mem_ctl_info *mci) else pvt->channel[i].dimms = 2; - debugf0("Channel %d (0x%08x): %d ranks, %d dimms " - "(%sregistered)\n", i, data, + debugf0("Ch%d (0x%08x): rd ch %d, wr ch %d, " + "%d ranks, %d %cDIMMs\n", + i, data, + RDLCH(pvt->info.ch_map, i), + WRLCH(pvt->info.ch_map, i), pvt->channel[i].ranks, pvt->channel[i].dimms, - (data & REGISTERED_DIMM)? "" : "un" ); + (data & REGISTERED_DIMM)? 'R' : 'U' ); } return 0; From 7b029d03c36e5b06e067884aaefcee2c1c62efc7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:29 -0300 Subject: [PATCH 06/83] i7core_edac: A few fixes at error injection code Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 70 ++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 09a0998e1c3f..0c17db673065 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -30,6 +30,8 @@ #include "edac_core.h" +/* To use the new pci_[read/write]_config_qword instead of two dword */ +#define USE_QWORD 1 /* * Alter this version for the module when modifications are made @@ -639,44 +641,71 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, /* Sets pvt->inject.dimm mask */ if (pvt->inject.dimm < 0) - mask |= 1l << 41; + mask |= 1L << 41; else { if (pvt->channel[pvt->inject.channel].dimms > 2) - mask |= (pvt->inject.dimm & 0x3l) << 35; + mask |= (pvt->inject.dimm & 0x3L) << 35; else - mask |= (pvt->inject.dimm & 0x1l) << 36; + mask |= (pvt->inject.dimm & 0x1L) << 36; } /* Sets pvt->inject.rank mask */ if (pvt->inject.rank < 0) - mask |= 1l << 40; + mask |= 1L << 40; else { if (pvt->channel[pvt->inject.channel].dimms > 2) - mask |= (pvt->inject.rank & 0x1l) << 34; + mask |= (pvt->inject.rank & 0x1L) << 34; else - mask |= (pvt->inject.rank & 0x3l) << 34; + mask |= (pvt->inject.rank & 0x3L) << 34; } /* Sets pvt->inject.bank mask */ if (pvt->inject.bank < 0) - mask |= 1l << 39; + mask |= 1L << 39; else - mask |= (pvt->inject.bank & 0x15l) << 30; + mask |= (pvt->inject.bank & 0x15L) << 30; /* Sets pvt->inject.page mask */ if (pvt->inject.page < 0) - mask |= 1l << 38; + mask |= 1L << 38; else - mask |= (pvt->inject.page & 0xffffl) << 14; + mask |= (pvt->inject.page & 0xffffL) << 14; /* Sets pvt->inject.column mask */ if (pvt->inject.col < 0) - mask |= 1l << 37; + mask |= 1L << 37; else - mask |= (pvt->inject.col & 0x3fffl); + mask |= (pvt->inject.col & 0x3fffL); +#if USE_QWORD pci_write_config_qword(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ADDR_MATCH, mask); +#else + pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], + MC_CHANNEL_ADDR_MATCH, mask); + pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], + MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L); +#endif + +#if 1 +#if USE_QWORD + u64 rdmask; + pci_read_config_qword(pvt->pci_ch[pvt->inject.channel][0], + MC_CHANNEL_ADDR_MATCH, &rdmask); + debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n", + mask, rdmask); +#else + u32 rdmask1, rdmask2; + + pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], + MC_CHANNEL_ADDR_MATCH, &rdmask1); + pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], + MC_CHANNEL_ADDR_MATCH + 4, &rdmask2); + + debugf0("Inject addr match write 0x%016llx, read: 0x%08x%08x\n", + mask, rdmask1, rdmask2); +#endif +#endif pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask); @@ -688,17 +717,18 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, * bit 4: INJECT_ADDR_PARITY */ - injectmask = (pvt->inject.type & 1) && - (pvt->inject.section & 0x3) << 1 && + injectmask = (pvt->inject.type & 1) | + (pvt->inject.section & 0x3) << 1 | (pvt->inject.type & 0x6) << (3 - 1); pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ERROR_MASK, injectmask); - debugf0("Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n", mask, pvt->inject.eccmask, injectmask); + + return count; } @@ -706,6 +736,16 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, char *data) { struct i7core_pvt *pvt = mci->pvt_info; + u32 injectmask; + + pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], + MC_CHANNEL_ERROR_MASK, &injectmask); + + debugf0("Inject error read: 0x%018x\n", injectmask); + + if (injectmask & 0x0c) + pvt->inject.enable = 1; + return sprintf(data, "%d\n", pvt->inject.enable); } From 87d1d272ba25a1863e40ebb1df4bc0eed7a8fd11 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:29 -0300 Subject: [PATCH 07/83] i7core_edac: need mci->edac_check, otherwise module removal doesn't work There are some locking troubles with edac_core: if you don't declare an edac_check, module may suffer from soft lock. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 0c17db673065..190596af601a 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -879,6 +879,15 @@ static int i7core_get_devices(struct mem_ctl_info *mci, struct pci_dev *mcidev) return 0; } +/* + * i7core_check_error Retrieve and process errors reported by the + * hardware. Called by the Core module. + */ +static void i7core_check_error(struct mem_ctl_info *mci) +{ + /* FIXME: need a real code here */ +} + /* * i7core_probe Probe for ONE instance of device to see if it is * present. @@ -912,8 +921,11 @@ static int __devinit i7core_probe(struct pci_dev *pdev, debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); + /* 'get' the pci devices we want to reserve for our use */ + if (i7core_get_devices(mci, pdev)) + goto fail0; + mci->dev = &pdev->dev; /* record ptr to the generic device */ - dev_set_drvdata(mci->dev, mci); pvt = mci->pvt_info; @@ -932,9 +944,8 @@ static int __devinit i7core_probe(struct pci_dev *pdev, mci->ctl_page_to_phys = NULL; mci->mc_driver_sysfs_attributes = i7core_inj_attrs; - /* 'get' the pci devices we want to reserve for our use */ - if (i7core_get_devices(mci, pdev)) - goto fail0; + /* Set the function pointer to an actual operation function */ + mci->edac_check = i7core_check_error; /* add this new MC control structure to EDAC's list of MCs */ if (edac_mc_add_mc(mci)) { @@ -992,6 +1003,7 @@ static void __devexit i7core_remove(struct pci_dev *pdev) edac_pci_release_generic_ctl(i7core_pci); mci = edac_mc_del_mc(&pdev->dev); + if (!mci) return; From 442305b152778f07504e9fdf64815d4841279bbe Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:29 -0300 Subject: [PATCH 08/83] i7core_edac: Add a memory check routine, based on device 3 function 4 This function appears only on Xeon 5500 datasheet. Yet, testing with a Xeon 3503 showed that this is also implemented on other Nehalem processors. At the first read, MC_TEST_ERR_RCV1 and MC_TEST_ERR_RCV0 can contain any value. Modify CE error logic to update the error count only after the second read. An alternative approach would be to do a write at rcv0 and rcv1 registers, but it seemed better to keep they untouched, since BIOS might eventually assume that they are exclusive for their usage. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 115 ++++++++++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 7 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 190596af601a..b5dbc2b83961 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -62,6 +62,18 @@ #define MC_STATUS 0x4c #define MC_MAX_DOD 0x64 +/* + * OFFSETS for Device 3 Function 4, as inicated on Xeon 5500 datasheet: + * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf + */ + +#define MC_TEST_ERR_RCV1 0x60 + #define DIMM2_COR_ERR(r) ((r) & 0x7fff) + +#define MC_TEST_ERR_RCV0 0x64 + #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff) + #define DIMM0_COR_ERR(r) ((r) & 0x7fff) + /* OFFSETS for Devices 4,5 and 6 Function 0 */ #define MC_CHANNEL_DIMM_INIT_PARAMS 0x58 @@ -136,8 +148,9 @@ */ #define NUM_CHANS 3 -#define NUM_MCR_FUNCS 4 -#define NUM_CHAN_FUNCS 3 +#define MAX_DIMMS 3 /* Max DIMMS per channel */ +#define MAX_MCR_FUNC 4 +#define MAX_CHAN_FUNC 3 struct i7core_info { u32 mc_control; @@ -159,8 +172,8 @@ struct i7core_inject { }; struct i7core_channel { - u32 ranks; - u32 dimms; + u32 ranks; + u32 dimms; }; struct pci_id_descr { @@ -171,11 +184,16 @@ struct pci_id_descr { }; struct i7core_pvt { - struct pci_dev *pci_mcr[NUM_MCR_FUNCS]; - struct pci_dev *pci_ch[NUM_CHANS][NUM_CHAN_FUNCS]; + struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1]; + struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1]; struct i7core_info info; struct i7core_inject inject; struct i7core_channel channel[NUM_CHANS]; + + int ce_count_available; + unsigned long ce_count[MAX_DIMMS]; /* ECC corrected errors counts per dimm */ + int last_ce_count[MAX_DIMMS]; + }; /* Device name and register DID (Device ID) */ @@ -749,6 +767,19 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, return sprintf(data, "%d\n", pvt->inject.enable); } +static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data) +{ + struct i7core_pvt *pvt = mci->pvt_info; + + if (!pvt->ce_count_available) + return sprintf(data, "unavailable\n"); + + return sprintf(data, "dimm0: %lu\ndimm1: %lu\ndimm2: %lu\n", + pvt->ce_count[0], + pvt->ce_count[1], + pvt->ce_count[2]); +} + /* * Sysfs struct */ @@ -789,6 +820,13 @@ static struct mcidev_sysfs_attribute i7core_inj_attrs[] = { }, .show = i7core_inject_enable_show, .store = i7core_inject_enable_store, + }, { + .attr = { + .name = "corrected_error_counts", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = i7core_ce_regs_show, + .store = NULL, }, }; @@ -879,13 +917,76 @@ static int i7core_get_devices(struct mem_ctl_info *mci, struct pci_dev *mcidev) return 0; } +/**************************************************************************** + Error check routines + ****************************************************************************/ + +/* This function is based on the device 3 function 4 registers as described on: + * Intel Xeon Processor 5500 Series Datasheet Volume 2 + * http://www.intel.com/Assets/PDF/datasheet/321322.pdf + * also available at: + * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf + */ +static void check_mc_test_err(struct mem_ctl_info *mci) +{ + struct i7core_pvt *pvt = mci->pvt_info; + u32 rcv1, rcv0; + int new0, new1, new2; + + if (!pvt->pci_mcr[4]) { + debugf0("%s MCR registers not found\n",__func__); + return; + } + + /* Corrected error reads */ + pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1); + pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0); + + /* Store the new values */ + new2 = DIMM2_COR_ERR(rcv1); + new1 = DIMM1_COR_ERR(rcv0); + new0 = DIMM0_COR_ERR(rcv0); + + debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n", + (pvt->ce_count_available ? "UPDATE" : "READ"), + rcv1, rcv0, new0, new1, new2); + + /* Updates CE counters if it is not the first time here */ + if (pvt->ce_count_available) { + /* Updates CE counters */ + int add0, add1, add2; + + add2 = new2 - pvt->last_ce_count[2]; + add1 = new1 - pvt->last_ce_count[1]; + add0 = new0 - pvt->last_ce_count[0]; + + if (add2 < 0) + add2 += 0x7fff; + pvt->ce_count[2] += add2; + + if (add1 < 0) + add1 += 0x7fff; + pvt->ce_count[1] += add1; + + if (add0 < 0) + add0 += 0x7fff; + pvt->ce_count[0] += add0; + } else + pvt->ce_count_available = 1; + + /* Store the new values */ + pvt->last_ce_count[2] = new2; + pvt->last_ce_count[1] = new1; + pvt->last_ce_count[0] = new0; +} + /* * i7core_check_error Retrieve and process errors reported by the * hardware. Called by the Core module. */ static void i7core_check_error(struct mem_ctl_info *mci) { - /* FIXME: need a real code here */ + check_mc_test_err(mci); } /* From ef708b53b98f2b53d9686a9f8f0b8d437952c295 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:30 -0300 Subject: [PATCH 09/83] i7core_edac: Add additional tests for error detection Properly check the number of channels and improve probing error detection Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 209 +++++++++++++++++++++++++------------ 1 file changed, 144 insertions(+), 65 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index b5dbc2b83961..bfa462f6fa0e 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -189,6 +189,7 @@ struct i7core_pvt { struct i7core_info info; struct i7core_inject inject; struct i7core_channel channel[NUM_CHANS]; + int channels; /* Number of active channels */ int ce_count_available; unsigned long ce_count[MAX_DIMMS]; /* ECC corrected errors counts per dimm */ @@ -259,12 +260,12 @@ static struct edac_pci_ctl_info *i7core_pci; ****************************************************************************/ /* MC_CONTROL bits */ -#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & 1 << (8 + ch)) -#define ECCx8(pvt) ((pvt)->info.mc_control & 1 << 1) +#define CH_ACTIVE(pvt, ch) ((pvt)->info.mc_control & (1 << (8 + ch))) +#define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1)) /* MC_STATUS bits */ -#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & 1 << 3) -#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & 1 << ch) +#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 3)) +#define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch)) /* MC_MAX_DOD read functions */ static inline int maxnumdimms(struct i7core_pvt *pvt) @@ -308,6 +309,48 @@ static inline int maxnumcol(struct i7core_pvt *pvt) /**************************************************************************** Memory check routines ****************************************************************************/ +static int i7core_get_active_channels(int *channels) +{ + struct pci_dev *pdev = NULL; + int i; + u32 status, control; + + *channels = 0; + + for (i = 0; i < N_DEVS; i++) { + if (!pci_devs[i].pdev) + continue; + + if (PCI_SLOT(pci_devs[i].pdev->devfn) == 3 && + PCI_FUNC(pci_devs[i].pdev->devfn) == 0) { + pdev = pci_devs[i].pdev; + break; + } + } + + if (!pdev) + return -ENODEV; + + /* Device 3 function 0 reads */ + pci_read_config_dword(pdev, MC_STATUS, &status); + pci_read_config_dword(pdev, MC_CONTROL, &control); + + for (i = 0; i < NUM_CHANS; i++) { + /* Check if the channel is active */ + if (!(control & (1 << (8 + i)))) + continue; + + /* Check if the channel is disabled */ + if (status & (1 << i)) { + continue; + } + + (*channels)++; + } + + return 0; +} + static int get_dimm_config(struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; @@ -852,69 +895,103 @@ static void i7core_put_devices(void) * * Need to 'get' device 16 func 1 and func 2 */ -static int i7core_get_devices(struct mem_ctl_info *mci, struct pci_dev *mcidev) +static int i7core_get_devices(void) { - struct i7core_pvt *pvt = mci->pvt_info; - int rc, i,func; + int rc, i; struct pci_dev *pdev = NULL; - pvt = mci->pvt_info; - memset(pvt, 0, sizeof(*pvt)); - for (i = 0; i < N_DEVS; i++) { pdev = pci_get_device(PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, NULL); - if (!pdev) { - /* End of list, leave */ + if (likely(pdev)) + pci_devs[i].pdev = pdev; + else { i7core_printk(KERN_ERR, "Device not found: PCI ID %04x:%04x " "(dev %d, func %d)\n", PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, pci_devs[i].dev,pci_devs[i].func); - if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2)) - continue; /* Only on chips with RDIMMs */ - else - i7core_put_devices(); - } - pci_devs[i].pdev = pdev; - rc = pci_enable_device(pdev); - if (rc < 0) { - i7core_printk(KERN_ERR, - "Couldn't enable PCI ID %04x:%04x " - "(dev %d, func %d)\n", - PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, - pci_devs[i].dev, pci_devs[i].func); - i7core_put_devices(); - return rc; + /* Dev 3 function 2 only exists on chips with RDIMMs */ + if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2)) + continue; + + /* End of list, leave */ + rc = -ENODEV; + goto error; } + /* Sanity check */ - if (PCI_FUNC(pdev->devfn) != pci_devs[i].func) { + if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[i].dev || + PCI_FUNC(pdev->devfn) != pci_devs[i].func)) { i7core_printk(KERN_ERR, "Device PCI ID %04x:%04x " - "has function %d instead of %d\n", + "has fn %d.%d instead of fn %d.%d\n", PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, - PCI_FUNC(pdev->devfn), pci_devs[i].func); - i7core_put_devices(); - return -EINVAL; + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), + pci_devs[i].dev, pci_devs[i].func); + rc = -EINVAL; + goto error; + } + + /* Be sure that the device is enabled */ + rc = pci_enable_device(pdev); + if (unlikely(rc < 0)) { + i7core_printk(KERN_ERR, + "Couldn't enable PCI ID %04x:%04x " + "fn %d.%d\n", + PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + goto error; } i7core_printk(KERN_INFO, - "Registered device %0x:%0x fn=%0x %0x\n", + "Registered device %0x:%0x fn %d.%d\n", PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - - func = PCI_FUNC(pdev->devfn); - if (pci_devs[i].dev < 4) { - pvt->pci_mcr[func] = pdev; - } else { - pvt->pci_ch[pci_devs[i].dev - 4][func] = pdev; - } } - i7core_printk(KERN_INFO, "Driver loaded.\n"); - return 0; + +error: + i7core_put_devices(); + return -EINVAL; +} + +static int mci_bind_devs(struct mem_ctl_info *mci) +{ + struct i7core_pvt *pvt = mci->pvt_info; + struct pci_dev *pdev; + int i, func, slot; + + for (i = 0; i < N_DEVS; i++) { + pdev = pci_devs[i].pdev; + if (!pdev) + continue; + + func = PCI_FUNC(pdev->devfn); + slot = PCI_SLOT(pdev->devfn); + if (slot == 3) { + if (unlikely(func > MAX_MCR_FUNC)) + goto error; + pvt->pci_mcr[func] = pdev; + } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) { + if (unlikely(func > MAX_CHAN_FUNC)) + goto error; + pvt->pci_ch[slot - 4][func] = pdev; + } else + goto error; + + debugf0("Associated fn %d.%d, dev = %p\n", + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev); + } + return 0; + +error: + i7core_printk(KERN_ERR, "Device %d, function %d " + "is out of the expected range\n", + slot, func); + return -EINVAL; } /**************************************************************************** @@ -1001,41 +1078,38 @@ static int __devinit i7core_probe(struct pci_dev *pdev, { struct mem_ctl_info *mci; struct i7core_pvt *pvt; - int num_channels; + int num_channels = 0; int num_csrows; - int num_dimms_per_channel; int dev_idx = id->driver_data; - if (dev_idx >= ARRAY_SIZE(i7core_devs)) + if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs))) return -EINVAL; - num_channels = NUM_CHANS; + /* get the pci devices we want to reserve for our use */ + if (unlikely(i7core_get_devices() < 0)) + return -ENODEV; - /* FIXME: FAKE data, since we currently don't now how to get this */ - num_dimms_per_channel = 4; - num_csrows = num_dimms_per_channel; + /* Check the number of active and not disabled channels */ + if (unlikely (i7core_get_active_channels(&num_channels)) < 0) + goto fail0; + + /* FIXME: we currently don't know the number of csrows */ + num_csrows = num_channels; /* allocate a new MC control structure */ mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0); - if (mci == NULL) + if (unlikely (!mci)) return -ENOMEM; debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); - /* 'get' the pci devices we want to reserve for our use */ - if (i7core_get_devices(mci, pdev)) - goto fail0; - mci->dev = &pdev->dev; /* record ptr to the generic device */ pvt = mci->pvt_info; - -// pvt->system_address = pdev; /* Record this device in our private */ -// pvt->maxch = num_channels; -// pvt->maxdimmperch = num_dimms_per_channel; + memset(pvt, 0, sizeof(*pvt)); mci->mc_idx = 0; - mci->mtype_cap = MEM_FLAG_FB_DDR2; /* FIXME: it uses DDR3 */ + mci->mtype_cap = MEM_FLAG_DDR3; /* FIXME: how to handle RDDR3? */ mci->edac_ctl_cap = EDAC_FLAG_NONE; mci->edac_cap = EDAC_FLAG_NONE; mci->mod_name = "i7core_edac.c"; @@ -1044,12 +1118,18 @@ static int __devinit i7core_probe(struct pci_dev *pdev, mci->dev_name = pci_name(pdev); mci->ctl_page_to_phys = NULL; mci->mc_driver_sysfs_attributes = i7core_inj_attrs; - /* Set the function pointer to an actual operation function */ mci->edac_check = i7core_check_error; + /* Store pci devices at mci for faster access */ + if (unlikely (mci_bind_devs(mci)) < 0) + goto fail1; + + /* Get dimm basic config */ + get_dimm_config(mci); + /* add this new MC control structure to EDAC's list of MCs */ - if (edac_mc_add_mc(mci)) { + if (unlikely(edac_mc_add_mc(mci)) < 0) { debugf0("MC: " __FILE__ ": %s(): failed edac_mc_add_mc()\n", __func__); /* FIXME: perhaps some code should go here that disables error @@ -1060,7 +1140,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, /* allocating generic PCI control info */ i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR); - if (!i7core_pci) { + if (unlikely (!i7core_pci)) { printk(KERN_WARNING "%s(): Unable to create PCI control\n", __func__); @@ -1070,15 +1150,14 @@ static int __devinit i7core_probe(struct pci_dev *pdev, } /* Default error mask is any memory */ - pvt->inject.channel = -1; + pvt->inject.channel = 0; pvt->inject.dimm = -1; pvt->inject.rank = -1; pvt->inject.bank = -1; pvt->inject.page = -1; pvt->inject.col = -1; - /* Get dimm basic config */ - get_dimm_config(mci); + i7core_printk(KERN_INFO, "Driver loaded.\n"); return 0; From 1c6fed808f1ccd0804786e87f6b2c907dcd730fa Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:30 -0300 Subject: [PATCH 10/83] i7core_edac: Properly fill struct csrow_info Thanks-to: Aristeu Rozanski for part of the code Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 48 ++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index bfa462f6fa0e..556a150e645b 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -348,13 +348,17 @@ static int i7core_get_active_channels(int *channels) (*channels)++; } + debugf0("Number of active channels: %d\n", *channels); + return 0; } static int get_dimm_config(struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; - int i; + struct csrow_info *csr; + int i, csrow = 0; + enum edac_type mode; if (!pvt->pci_mcr[0]) return -ENODEV; @@ -365,7 +369,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) pci_read_config_dword(pvt->pci_mcr[0], MC_STATUS, &pvt->info.mc_status); pci_read_config_dword(pvt->pci_mcr[0], MC_MAX_DOD, - &pvt->info.max_dod); + &pvt->info.max_dod); pci_read_config_dword(pvt->pci_mcr[0], MC_CHANNEL_MAPPER, &pvt->info.ch_map); @@ -373,10 +377,16 @@ static int get_dimm_config(struct mem_ctl_info *mci) pvt->info.mc_control, pvt->info.mc_status, pvt->info.max_dod, pvt->info.ch_map); - if (ECC_ENABLED(pvt)) + if (ECC_ENABLED(pvt)) { debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt)?8:4); - else + if (ECCx8(pvt)) + mode = EDAC_S8ECD8ED; + else + mode = EDAC_S4ECD4ED; + } else { debugf0("ECC disabled\n"); + mode = EDAC_NONE; + } /* FIXME: need to handle the error codes */ debugf0("DOD Maximum limits: DIMMS: %d, %d-ranked, %d-banked\n", @@ -411,13 +421,31 @@ static int get_dimm_config(struct mem_ctl_info *mci) else pvt->channel[i].dimms = 2; - debugf0("Ch%d (0x%08x): rd ch %d, wr ch %d, " - "%d ranks, %d %cDIMMs\n", - i, data, - RDLCH(pvt->info.ch_map, i), - WRLCH(pvt->info.ch_map, i), + debugf0("Ch%d phy rd%d, wr%d (0x%08x): " + "%d ranks, %d %cDIMMs, offset = %d\n", + i, + RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i), + data, pvt->channel[i].ranks, pvt->channel[i].dimms, - (data & REGISTERED_DIMM)? 'R' : 'U' ); + (data & REGISTERED_DIMM)? 'R' : 'U', + RANKOFFSET(data)); + + csr = &mci->csrows[csrow]; + csr->first_page = 0; + csr->last_page = 0; + csr->page_mask = 0; + csr->nr_pages = 0; + csr->grain = 0; + csr->csrow_idx = csrow; + csr->dtype = DEV_X8; /* FIXME: check this */ + + if (data & REGISTERED_DIMM) + csr->mtype = MEM_RDDR3; + else + csr->mtype = MEM_DDR3; + csr->edac_mode = mode; + + csrow++; } return 0; From b7c761512c5412eb30be567a0640060cccfc372f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:30 -0300 Subject: [PATCH 11/83] i7core_edac: Improve error handling Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 556a150e645b..62ae472c4e27 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -328,8 +328,10 @@ static int i7core_get_active_channels(int *channels) } } - if (!pdev) + if (!pdev) { + i7core_printk(KERN_ERR, "Couldn't find fn 3.0!!!\n"); return -ENODEV; + } /* Device 3 function 0 reads */ pci_read_config_dword(pdev, MC_STATUS, &status); @@ -1109,16 +1111,19 @@ static int __devinit i7core_probe(struct pci_dev *pdev, int num_channels = 0; int num_csrows; int dev_idx = id->driver_data; + int rc; if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs))) return -EINVAL; /* get the pci devices we want to reserve for our use */ - if (unlikely(i7core_get_devices() < 0)) - return -ENODEV; + rc = i7core_get_devices(); + if (unlikely(rc < 0)) + return rc; /* Check the number of active and not disabled channels */ - if (unlikely (i7core_get_active_channels(&num_channels)) < 0) + rc = i7core_get_active_channels(&num_channels); + if (unlikely (rc < 0)) goto fail0; /* FIXME: we currently don't know the number of csrows */ @@ -1126,8 +1131,10 @@ static int __devinit i7core_probe(struct pci_dev *pdev, /* allocate a new MC control structure */ mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0); - if (unlikely (!mci)) - return -ENOMEM; + if (unlikely (!mci)) { + rc = -ENOMEM; + goto fail0; + } debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); @@ -1150,19 +1157,22 @@ static int __devinit i7core_probe(struct pci_dev *pdev, mci->edac_check = i7core_check_error; /* Store pci devices at mci for faster access */ - if (unlikely (mci_bind_devs(mci)) < 0) + rc = mci_bind_devs(mci); + if (unlikely (rc < 0)) goto fail1; /* Get dimm basic config */ get_dimm_config(mci); /* add this new MC control structure to EDAC's list of MCs */ - if (unlikely(edac_mc_add_mc(mci)) < 0) { + if (unlikely(edac_mc_add_mc(mci))) { debugf0("MC: " __FILE__ ": %s(): failed edac_mc_add_mc()\n", __func__); /* FIXME: perhaps some code should go here that disables error * reporting if we just enabled it */ + + rc = -EINVAL; goto fail1; } @@ -1190,11 +1200,11 @@ static int __devinit i7core_probe(struct pci_dev *pdev, return 0; fail1: - i7core_put_devices(); + edac_mc_free(mci); fail0: - edac_mc_free(mci); - return -ENODEV; + i7core_put_devices(); + return rc; } /* From 7dd6953c5fecc44d264710e1fa158d0038215b63 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:30 -0300 Subject: [PATCH 12/83] i7core_edac: Add more information about each active dimm Thanks-to: Aristeu Rozanski for part of the code Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 43 ++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 62ae472c4e27..a6e798349e93 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -359,21 +359,18 @@ static int get_dimm_config(struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; struct csrow_info *csr; - int i, csrow = 0; + struct pci_dev *pdev = pvt->pci_mcr[0]; + int i, j, csrow = 0; enum edac_type mode; - if (!pvt->pci_mcr[0]) + if (!pdev) return -ENODEV; /* Device 3 function 0 reads */ - pci_read_config_dword(pvt->pci_mcr[0], MC_CONTROL, - &pvt->info.mc_control); - pci_read_config_dword(pvt->pci_mcr[0], MC_STATUS, - &pvt->info.mc_status); - pci_read_config_dword(pvt->pci_mcr[0], MC_MAX_DOD, - &pvt->info.max_dod); - pci_read_config_dword(pvt->pci_mcr[0], MC_CHANNEL_MAPPER, - &pvt->info.ch_map); + pci_read_config_dword(pdev, MC_CONTROL, &pvt->info.mc_control); + pci_read_config_dword(pdev, MC_STATUS, &pvt->info.mc_status); + pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod); + pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map); debugf0("MC control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n", pvt->info.mc_control, pvt->info.mc_status, @@ -399,7 +396,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) debugf0("Memory channel configuration:\n"); for (i = 0; i < NUM_CHANS; i++) { - u32 data; + u32 data, value[8]; if (!CH_ACTIVE(pvt, i)) { debugf0("Channel %i is not active\n", i); @@ -424,13 +421,33 @@ static int get_dimm_config(struct mem_ctl_info *mci) pvt->channel[i].dimms = 2; debugf0("Ch%d phy rd%d, wr%d (0x%08x): " - "%d ranks, %d %cDIMMs, offset = %d\n", + "%d ranks, %d %cDIMMs, offset = %d\n\t" + "present: %i, numbank: %#x, numrank: %#x, " + "numrow: %#x, numcol: %#x\n", i, RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i), data, pvt->channel[i].ranks, pvt->channel[i].dimms, (data & REGISTERED_DIMM)? 'R' : 'U', - RANKOFFSET(data)); + RANKOFFSET(data), + DIMM_PRESENT(data), + NUMBANK(data), NUMRANK(data), + NUMROW(data), NUMCOL(data)); + + pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]); + pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]); + pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]); + pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]); + pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]); + pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]); + pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]); + pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]); + printk("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i); + for (j = 0; j < 8; j++) + printk("\t\t%#x\t%#x\t%#x\n", + (value[j] >> 27) & 0x1, + (value[j] >> 24) & 0x7, + (value[j] && ((1 << 24) - 1))); csr = &mci->csrows[csrow]; csr->first_page = 0; From 854d3349973a7c47bd989794037f526b74af20c4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:30 -0300 Subject: [PATCH 13/83] i7core_edac: Get more info about the memory DIMMs Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 166 +++++++++++++++++++++++-------------- 1 file changed, 105 insertions(+), 61 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index a6e798349e93..483cca2e543b 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -109,14 +109,14 @@ #define RANKOFFSET(x) ((x & RANKOFFSET_MASK) >> 10) #define DIMM_PRESENT_MASK (1 << 9) #define DIMM_PRESENT(x) (((x) & DIMM_PRESENT_MASK) >> 9) - #define NUMBANK_MASK ((1 << 8) | (1 << 7)) - #define NUMBANK(x) (((x) & NUMBANK_MASK) >> 7) - #define NUMRANK_MASK ((1 << 6) | (1 << 5)) - #define NUMRANK(x) (((x) & NUMRANK_MASK) >> 5) - #define NUMROW_MASK ((1 << 4) | (1 << 3)) - #define NUMROW(x) (((x) & NUMROW_MASK) >> 3) - #define NUMCOL_MASK 3 - #define NUMCOL(x) ((x) & NUMCOL_MASK) + #define MC_DOD_NUMBANK_MASK ((1 << 8) | (1 << 7)) + #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7) + #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5)) + #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5) + #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3)) + #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 3) + #define MC_DOD_NUMCOL_MASK 3 + #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK) #define MC_RANK_PRESENT 0x7c @@ -268,41 +268,41 @@ static struct edac_pci_ctl_info *i7core_pci; #define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch)) /* MC_MAX_DOD read functions */ -static inline int maxnumdimms(struct i7core_pvt *pvt) +static inline int numdimms(u32 dimms) { - return (pvt->info.max_dod & 0x3) + 1; + return (dimms & 0x3) + 1; } -static inline int maxnumrank(struct i7core_pvt *pvt) +static inline int numrank(u32 rank) { static int ranks[4] = { 1, 2, 4, -EINVAL }; - return ranks[(pvt->info.max_dod >> 2) & 0x3]; + return ranks[rank & 0x3]; } -static inline int maxnumbank(struct i7core_pvt *pvt) +static inline int numbank(u32 bank) { static int banks[4] = { 4, 8, 16, -EINVAL }; - return banks[(pvt->info.max_dod >> 4) & 0x3]; + return banks[bank & 0x3]; } -static inline int maxnumrow(struct i7core_pvt *pvt) +static inline int numrow(u32 row) { static int rows[8] = { 1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, -EINVAL, -EINVAL, -EINVAL, }; - return rows[((pvt->info.max_dod >> 6) & 0x7)]; + return rows[row & 0x7]; } -static inline int maxnumcol(struct i7core_pvt *pvt) +static inline int numcol(u32 col) { static int cols[8] = { 1 << 10, 1 << 11, 1 << 12, -EINVAL, }; - return cols[((pvt->info.max_dod >> 9) & 0x3) << 12]; + return cols[col & 0x3]; } @@ -359,10 +359,13 @@ static int get_dimm_config(struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; struct csrow_info *csr; - struct pci_dev *pdev = pvt->pci_mcr[0]; + struct pci_dev *pdev; int i, j, csrow = 0; enum edac_type mode; + enum mem_type mtype; + /* Get data from the MC register, function 0 */ + pdev = pvt->pci_mcr[0]; if (!pdev) return -ENODEV; @@ -388,15 +391,18 @@ static int get_dimm_config(struct mem_ctl_info *mci) } /* FIXME: need to handle the error codes */ - debugf0("DOD Maximum limits: DIMMS: %d, %d-ranked, %d-banked\n", - maxnumdimms(pvt), maxnumrank(pvt), maxnumbank(pvt)); - debugf0("DOD Maximum rows x colums = 0x%x x 0x%x\n", - maxnumrow(pvt), maxnumcol(pvt)); + debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked\n", + numdimms(pvt->info.max_dod), + numrank(pvt->info.max_dod >> 2), + numbank(pvt->info.max_dod >> 4)); + debugf0("DOD Max rows x colums = 0x%x x 0x%x\n", + numrow(pvt->info.max_dod >> 6), + numcol(pvt->info.max_dod >> 9)); debugf0("Memory channel configuration:\n"); for (i = 0; i < NUM_CHANS; i++) { - u32 data, value[8]; + u32 data, dimm_dod[3], value[8]; if (!CH_ACTIVE(pvt, i)) { debugf0("Channel %i is not active\n", i); @@ -413,58 +419,96 @@ static int get_dimm_config(struct mem_ctl_info *mci) pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT)? 4 : 2; + if (data & REGISTERED_DIMM) + mtype = MEM_RDDR3; + else + mtype = MEM_DDR3; +#if 0 if (data & THREE_DIMMS_PRESENT) pvt->channel[i].dimms = 3; else if (data & SINGLE_QUAD_RANK_PRESENT) pvt->channel[i].dimms = 1; else pvt->channel[i].dimms = 2; +#endif + + /* Devices 4-6 function 1 */ + pci_read_config_dword(pvt->pci_ch[i][1], + MC_DOD_CH_DIMM0, &dimm_dod[0]); + pci_read_config_dword(pvt->pci_ch[i][1], + MC_DOD_CH_DIMM1, &dimm_dod[1]); + pci_read_config_dword(pvt->pci_ch[i][1], + MC_DOD_CH_DIMM2, &dimm_dod[2]); debugf0("Ch%d phy rd%d, wr%d (0x%08x): " - "%d ranks, %d %cDIMMs, offset = %d\n\t" - "present: %i, numbank: %#x, numrank: %#x, " - "numrow: %#x, numcol: %#x\n", + "%d ranks, %cDIMMs\n", i, RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i), data, - pvt->channel[i].ranks, pvt->channel[i].dimms, - (data & REGISTERED_DIMM)? 'R' : 'U', - RANKOFFSET(data), - DIMM_PRESENT(data), - NUMBANK(data), NUMRANK(data), - NUMROW(data), NUMCOL(data)); + pvt->channel[i].ranks, + (data & REGISTERED_DIMM)? 'R' : 'U'); - pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]); - pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]); - pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]); - pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]); - pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]); - pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]); - pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]); - pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]); - printk("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i); - for (j = 0; j < 8; j++) - printk("\t\t%#x\t%#x\t%#x\n", - (value[j] >> 27) & 0x1, - (value[j] >> 24) & 0x7, - (value[j] && ((1 << 24) - 1))); + for (j = 0; j < 3; j++) { + u32 banks, ranks, rows, cols; - csr = &mci->csrows[csrow]; - csr->first_page = 0; - csr->last_page = 0; - csr->page_mask = 0; - csr->nr_pages = 0; - csr->grain = 0; - csr->csrow_idx = csrow; - csr->dtype = DEV_X8; /* FIXME: check this */ + if (!DIMM_PRESENT(dimm_dod[j])) + continue; - if (data & REGISTERED_DIMM) - csr->mtype = MEM_RDDR3; - else - csr->mtype = MEM_DDR3; - csr->edac_mode = mode; + banks = numbank(MC_DOD_NUMBANK(dimm_dod[j])); + ranks = numrank(MC_DOD_NUMRANK(dimm_dod[j])); + rows = numrow(MC_DOD_NUMROW(dimm_dod[j])); + cols = numcol(MC_DOD_NUMCOL(dimm_dod[j])); - csrow++; + pvt->channel[i].dimms++; + + debugf0("\tdimm %d offset: %x, numbank: %#x, " + "numrank: %#x, numrow: %#x, numcol: %#x\n", + j, + RANKOFFSET(dimm_dod[j]), + banks, ranks, rows, cols); + + csr = &mci->csrows[csrow]; + csr->first_page = 0; + csr->last_page = 0; + csr->page_mask = 0; + csr->nr_pages = 0; + csr->grain = 0; + csr->csrow_idx = csrow; + + switch (banks) { + case 4: + csr->dtype = DEV_X4; + break; + case 8: + csr->dtype = DEV_X8; + break; + case 16: + csr->dtype = DEV_X16; + break; + default: + csr->dtype = DEV_UNKNOWN; + } + + csr->edac_mode = mode; + csr->mtype = mtype; + + csrow++; + } + + pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]); + pci_read_config_dword(pdev, MC_SAG_CH_1, &value[1]); + pci_read_config_dword(pdev, MC_SAG_CH_2, &value[2]); + pci_read_config_dword(pdev, MC_SAG_CH_3, &value[3]); + pci_read_config_dword(pdev, MC_SAG_CH_4, &value[4]); + pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]); + pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]); + pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]); + printk("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i); + for (j = 0; j < 8; j++) + printk("\t\t%#x\t%#x\t%#x\n", + (value[j] >> 27) & 0x1, + (value[j] >> 24) & 0x7, + (value[j] && ((1 << 24) - 1))); } return 0; From 5566cb7c91ba4ff4447278bb27896b4a2bb7d18a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:31 -0300 Subject: [PATCH 14/83] i7core_edac: Memory info fixes and preparation for properly filling cswrow data Now, memory size is properly displayed: EDAC i7core: DOD Max limits: DIMMS: 2, 1-ranked, 8-banked EDAC i7core: DOD Max rows x colums = 0x4000 x 0x400 EDAC i7core: Memory channel configuration: EDAC i7core: Ch0 phy rd0, wr0 (0x063f7c31): 2 ranks, UDIMMs EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8, numrank: 1, numrow: 0x4000, numcol: 0x400 EDAC i7core: dimm 1 (0x00001288) 1024 Mb offset: 4, numbank: 8, numrank: 1, numrow: 0x4000, numcol: 0x400 EDAC i7core: Ch1 phy rd1, wr1 (0x063f7c31): 2 ranks, UDIMMs EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8, numrank: 1, numrow: 0x4000, numcol: 0x400 EDAC i7core: Ch2 phy rd3, wr3 (0x063f7c31): 2 ranks, UDIMMs EDAC i7core: dimm 0 (0x00000288) 1024 Mb offset: 0, numbank: 8, numrank: 1, numrow: 0x4000, numcol: 0x400 Still, as the way to retrieve csrows info is not known, it does a mapping of what's available to csrows basic unit at edac core. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 483cca2e543b..772219fa10be 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -113,8 +113,8 @@ #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7) #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5)) #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5) - #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3)) - #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 3) + #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3)| (1 << 2)) + #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2) #define MC_DOD_NUMCOL_MASK 3 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK) @@ -361,6 +361,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) struct csrow_info *csr; struct pci_dev *pdev; int i, j, csrow = 0; + unsigned long last_page = 0; enum edac_type mode; enum mem_type mtype; @@ -380,7 +381,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) pvt->info.max_dod, pvt->info.ch_map); if (ECC_ENABLED(pvt)) { - debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt)?8:4); + debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ?8:4); if (ECCx8(pvt)) mode = EDAC_S8ECD8ED; else @@ -450,6 +451,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) for (j = 0; j < 3; j++) { u32 banks, ranks, rows, cols; + u32 size, npages; if (!DIMM_PRESENT(dimm_dod[j])) continue; @@ -459,19 +461,27 @@ static int get_dimm_config(struct mem_ctl_info *mci) rows = numrow(MC_DOD_NUMROW(dimm_dod[j])); cols = numcol(MC_DOD_NUMCOL(dimm_dod[j])); + /* DDR3 has 8 I/O banks */ + size = (rows * cols * banks * ranks) >> (20 - 3); + pvt->channel[i].dimms++; - debugf0("\tdimm %d offset: %x, numbank: %#x, " - "numrank: %#x, numrow: %#x, numcol: %#x\n", - j, + debugf0("\tdimm %d (0x%08x) %d Mb offset: %x, " + "numbank: %d,\n\t\t" + "numrank: %d, numrow: %#x, numcol: %#x\n", + j, dimm_dod[j], size, RANKOFFSET(dimm_dod[j]), banks, ranks, rows, cols); + npages = cols * rows; /* FIXME */ + csr = &mci->csrows[csrow]; - csr->first_page = 0; - csr->last_page = 0; + csr->first_page = last_page + 1; + last_page += npages; + csr->last_page = last_page; + csr->nr_pages = npages; + csr->page_mask = 0; - csr->nr_pages = 0; csr->grain = 0; csr->csrow_idx = csrow; From eb94fc402f1592dfe847b245d9109c11a99a2ea1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:31 -0300 Subject: [PATCH 15/83] i7core_edac: fill csrows edac sysfs info csrows is still fake, since we can't identify its representation with Nehalem registers. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 66 +++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 772219fa10be..e0a3b217c52b 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -309,25 +309,33 @@ static inline int numcol(u32 col) /**************************************************************************** Memory check routines ****************************************************************************/ -static int i7core_get_active_channels(int *channels) +static struct pci_dev *get_pdev_slot_func(int slot, int func) { - struct pci_dev *pdev = NULL; int i; - u32 status, control; - - *channels = 0; for (i = 0; i < N_DEVS; i++) { if (!pci_devs[i].pdev) continue; - if (PCI_SLOT(pci_devs[i].pdev->devfn) == 3 && - PCI_FUNC(pci_devs[i].pdev->devfn) == 0) { - pdev = pci_devs[i].pdev; - break; + if (PCI_SLOT(pci_devs[i].pdev->devfn) == slot && + PCI_FUNC(pci_devs[i].pdev->devfn) == func) { + return pci_devs[i].pdev; } } + return NULL; +} + +static int i7core_get_active_channels(int *channels, int *csrows) +{ + struct pci_dev *pdev = NULL; + int i, j; + u32 status, control; + + *channels = 0; + *csrows = 0; + + pdev = get_pdev_slot_func(3, 0); if (!pdev) { i7core_printk(KERN_ERR, "Couldn't find fn 3.0!!!\n"); return -ENODEV; @@ -338,6 +346,7 @@ static int i7core_get_active_channels(int *channels) pci_read_config_dword(pdev, MC_CONTROL, &control); for (i = 0; i < NUM_CHANS; i++) { + u32 dimm_dod[3]; /* Check if the channel is active */ if (!(control & (1 << (8 + i)))) continue; @@ -347,7 +356,27 @@ static int i7core_get_active_channels(int *channels) continue; } + pdev = get_pdev_slot_func(i + 4, 1); + if (!pdev) { + i7core_printk(KERN_ERR, "Couldn't find fn %d.%d!!!\n", + i + 4, 1); + return -ENODEV; + } + /* Devices 4-6 function 1 */ + pci_read_config_dword(pdev, + MC_DOD_CH_DIMM0, &dimm_dod[0]); + pci_read_config_dword(pdev, + MC_DOD_CH_DIMM1, &dimm_dod[1]); + pci_read_config_dword(pdev, + MC_DOD_CH_DIMM2, &dimm_dod[2]); + (*channels)++; + + for (j = 0; j < 3; j++) { + if (!DIMM_PRESENT(dimm_dod[j])) + continue; + (*csrows)++; + } } debugf0("Number of active channels: %d\n", *channels); @@ -473,7 +502,11 @@ static int get_dimm_config(struct mem_ctl_info *mci) RANKOFFSET(dimm_dod[j]), banks, ranks, rows, cols); - npages = cols * rows; /* FIXME */ +#if PAGE_SHIFT > 20 + npages = size >> (PAGE_SHIFT - 20); +#else + npages = size << (20 - PAGE_SHIFT); +#endif csr = &mci->csrows[csrow]; csr->first_page = last_page + 1; @@ -482,8 +515,12 @@ static int get_dimm_config(struct mem_ctl_info *mci) csr->nr_pages = npages; csr->page_mask = 0; - csr->grain = 0; + csr->grain = 8; csr->csrow_idx = csrow; + csr->nr_channels = 1; + + csr->channels[0].chan_idx = i; + csr->channels[0].ce_count = 0; switch (banks) { case 4: @@ -1179,7 +1216,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, { struct mem_ctl_info *mci; struct i7core_pvt *pvt; - int num_channels = 0; + int num_channels; int num_csrows; int dev_idx = id->driver_data; int rc; @@ -1193,13 +1230,10 @@ static int __devinit i7core_probe(struct pci_dev *pdev, return rc; /* Check the number of active and not disabled channels */ - rc = i7core_get_active_channels(&num_channels); + rc = i7core_get_active_channels(&num_channels, &num_csrows); if (unlikely (rc < 0)) goto fail0; - /* FIXME: we currently don't know the number of csrows */ - num_csrows = num_channels; - /* allocate a new MC control structure */ mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0); if (unlikely (!mci)) { From 41fcb7feed70d8076f1591664314ca172fcdff7b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Jun 2009 22:48:31 -0300 Subject: [PATCH 16/83] i7core_edac: CodingStyle fixes Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 59 +++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index e0a3b217c52b..914914759690 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -113,7 +113,7 @@ #define MC_DOD_NUMBANK(x) (((x) & MC_DOD_NUMBANK_MASK) >> 7) #define MC_DOD_NUMRANK_MASK ((1 << 6) | (1 << 5)) #define MC_DOD_NUMRANK(x) (((x) & MC_DOD_NUMRANK_MASK) >> 5) - #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3)| (1 << 2)) + #define MC_DOD_NUMROW_MASK ((1 << 4) | (1 << 3) | (1 << 2)) #define MC_DOD_NUMROW(x) (((x) & MC_DOD_NUMROW_MASK) >> 2) #define MC_DOD_NUMCOL_MASK 3 #define MC_DOD_NUMCOL(x) ((x) & MC_DOD_NUMCOL_MASK) @@ -352,9 +352,8 @@ static int i7core_get_active_channels(int *channels, int *csrows) continue; /* Check if the channel is disabled */ - if (status & (1 << i)) { + if (status & (1 << i)) continue; - } pdev = get_pdev_slot_func(i + 4, 1); if (!pdev) { @@ -410,7 +409,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) pvt->info.max_dod, pvt->info.ch_map); if (ECC_ENABLED(pvt)) { - debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ?8:4); + debugf0("ECC enabled with x%d SDCC\n", ECCx8(pvt) ? 8 : 4); if (ECCx8(pvt)) mode = EDAC_S8ECD8ED; else @@ -447,7 +446,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) pci_read_config_dword(pvt->pci_ch[i][0], MC_CHANNEL_DIMM_INIT_PARAMS, &data); - pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT)? 4 : 2; + pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ? 4 : 2; if (data & REGISTERED_DIMM) mtype = MEM_RDDR3; @@ -476,7 +475,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i), data, pvt->channel[i].ranks, - (data & REGISTERED_DIMM)? 'R' : 'U'); + (data & REGISTERED_DIMM) ? 'R' : 'U'); for (j = 0; j < 3; j++) { u32 banks, ranks, rows, cols; @@ -550,9 +549,9 @@ static int get_dimm_config(struct mem_ctl_info *mci) pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]); pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]); pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]); - printk("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i); + debugf0("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i); for (j = 0; j < 8; j++) - printk("\t\t%#x\t%#x\t%#x\n", + debugf0("\t\t%#x\t%#x\t%#x\n", (value[j] >> 27) & 0x1, (value[j] >> 24) & 0x7, (value[j] && ((1 << 24) - 1))); @@ -602,7 +601,7 @@ static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci, int rc; if (pvt->inject.enable) - disable_inject(mci); + disable_inject(mci); rc = strict_strtoul(data, 10, &value); if ((rc < 0) || (value > 3)) @@ -635,7 +634,7 @@ static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci, int rc; if (pvt->inject.enable) - disable_inject(mci); + disable_inject(mci); rc = strict_strtoul(data, 10, &value); if ((rc < 0) || (value > 7)) @@ -670,7 +669,7 @@ static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci, int rc; if (pvt->inject.enable) - disable_inject(mci); + disable_inject(mci); rc = strict_strtoul(data, 10, &value); if (rc < 0) @@ -706,7 +705,7 @@ static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci, int rc; if (pvt->inject.enable) - disable_inject(mci); + disable_inject(mci); do { cmd = strsep((char **) &data, ":"); @@ -716,7 +715,7 @@ static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci, if (!val) return cmd - data; - if (!strcasecmp(val,"any")) + if (!strcasecmp(val, "any")) value = -1; else { rc = strict_strtol(val, 10, &value); @@ -724,33 +723,33 @@ static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci, return cmd - data; } - if (!strcasecmp(cmd,"channel")) { + if (!strcasecmp(cmd, "channel")) { if (value < 3) pvt->inject.channel = value; else return cmd - data; - } else if (!strcasecmp(cmd,"dimm")) { + } else if (!strcasecmp(cmd, "dimm")) { if (value < 4) pvt->inject.dimm = value; else return cmd - data; - } else if (!strcasecmp(cmd,"rank")) { + } else if (!strcasecmp(cmd, "rank")) { if (value < 4) pvt->inject.rank = value; else return cmd - data; - } else if (!strcasecmp(cmd,"bank")) { + } else if (!strcasecmp(cmd, "bank")) { if (value < 4) pvt->inject.bank = value; else return cmd - data; - } else if (!strcasecmp(cmd,"page")) { + } else if (!strcasecmp(cmd, "page")) { if (value <= 0xffff) pvt->inject.page = value; else return cmd - data; - } else if (!strcasecmp(cmd,"col") || - !strcasecmp(cmd,"column")) { + } else if (!strcasecmp(cmd, "col") || + !strcasecmp(cmd, "column")) { if (value <= 0x3fff) pvt->inject.col = value; else @@ -923,7 +922,8 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ERROR_MASK, injectmask); - debugf0("Error inject addr match 0x%016llx, ecc 0x%08x, inject 0x%08x\n", + debugf0("Error inject addr match 0x%016llx, ecc 0x%08x," + " inject 0x%08x\n", mask, pvt->inject.eccmask, injectmask); @@ -1048,7 +1048,7 @@ static int i7core_get_devices(void) "Device not found: PCI ID %04x:%04x " "(dev %d, func %d)\n", PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, - pci_devs[i].dev,pci_devs[i].func); + pci_devs[i].dev, pci_devs[i].func); /* Dev 3 function 2 only exists on chips with RDIMMs */ if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2)) @@ -1231,12 +1231,12 @@ static int __devinit i7core_probe(struct pci_dev *pdev, /* Check the number of active and not disabled channels */ rc = i7core_get_active_channels(&num_channels, &num_csrows); - if (unlikely (rc < 0)) + if (unlikely(rc < 0)) goto fail0; /* allocate a new MC control structure */ mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0); - if (unlikely (!mci)) { + if (unlikely(!mci)) { rc = -ENOMEM; goto fail0; } @@ -1249,7 +1249,12 @@ static int __devinit i7core_probe(struct pci_dev *pdev, memset(pvt, 0, sizeof(*pvt)); mci->mc_idx = 0; - mci->mtype_cap = MEM_FLAG_DDR3; /* FIXME: how to handle RDDR3? */ + /* + * FIXME: how to handle RDDR3 at MCI level? It is possible to have + * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different + * memory channels + */ + mci->mtype_cap = MEM_FLAG_DDR3; mci->edac_ctl_cap = EDAC_FLAG_NONE; mci->edac_cap = EDAC_FLAG_NONE; mci->mod_name = "i7core_edac.c"; @@ -1263,7 +1268,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, /* Store pci devices at mci for faster access */ rc = mci_bind_devs(mci); - if (unlikely (rc < 0)) + if (unlikely(rc < 0)) goto fail1; /* Get dimm basic config */ @@ -1283,7 +1288,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, /* allocating generic PCI control info */ i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR); - if (unlikely (!i7core_pci)) { + if (unlikely(!i7core_pci)) { printk(KERN_WARNING "%s(): Unable to create PCI control\n", __func__); From 696e409dbd1ce325129c5030267365619364dfa0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 23 Jul 2009 06:57:45 -0300 Subject: [PATCH 17/83] edac_mce: Add an interface driver to report mce errors via edac edac_mce module is an interface module that gets mcelog data and forwards to any registered edac module that expects to receive data via mce. Signed-off-by: Mauro Carvalho Chehab --- arch/x86/kernel/cpu/mcheck/mce.c | 10 ++++++ drivers/edac/Kconfig | 8 ++++- drivers/edac/Makefile | 1 + drivers/edac/edac_mce.c | 58 ++++++++++++++++++++++++++++++++ include/linux/edac_mce.h | 31 +++++++++++++++++ 5 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 drivers/edac/edac_mce.c create mode 100644 include/linux/edac_mce.h diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index 8a6f0afa767e..6585ff07ddf5 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -168,6 +169,15 @@ void mce_log(struct mce *mce) for (;;) { entry = rcu_dereference_check_mce(mcelog.next); for (;;) { + /* + * If edac_mce is enabled, it will check the error type + * and will process it, if it is a known error. + * Otherwise, the error will be sent through mcelog + * interface + */ + if (edac_mce_parse(mce)) + return; + /* * When the buffer fills up discard new entries. * Assume that the earlier errors are the more diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 391ddbfb2a34..5b7fbc5aec87 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -69,6 +69,9 @@ config EDAC_MM_EDAC occurred so that a particular failing memory module can be replaced. If unsure, select 'Y'. +config EDAC_MCE + tristate + config EDAC_AMD64 tristate "AMD64 (Opteron, Athlon64) K8, F10h, F11h" depends on EDAC_MM_EDAC && K8_NB && X86_64 && PCI && EDAC_DECODE_MCE @@ -169,9 +172,12 @@ config EDAC_I5400 config EDAC_I7CORE tristate "Intel i7 Core (Nehalem) processors" depends on EDAC_MM_EDAC && PCI && X86 + select EDAC_MCE help Support for error detection and correction the Intel - i7 Core (Nehalem) Integrated Memory Controller + i7 Core (Nehalem) Integrated Memory Controller that exists on + newer processors like i7 Core, i7 Core Extreme, Xeon 35xx + and Xeon 55xx processors. config EDAC_I82860 tristate "Intel 82860" diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index b9996195b233..ca6b1bb24ccc 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_EDAC) := edac_stub.o obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o +obj-$(CONFIG_EDAC_MCE) += edac_mce.o edac_core-objs := edac_mc.o edac_device.o edac_mc_sysfs.o edac_pci_sysfs.o edac_core-objs += edac_module.o edac_device_sysfs.o diff --git a/drivers/edac/edac_mce.c b/drivers/edac/edac_mce.c new file mode 100644 index 000000000000..b1efa8e51921 --- /dev/null +++ b/drivers/edac/edac_mce.c @@ -0,0 +1,58 @@ +/* Provides edac interface to mcelog events + * + * This file may be distributed under the terms of the + * GNU General Public License version 2. + * + * Copyright (c) 2009 by: + * Mauro Carvalho Chehab + * + * Red Hat Inc. http://www.redhat.com + */ + +#include +#include +#include + +int edac_mce_enabled; +EXPORT_SYMBOL_GPL(edac_mce_enabled); + + +/* + * Extension interface + */ + +static LIST_HEAD(edac_mce_list); +static DEFINE_MUTEX(edac_mce_lock); + +int edac_mce_register(struct edac_mce *edac_mce) +{ + mutex_lock(&edac_mce_lock); + list_add_tail(&edac_mce->list, &edac_mce_list); + mutex_unlock(&edac_mce_lock); + return 0; +} +EXPORT_SYMBOL(edac_mce_register); + +void edac_mce_unregister(struct edac_mce *edac_mce) +{ + mutex_lock(&edac_mce_lock); + list_del(&edac_mce->list); + mutex_unlock(&edac_mce_lock); +} +EXPORT_SYMBOL(edac_mce_unregister); + + + +int edac_mce_queue(struct mce *mce) +{ + struct edac_mce *edac_mce; + + list_for_each_entry(edac_mce, &edac_mce_list, list) { + if (edac_mce->check_error(edac_mce->priv, mce)) + return 1; + } + + /* Nobody queued the error */ + return 0; +} +EXPORT_SYMBOL_GPL(edac_mce_queue); diff --git a/include/linux/edac_mce.h b/include/linux/edac_mce.h new file mode 100644 index 000000000000..f974fc035363 --- /dev/null +++ b/include/linux/edac_mce.h @@ -0,0 +1,31 @@ +/* Provides edac interface to mcelog events + * + * This file may be distributed under the terms of the + * GNU General Public License version 2. + * + * Copyright (c) 2009 by: + * Mauro Carvalho Chehab + * + * Red Hat Inc. http://www.redhat.com + */ + +#if defined(CONFIG_EDAC_MCE) || \ + (defined(CONFIG_EDAC_MCE_MODULE) && defined(MODULE)) + +#include +#include + +struct edac_mce { + struct list_head list; + + void *priv; + int (*check_error)(void *priv, struct mce *mce); +}; + +int edac_mce_register(struct edac_mce *edac_mce); +void edac_mce_unregister(struct edac_mce *edac_mce); +int edac_mce_parse(struct mce *mce); + +#else +#define edac_mce_parse(mce) (0) +#endif From 963c5ba35984c87963480031d1d7e2e556256ad7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 9 Jul 2009 22:04:30 -0300 Subject: [PATCH 18/83] edac/Kconfig: edac_mce can't be module Since mcelog is bool, edac_mce glue should also be bool, or otherwise will not work. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 5b7fbc5aec87..aedef7941b22 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -70,7 +70,7 @@ config EDAC_MM_EDAC replaced. If unsure, select 'Y'. config EDAC_MCE - tristate + bool config EDAC_AMD64 tristate "AMD64 (Opteron, Athlon64) K8, F10h, F11h" From d5381642ab01b084787925acdf26b5524d434476 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 9 Jul 2009 22:06:41 -0300 Subject: [PATCH 19/83] i7core_edac: Add edac_mce glue Adds a glue code to allow i7core to work with mcelog. With the glue, i7core registers itself on edac_mce. At mce, when an error is detected, it calls all registered drivers (in this case, i7core), for EDAC error handling. TODO: It currently just prints the MCE error log using about the same format as mce panic messages. The error message should be enhanced with mcelog userspace info and converted into the proper EDAC format, to feed the EDAC error counts. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_mce.c | 11 ++-- drivers/edac/i7core_edac.c | 111 ++++++++++++++++++++++++++++++++++++- 2 files changed, 116 insertions(+), 6 deletions(-) diff --git a/drivers/edac/edac_mce.c b/drivers/edac/edac_mce.c index b1efa8e51921..9ccdc5b140e7 100644 --- a/drivers/edac/edac_mce.c +++ b/drivers/edac/edac_mce.c @@ -41,9 +41,7 @@ void edac_mce_unregister(struct edac_mce *edac_mce) } EXPORT_SYMBOL(edac_mce_unregister); - - -int edac_mce_queue(struct mce *mce) +int edac_mce_parse(struct mce *mce) { struct edac_mce *edac_mce; @@ -55,4 +53,9 @@ int edac_mce_queue(struct mce *mce) /* Nobody queued the error */ return 0; } -EXPORT_SYMBOL_GPL(edac_mce_queue); +EXPORT_SYMBOL_GPL(edac_mce_parse); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); +MODULE_DESCRIPTION("EDAC Driver for mcelog captured errors"); diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 914914759690..3c7bb5f405f6 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include "edac_core.h" @@ -195,6 +197,11 @@ struct i7core_pvt { unsigned long ce_count[MAX_DIMMS]; /* ECC corrected errors counts per dimm */ int last_ce_count[MAX_DIMMS]; + /* mcelog glue */ + struct edac_mce edac_mce; + struct mce mce_entry[MCE_LOG_LEN]; + unsigned mce_count; + spinlock_t mce_lock; }; /* Device name and register DID (Device ID) */ @@ -900,7 +907,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ADDR_MATCH + 4, &rdmask2); - debugf0("Inject addr match write 0x%016llx, read: 0x%08x%08x\n", + debugf0("Inject addr match write 0x%016llx, read: 0x%08x 0x%08x\n", mask, rdmask1, rdmask2); #endif #endif @@ -1162,9 +1169,11 @@ static void check_mc_test_err(struct mem_ctl_info *mci) new1 = DIMM1_COR_ERR(rcv0); new0 = DIMM0_COR_ERR(rcv0); +#if 0 debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n", (pvt->ce_count_available ? "UPDATE" : "READ"), rcv1, rcv0, new0, new1, new2); +#endif /* Updates CE counters if it is not the first time here */ if (pvt->ce_count_available) { @@ -1195,15 +1204,96 @@ static void check_mc_test_err(struct mem_ctl_info *mci) pvt->last_ce_count[0] = new0; } +static void i7core_mce_output_error(struct mem_ctl_info *mci, + struct mce *m) +{ + debugf0("CPU %d: Machine Check Exception: %16Lx" + "Bank %d: %016Lx\n", + m->cpu, m->mcgstatus, m->bank, m->status); + if (m->ip) { + debugf0("RIP%s %02x:<%016Lx>\n", + !(m->mcgstatus & MCG_STATUS_EIPV) ? " !INEXACT!" : "", + m->cs, m->ip); + } + printk(KERN_EMERG "TSC %llx ", m->tsc); + if (m->addr) + printk("ADDR %llx ", m->addr); + if (m->misc) + printk("MISC %llx ", m->misc); + +#if 0 + snprintf(msg, sizeof(msg), + "%s (Branch=%d DRAM-Bank=%d Buffer ID = %d RDWR=%s " + "RAS=%d CAS=%d %s Err=0x%lx (%s))", + type, branch >> 1, bank, buf_id, rdwr_str(rdwr), ras, cas, + type, allErrors, error_name[errnum]); + + /* Call the helper to output message */ + edac_mc_handle_fbd_ue(mci, rank, channel, channel + 1, msg); +#endif +} + /* * i7core_check_error Retrieve and process errors reported by the * hardware. Called by the Core module. */ static void i7core_check_error(struct mem_ctl_info *mci) { + struct i7core_pvt *pvt = mci->pvt_info; + int i; + unsigned count = 0; + struct mce *m = NULL; + unsigned long flags; + + debugf0(__FILE__ ": %s()\n", __func__); + + /* Copy all mce errors into a temporary buffer */ + spin_lock_irqsave(&pvt->mce_lock, flags); + if (pvt->mce_count) { + m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC); + if (m) { + count = pvt->mce_count; + memcpy(m, &pvt->mce_entry, sizeof(*m) * count); + } + pvt->mce_count = 0; + } + spin_unlock_irqrestore(&pvt->mce_lock, flags); + + /* proccess mcelog errors */ + for (i = 0; i < count; i++) + i7core_mce_output_error(mci, &m[i]); + + kfree(m); + + /* check memory count errors */ check_mc_test_err(mci); } +/* + * i7core_mce_check_error Replicates mcelog routine to get errors + * This routine simply queues mcelog errors, and + * return. The error itself should be handled later + * by i7core_check_error. + */ +static int i7core_mce_check_error(void *priv, struct mce *mce) +{ + struct i7core_pvt *pvt = priv; + unsigned long flags; + + debugf0(__FILE__ ": %s()\n", __func__); + + spin_lock_irqsave(&pvt->mce_lock, flags); + if (pvt->mce_count < MCE_LOG_LEN) { + memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce)); + pvt->mce_count++; + } + spin_unlock_irqrestore(&pvt->mce_lock, flags); + + /* Advice mcelog that the error were handled */ +// return 1; + return 0; // Let's duplicate the log +} + /* * i7core_probe Probe for ONE instance of device to see if it is * present. @@ -1305,6 +1395,18 @@ static int __devinit i7core_probe(struct pci_dev *pdev, pvt->inject.page = -1; pvt->inject.col = -1; + /* Registers on edac_mce in order to receive memory errors */ + pvt->edac_mce.priv = pvt; + pvt->edac_mce.check_error = i7core_mce_check_error; + spin_lock_init(&pvt->mce_lock); + + rc = edac_mce_register(&pvt->edac_mce); + if (unlikely (rc < 0)) { + debugf0("MC: " __FILE__ + ": %s(): failed edac_mce_register()\n", __func__); + goto fail1; + } + i7core_printk(KERN_INFO, "Driver loaded.\n"); return 0; @@ -1324,17 +1426,22 @@ static int __devinit i7core_probe(struct pci_dev *pdev, static void __devexit i7core_remove(struct pci_dev *pdev) { struct mem_ctl_info *mci; + struct i7core_pvt *pvt; debugf0(__FILE__ ": %s()\n", __func__); if (i7core_pci) edac_pci_release_generic_ctl(i7core_pci); - mci = edac_mc_del_mc(&pdev->dev); + mci = edac_mc_del_mc(&pdev->dev); if (!mci) return; + /* Unregisters on edac_mce in order to receive memory errors */ + pvt = mci->pvt_info; + edac_mce_unregister(&pvt->edac_mce); + /* retrieve references to resources, and free those resources */ i7core_put_devices(); From e9bd2e73793bf0f7fcd8f94b532bb8f5c5b44171 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 9 Jul 2009 22:14:35 -0300 Subject: [PATCH 20/83] i7core_edac: Adds write unlock to MC registers The public Intel Xeon 5500 volume 2 datasheet describes, on page 53, session 2.6.7 a register that can lock/unlock Memory Controller the configuration register, called MC_CFG_CONTROL. Adds support for it in the hope that software error injection would work. With my tests with Xeon 35xx, there's still something missing. With a program that does sequencial bit writes at dev 0.0, sometimes, it produces error injection, after unblocking the MC_CFG_CONTROL (and, sometimes, it just locks my testing machine). I'll try later to discover by trial and error what's the register that solves this issue on Xeon 35xx. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 30 +++++++++++++++++++++++++++--- include/linux/pci_ids.h | 1 + 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 3c7bb5f405f6..26cd5c924d56 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -33,7 +33,7 @@ #include "edac_core.h" /* To use the new pci_[read/write]_config_qword instead of two dword */ -#define USE_QWORD 1 +#define USE_QWORD 0 /* * Alter this version for the module when modifications are made @@ -58,6 +58,10 @@ * i7core Memory Controller Registers */ + /* OFFSETS for Device 0 Function 0 */ + +#define MC_CFG_CONTROL 0x90 + /* OFFSETS for Device 3 Function 0 */ #define MC_CONTROL 0x48 @@ -186,6 +190,7 @@ struct pci_id_descr { }; struct i7core_pvt { + struct pci_dev *pci_noncore; struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1]; struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1]; struct i7core_info info; @@ -222,6 +227,9 @@ struct pci_id_descr pci_devs[] = { { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */ { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) }, + /* Generic Non-core registers */ + { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) }, + /* Channel 0 */ { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) }, { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) }, @@ -882,6 +890,16 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, else mask |= (pvt->inject.col & 0x3fffL); + /* Unlock writes to registers */ + pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 0x2); + msleep(100); + + /* Zeroes error count registers */ + pci_write_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, 0); + pci_write_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, 0); + pvt->ce_count_available = 0; + + #if USE_QWORD pci_write_config_qword(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ADDR_MATCH, mask); @@ -929,12 +947,15 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ERROR_MASK, injectmask); +#if 0 + /* lock writes to registers */ + pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 0); +#endif debugf0("Error inject addr match 0x%016llx, ecc 0x%08x," " inject 0x%08x\n", mask, pvt->inject.eccmask, injectmask); - return count; } @@ -1124,12 +1145,15 @@ static int mci_bind_devs(struct mem_ctl_info *mci) if (unlikely(func > MAX_CHAN_FUNC)) goto error; pvt->pci_ch[slot - 4][func] = pdev; - } else + } else if (!slot && !func) + pvt->pci_noncore = pdev; + else goto error; debugf0("Associated fn %d.%d, dev = %p\n", PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev); } + return 0; error: diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index c5dd0994bd7c..9d5bfe86ba73 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2548,6 +2548,7 @@ #define PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR 0x2c31 #define PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK 0x2c32 #define PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC 0x2c33 +#define PCI_DEVICE_ID_INTEL_I7_NOCORE 0x2c41 #define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 #define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 #define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a From 5707b24a50b40582226618c56692af932db9fe02 Mon Sep 17 00:00:00 2001 From: Aristeu Rozanski Date: Thu, 9 Jul 2009 22:21:13 -0300 Subject: [PATCH 21/83] pci: Add a probing code that seeks for an specific bus This patch adds a probing code that seeks for an specific pci bus. It still needs testing, but it is hoped that this will help to identify the memory controller with Xeon 55xx series. Signed-off-by: Aristeu Sergio Signed-off-by: Mauro Carvalho Chehab --- arch/x86/include/asm/pci_x86.h | 2 ++ arch/x86/pci/legacy.c | 42 ++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index 1a0422348d6d..ff5404b82fdc 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -53,6 +53,8 @@ extern int pcibios_last_bus; extern struct pci_bus *pci_root_bus; extern struct pci_ops pci_root_ops; +void pcibios_scan_specific_bus(int busn); + /* pci-irq.c */ struct irq_info { diff --git a/arch/x86/pci/legacy.c b/arch/x86/pci/legacy.c index 0db5eaf54560..c734c277b116 100644 --- a/arch/x86/pci/legacy.c +++ b/arch/x86/pci/legacy.c @@ -11,28 +11,14 @@ */ static void __devinit pcibios_fixup_peer_bridges(void) { - int n, devfn; - long node; + int n; if (pcibios_last_bus <= 0 || pcibios_last_bus > 0xff) return; DBG("PCI: Peer bridge fixup\n"); - for (n=0; n <= pcibios_last_bus; n++) { - u32 l; - if (pci_find_bus(0, n)) - continue; - node = get_mp_bus_to_node(n); - for (devfn = 0; devfn < 256; devfn += 8) { - if (!raw_pci_read(0, n, devfn, PCI_VENDOR_ID, 2, &l) && - l != 0x0000 && l != 0xffff) { - DBG("Found device at %02x:%02x [%04x]\n", n, devfn, l); - printk(KERN_INFO "PCI: Discovered peer bus %02x\n", n); - pci_scan_bus_on_node(n, &pci_root_ops, node); - break; - } - } - } + for (n=0; n <= pcibios_last_bus; n++) + pcibios_scan_specific_bus(n); } int __init pci_legacy_init(void) @@ -49,6 +35,28 @@ int __init pci_legacy_init(void) return 0; } +EXPORT_SYMBOL_GPL(pci_legacy_init); + +void pcibios_scan_specific_bus(int busn) +{ + int devfn; + long node; + u32 l; + + if (pci_find_bus(0, busn)) + return; + + node = get_mp_bus_to_node(busn); + for (devfn = 0; devfn < 256; devfn += 8) { + if (!raw_pci_read(0, busn, devfn, PCI_VENDOR_ID, 2, &l) && + l != 0x0000 && l != 0xffff) { + DBG("Found device at %02x:%02x [%04x]\n", busn, devfn, l); + printk(KERN_INFO "PCI: Discovered peer bus %02x\n", busn); + pci_scan_bus_on_node(busn, &pci_root_ops, node); + return; + } + } +} int __init pci_subsys_init(void) { From d1fd4fb69eeeb7db0693df58b9116db498d5bfe1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 10 Jul 2009 18:39:53 -0300 Subject: [PATCH 22/83] i7core_edac: Add a code to probe Xeon 55xx bus This code changes the detection procedure of i7core_edac. Instead of directly probing for MC registers, it probes for another register found on Nehalem. If found, it tries to pick the first MC PCI BUS. This should work fine with Xeon 35xx, but, on Xeon 55xx, this is at bus 254 and 255 that are not properly detected by the non-legacy PCI methods. The new detection code scans specifically at buses 254 and 255 for the Xeon 55xx devices. This code has not tested yet. After working, a change at the code will be needed, since the i7core is not yet ready for working with 2 sets of MC. Signed-off-by: Mauro Carvalho Chehab --- arch/x86/pci/legacy.c | 1 + drivers/edac/i7core_edac.c | 17 +++++++++++++---- include/linux/pci.h | 1 + include/linux/pci_ids.h | 1 + 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/arch/x86/pci/legacy.c b/arch/x86/pci/legacy.c index c734c277b116..d6cc2eddf339 100644 --- a/arch/x86/pci/legacy.c +++ b/arch/x86/pci/legacy.c @@ -57,6 +57,7 @@ void pcibios_scan_specific_bus(int busn) } } } +EXPORT_SYMBOL_GPL(pcibios_scan_specific_bus); int __init pci_subsys_init(void) { diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 26cd5c924d56..eec0c13c0205 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -221,15 +221,15 @@ struct i7core_dev_info { .dev_id = (device_id) struct pci_id_descr pci_devs[] = { + /* Generic Non-core registers */ + { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) }, + /* Memory controller */ { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) }, { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) }, { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */ { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) }, - /* Generic Non-core registers */ - { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) }, - /* Channel 0 */ { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL) }, { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR) }, @@ -255,7 +255,7 @@ struct pci_id_descr pci_devs[] = { * This should match the first device at pci_devs table */ static const struct pci_device_id i7core_pci_tbl[] __devinitdata = { - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)}, {0,} /* 0 terminated list. */ }; @@ -1069,6 +1069,15 @@ static int i7core_get_devices(void) for (i = 0; i < N_DEVS; i++) { pdev = pci_get_device(PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, NULL); + + if (!pdev && !i) { + pcibios_scan_specific_bus(254); + pcibios_scan_specific_bus(255); + + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, + pci_devs[i].dev_id, NULL); + } + if (likely(pdev)) pci_devs[i].pdev = pdev; else { diff --git a/include/linux/pci.h b/include/linux/pci.h index a788fa12ff31..5e2c7e15187d 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -621,6 +621,7 @@ void pci_fixup_cardbus(struct pci_bus *); /* Generic PCI functions used internally */ +void pcibios_scan_specific_bus(int busn); extern struct pci_bus *pci_find_bus(int domain, int busnr); void pci_bus_add_devices(const struct pci_bus *bus); struct pci_bus *pci_scan_bus_parented(struct device *parent, int bus, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 9d5bfe86ba73..12c3da6ef14d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2554,6 +2554,7 @@ #define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a #define PCI_DEVICE_ID_INTEL_IOAT_TBG6 0x342b #define PCI_DEVICE_ID_INTEL_IOAT_TBG7 0x342c +#define PCI_DEVICE_ID_INTEL_X58_HUB_MGMT 0x342e #define PCI_DEVICE_ID_INTEL_IOAT_TBG0 0x3430 #define PCI_DEVICE_ID_INTEL_IOAT_TBG1 0x3431 #define PCI_DEVICE_ID_INTEL_IOAT_TBG2 0x3432 From 67166af4abc11d9c0deb497ebe0b562f69c71942 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jul 2009 06:56:23 -0300 Subject: [PATCH 23/83] i7core_edac: add support for more than one MC socket Some Nehalem architectures have more than one MC socket. Socket 0 is located at bus 255. Currently, it is using up to 2 sockets, but increasing it to a larger number is just a matter of increasing MAX_SOCKETS definition. This seems to be required for properly support of Xeon 55xx. Still needs testing with Xeon 55xx. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 326 ++++++++++++++++++++++++------------- 1 file changed, 213 insertions(+), 113 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index eec0c13c0205..69eacc1b50d7 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -155,6 +155,7 @@ #define NUM_CHANS 3 #define MAX_DIMMS 3 /* Max DIMMS per channel */ +#define NUM_SOCKETS 2 /* Max number of MC sockets */ #define MAX_MCR_FUNC 4 #define MAX_CHAN_FUNC 3 @@ -169,6 +170,7 @@ struct i7core_info { struct i7core_inject { int enable; + u8 socket; u32 section; u32 type; u32 eccmask; @@ -186,21 +188,25 @@ struct pci_id_descr { int dev; int func; int dev_id; - struct pci_dev *pdev; + struct pci_dev *pdev[NUM_SOCKETS]; }; struct i7core_pvt { - struct pci_dev *pci_noncore; - struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1]; - struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1]; + struct pci_dev *pci_noncore[NUM_SOCKETS]; + struct pci_dev *pci_mcr[NUM_SOCKETS][MAX_MCR_FUNC + 1]; + struct pci_dev *pci_ch[NUM_SOCKETS][NUM_CHANS][MAX_CHAN_FUNC + 1]; + struct i7core_info info; struct i7core_inject inject; - struct i7core_channel channel[NUM_CHANS]; + struct i7core_channel channel[NUM_SOCKETS][NUM_CHANS]; + + int sockets; /* Number of sockets */ int channels; /* Number of active channels */ - int ce_count_available; - unsigned long ce_count[MAX_DIMMS]; /* ECC corrected errors counts per dimm */ - int last_ce_count[MAX_DIMMS]; + int ce_count_available[NUM_SOCKETS]; + /* ECC corrected errors counts per dimm */ + unsigned long ce_count[NUM_SOCKETS][MAX_DIMMS]; + int last_ce_count[NUM_SOCKETS][MAX_DIMMS]; /* mcelog glue */ struct edac_mce edac_mce; @@ -324,24 +330,26 @@ static inline int numcol(u32 col) /**************************************************************************** Memory check routines ****************************************************************************/ -static struct pci_dev *get_pdev_slot_func(int slot, int func) +static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot, + unsigned func) { int i; for (i = 0; i < N_DEVS; i++) { - if (!pci_devs[i].pdev) + if (!pci_devs[i].pdev[socket]) continue; - if (PCI_SLOT(pci_devs[i].pdev->devfn) == slot && - PCI_FUNC(pci_devs[i].pdev->devfn) == func) { - return pci_devs[i].pdev; + if (PCI_SLOT(pci_devs[i].pdev[socket]->devfn) == slot && + PCI_FUNC(pci_devs[i].pdev[socket]->devfn) == func) { + return pci_devs[i].pdev[socket]; } } return NULL; } -static int i7core_get_active_channels(int *channels, int *csrows) +static int i7core_get_active_channels(u8 socket, unsigned *channels, + unsigned *csrows) { struct pci_dev *pdev = NULL; int i, j; @@ -350,9 +358,10 @@ static int i7core_get_active_channels(int *channels, int *csrows) *channels = 0; *csrows = 0; - pdev = get_pdev_slot_func(3, 0); + pdev = get_pdev_slot_func(socket, 3, 0); if (!pdev) { - i7core_printk(KERN_ERR, "Couldn't find fn 3.0!!!\n"); + i7core_printk(KERN_ERR, "Couldn't find socket %d fn 3.0!!!\n", + socket); return -ENODEV; } @@ -370,10 +379,11 @@ static int i7core_get_active_channels(int *channels, int *csrows) if (status & (1 << i)) continue; - pdev = get_pdev_slot_func(i + 4, 1); + pdev = get_pdev_slot_func(socket, i + 4, 1); if (!pdev) { - i7core_printk(KERN_ERR, "Couldn't find fn %d.%d!!!\n", - i + 4, 1); + i7core_printk(KERN_ERR, "Couldn't find socket %d " + "fn %d.%d!!!\n", + socket, i + 4, 1); return -ENODEV; } /* Devices 4-6 function 1 */ @@ -393,12 +403,13 @@ static int i7core_get_active_channels(int *channels, int *csrows) } } - debugf0("Number of active channels: %d\n", *channels); + debugf0("Number of active channels on socked %d: %d\n", + socket, *channels); return 0; } -static int get_dimm_config(struct mem_ctl_info *mci) +static int get_dimm_config(struct mem_ctl_info *mci, u8 socket) { struct i7core_pvt *pvt = mci->pvt_info; struct csrow_info *csr; @@ -409,7 +420,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) enum mem_type mtype; /* Get data from the MC register, function 0 */ - pdev = pvt->pci_mcr[0]; + pdev = pvt->pci_mcr[socket][0]; if (!pdev) return -ENODEV; @@ -458,10 +469,11 @@ static int get_dimm_config(struct mem_ctl_info *mci) } /* Devices 4-6 function 0 */ - pci_read_config_dword(pvt->pci_ch[i][0], + pci_read_config_dword(pvt->pci_ch[socket][i][0], MC_CHANNEL_DIMM_INIT_PARAMS, &data); - pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ? 4 : 2; + pvt->channel[socket][i].ranks = (data & QUAD_RANK_PRESENT) ? + 4 : 2; if (data & REGISTERED_DIMM) mtype = MEM_RDDR3; @@ -477,11 +489,11 @@ static int get_dimm_config(struct mem_ctl_info *mci) #endif /* Devices 4-6 function 1 */ - pci_read_config_dword(pvt->pci_ch[i][1], + pci_read_config_dword(pvt->pci_ch[socket][i][1], MC_DOD_CH_DIMM0, &dimm_dod[0]); - pci_read_config_dword(pvt->pci_ch[i][1], + pci_read_config_dword(pvt->pci_ch[socket][i][1], MC_DOD_CH_DIMM1, &dimm_dod[1]); - pci_read_config_dword(pvt->pci_ch[i][1], + pci_read_config_dword(pvt->pci_ch[socket][i][1], MC_DOD_CH_DIMM2, &dimm_dod[2]); debugf0("Ch%d phy rd%d, wr%d (0x%08x): " @@ -489,7 +501,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) i, RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i), data, - pvt->channel[i].ranks, + pvt->channel[socket][i].ranks, (data & REGISTERED_DIMM) ? 'R' : 'U'); for (j = 0; j < 3; j++) { @@ -507,7 +519,7 @@ static int get_dimm_config(struct mem_ctl_info *mci) /* DDR3 has 8 I/O banks */ size = (rows * cols * banks * ranks) >> (20 - 3); - pvt->channel[i].dimms++; + pvt->channel[socket][i].dimms++; debugf0("\tdimm %d (0x%08x) %d Mb offset: %x, " "numbank: %d,\n\t\t" @@ -592,15 +604,42 @@ static int disable_inject(struct mem_ctl_info *mci) pvt->inject.enable = 0; - if (!pvt->pci_ch[pvt->inject.channel][0]) + if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0]) return -ENODEV; - pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], + pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], MC_CHANNEL_ERROR_MASK, 0); return 0; } +/* + * i7core inject inject.socket + * + * accept and store error injection inject.socket value + */ +static ssize_t i7core_inject_socket_store(struct mem_ctl_info *mci, + const char *data, size_t count) +{ + struct i7core_pvt *pvt = mci->pvt_info; + unsigned long value; + int rc; + + rc = strict_strtoul(data, 10, &value); + if ((rc < 0) || (value > pvt->sockets)) + return 0; + + pvt->inject.section = (u32) value; + return count; +} + +static ssize_t i7core_inject_socket_show(struct mem_ctl_info *mci, + char *data) +{ + struct i7core_pvt *pvt = mci->pvt_info; + return sprintf(data, "%d\n", pvt->inject.socket); +} + /* * i7core inject inject.section * @@ -838,7 +877,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, int rc; long enable; - if (!pvt->pci_ch[pvt->inject.channel][0]) + if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0]) return 0; rc = strict_strtoul(data, 10, &enable); @@ -856,7 +895,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, if (pvt->inject.dimm < 0) mask |= 1L << 41; else { - if (pvt->channel[pvt->inject.channel].dimms > 2) + if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2) mask |= (pvt->inject.dimm & 0x3L) << 35; else mask |= (pvt->inject.dimm & 0x1L) << 36; @@ -866,7 +905,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, if (pvt->inject.rank < 0) mask |= 1L << 40; else { - if (pvt->channel[pvt->inject.channel].dimms > 2) + if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2) mask |= (pvt->inject.rank & 0x1L) << 34; else mask |= (pvt->inject.rank & 0x3L) << 34; @@ -891,38 +930,41 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, mask |= (pvt->inject.col & 0x3fffL); /* Unlock writes to registers */ - pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 0x2); + pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket], + MC_CFG_CONTROL, 0x2); msleep(100); /* Zeroes error count registers */ - pci_write_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, 0); - pci_write_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, 0); - pvt->ce_count_available = 0; + pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4], + MC_TEST_ERR_RCV1, 0); + pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4], + MC_TEST_ERR_RCV0, 0); + pvt->ce_count_available[pvt->inject.socket] = 0; #if USE_QWORD - pci_write_config_qword(pvt->pci_ch[pvt->inject.channel][0], + pci_write_config_qword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], MC_CHANNEL_ADDR_MATCH, mask); #else - pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], + pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], MC_CHANNEL_ADDR_MATCH, mask); - pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], + pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L); #endif #if 1 #if USE_QWORD u64 rdmask; - pci_read_config_qword(pvt->pci_ch[pvt->inject.channel][0], + pci_read_config_qword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], MC_CHANNEL_ADDR_MATCH, &rdmask); debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n", mask, rdmask); #else u32 rdmask1, rdmask2; - pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], + pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], MC_CHANNEL_ADDR_MATCH, &rdmask1); - pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], + pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], MC_CHANNEL_ADDR_MATCH + 4, &rdmask2); debugf0("Inject addr match write 0x%016llx, read: 0x%08x 0x%08x\n", @@ -930,7 +972,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, #endif #endif - pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], + pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask); /* @@ -944,7 +986,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, (pvt->inject.section & 0x3) << 1 | (pvt->inject.type & 0x6) << (3 - 1); - pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], + pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], MC_CHANNEL_ERROR_MASK, injectmask); #if 0 @@ -965,7 +1007,7 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, struct i7core_pvt *pvt = mci->pvt_info; u32 injectmask; - pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], + pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], MC_CHANNEL_ERROR_MASK, &injectmask); debugf0("Inject error read: 0x%018x\n", injectmask); @@ -978,23 +1020,38 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data) { + unsigned i, count, total = 0; struct i7core_pvt *pvt = mci->pvt_info; - if (!pvt->ce_count_available) - return sprintf(data, "unavailable\n"); + for (i = 0; i < pvt->sockets; i++) { + if (!pvt->ce_count_available[i]) + count = sprintf(data, "socket 0 data unavailable\n"); + else + count = sprintf(data, "socket %d, dimm0: %lu\n" + "dimm1: %lu\ndimm2: %lu\n", + i, + pvt->ce_count[i][0], + pvt->ce_count[i][1], + pvt->ce_count[i][2]); + data += count; + total += count; + } - return sprintf(data, "dimm0: %lu\ndimm1: %lu\ndimm2: %lu\n", - pvt->ce_count[0], - pvt->ce_count[1], - pvt->ce_count[2]); + return total; } /* * Sysfs struct */ static struct mcidev_sysfs_attribute i7core_inj_attrs[] = { - { + .attr = { + .name = "inject_socket", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = i7core_inject_socket_show, + .store = i7core_inject_socket_store, + }, { .attr = { .name = "inject_section", .mode = (S_IRUGO | S_IWUSR) @@ -1049,10 +1106,11 @@ static struct mcidev_sysfs_attribute i7core_inj_attrs[] = { */ static void i7core_put_devices(void) { - int i; + int i, j; - for (i = 0; i < N_DEVS; i++) - pci_dev_put(pci_devs[i].pdev); + for (i = 0; i < NUM_SOCKETS; i++) + for (j = 0; j < N_DEVS; j++) + pci_dev_put(pci_devs[j].pdev[i]); } /* @@ -1065,6 +1123,8 @@ static int i7core_get_devices(void) { int rc, i; struct pci_dev *pdev = NULL; + u8 bus = 0; + u8 socket = 0; for (i = 0; i < N_DEVS; i++) { pdev = pci_get_device(PCI_VENDOR_ID_INTEL, @@ -1078,14 +1138,32 @@ static int i7core_get_devices(void) pci_devs[i].dev_id, NULL); } - if (likely(pdev)) - pci_devs[i].pdev = pdev; - else { + if (likely(pdev)) { + bus = pdev->bus->number; + + if (bus == 0x3f) + socket = 0; + else + socket = 255 - bus; + + if (socket >= NUM_SOCKETS) { + i7core_printk(KERN_ERR, + "Found unexpected socket for " + "dev %02x:%02x.%d PCI ID %04x:%04x\n", + bus, pci_devs[i].dev, pci_devs[i].func, + PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id); + + rc = -ENODEV; + goto error; + } + + pci_devs[i].pdev[socket] = pdev; + } else { i7core_printk(KERN_ERR, - "Device not found: PCI ID %04x:%04x " - "(dev %d, func %d)\n", - PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, - pci_devs[i].dev, pci_devs[i].func); + "Device not found: " + "dev %02x:%02x.%d PCI ID %04x:%04x\n", + bus, pci_devs[i].dev, pci_devs[i].func, + PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id); /* Dev 3 function 2 only exists on chips with RDIMMs */ if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2)) @@ -1121,9 +1199,10 @@ static int i7core_get_devices(void) } i7core_printk(KERN_INFO, - "Registered device %0x:%0x fn %d.%d\n", - PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + "Registered socket %d " + "dev %02x:%02x.%d PCI ID %04x:%04x\n", + socket, bus, pci_devs[i].dev, pci_devs[i].func, + PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id); } return 0; @@ -1137,30 +1216,33 @@ static int mci_bind_devs(struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; struct pci_dev *pdev; - int i, func, slot; + int i, j, func, slot; - for (i = 0; i < N_DEVS; i++) { - pdev = pci_devs[i].pdev; - if (!pdev) - continue; + for (i = 0; i < pvt->sockets; i++) { + for (j = 0; j < N_DEVS; j++) { + pdev = pci_devs[j].pdev[i]; + if (!pdev) + continue; - func = PCI_FUNC(pdev->devfn); - slot = PCI_SLOT(pdev->devfn); - if (slot == 3) { - if (unlikely(func > MAX_MCR_FUNC)) + func = PCI_FUNC(pdev->devfn); + slot = PCI_SLOT(pdev->devfn); + if (slot == 3) { + if (unlikely(func > MAX_MCR_FUNC)) + goto error; + pvt->pci_mcr[i][func] = pdev; + } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) { + if (unlikely(func > MAX_CHAN_FUNC)) + goto error; + pvt->pci_ch[i][slot - 4][func] = pdev; + } else if (!slot && !func) + pvt->pci_noncore[i] = pdev; + else goto error; - pvt->pci_mcr[func] = pdev; - } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) { - if (unlikely(func > MAX_CHAN_FUNC)) - goto error; - pvt->pci_ch[slot - 4][func] = pdev; - } else if (!slot && !func) - pvt->pci_noncore = pdev; - else - goto error; - debugf0("Associated fn %d.%d, dev = %p\n", - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev); + debugf0("Associated fn %d.%d, dev = %p, socket %d\n", + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), + pdev, i); + } } return 0; @@ -1182,20 +1264,20 @@ static int mci_bind_devs(struct mem_ctl_info *mci) * also available at: * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf */ -static void check_mc_test_err(struct mem_ctl_info *mci) +static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket) { struct i7core_pvt *pvt = mci->pvt_info; u32 rcv1, rcv0; int new0, new1, new2; - if (!pvt->pci_mcr[4]) { + if (!pvt->pci_mcr[socket][4]) { debugf0("%s MCR registers not found\n",__func__); return; } /* Corrected error reads */ - pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1); - pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0); + pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1); + pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0); /* Store the new values */ new2 = DIMM2_COR_ERR(rcv1); @@ -1209,32 +1291,32 @@ static void check_mc_test_err(struct mem_ctl_info *mci) #endif /* Updates CE counters if it is not the first time here */ - if (pvt->ce_count_available) { + if (pvt->ce_count_available[socket]) { /* Updates CE counters */ int add0, add1, add2; - add2 = new2 - pvt->last_ce_count[2]; - add1 = new1 - pvt->last_ce_count[1]; - add0 = new0 - pvt->last_ce_count[0]; + add2 = new2 - pvt->last_ce_count[socket][2]; + add1 = new1 - pvt->last_ce_count[socket][1]; + add0 = new0 - pvt->last_ce_count[socket][0]; if (add2 < 0) add2 += 0x7fff; - pvt->ce_count[2] += add2; + pvt->ce_count[socket][2] += add2; if (add1 < 0) add1 += 0x7fff; - pvt->ce_count[1] += add1; + pvt->ce_count[socket][1] += add1; if (add0 < 0) add0 += 0x7fff; - pvt->ce_count[0] += add0; + pvt->ce_count[socket][0] += add0; } else - pvt->ce_count_available = 1; + pvt->ce_count_available[socket] = 1; /* Store the new values */ - pvt->last_ce_count[2] = new2; - pvt->last_ce_count[1] = new1; - pvt->last_ce_count[0] = new0; + pvt->last_ce_count[socket][2] = new2; + pvt->last_ce_count[socket][1] = new1; + pvt->last_ce_count[socket][0] = new0; } static void i7core_mce_output_error(struct mem_ctl_info *mci, @@ -1299,7 +1381,8 @@ static void i7core_check_error(struct mem_ctl_info *mci) kfree(m); /* check memory count errors */ - check_mc_test_err(mci); + for (i = 0; i < pvt->sockets; i++) + check_mc_test_err(mci, i); } /* @@ -1339,10 +1422,11 @@ static int __devinit i7core_probe(struct pci_dev *pdev, { struct mem_ctl_info *mci; struct i7core_pvt *pvt; - int num_channels; - int num_csrows; + int num_channels = 0; + int num_csrows = 0; int dev_idx = id->driver_data; - int rc; + int rc, i; + u8 sockets; if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs))) return -EINVAL; @@ -1352,10 +1436,25 @@ static int __devinit i7core_probe(struct pci_dev *pdev, if (unlikely(rc < 0)) return rc; - /* Check the number of active and not disabled channels */ - rc = i7core_get_active_channels(&num_channels, &num_csrows); - if (unlikely(rc < 0)) - goto fail0; + sockets = 1; + for (i = NUM_SOCKETS - 1; i > 0; i--) + if (pci_devs[0].pdev[i]) { + sockets = i + 1; + break; + } + + for (i = 0; i < sockets; i++) { + int channels; + int csrows; + + /* Check the number of active and not disabled channels */ + rc = i7core_get_active_channels(i, &channels, &csrows); + if (unlikely(rc < 0)) + goto fail0; + + num_channels += channels; + num_csrows += csrows; + } /* allocate a new MC control structure */ mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0); @@ -1367,11 +1466,11 @@ static int __devinit i7core_probe(struct pci_dev *pdev, debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); mci->dev = &pdev->dev; /* record ptr to the generic device */ - pvt = mci->pvt_info; memset(pvt, 0, sizeof(*pvt)); - + pvt->sockets = sockets; mci->mc_idx = 0; + /* * FIXME: how to handle RDDR3 at MCI level? It is possible to have * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different @@ -1395,7 +1494,8 @@ static int __devinit i7core_probe(struct pci_dev *pdev, goto fail1; /* Get dimm basic config */ - get_dimm_config(mci); + for (i = 0; i < sockets; i++) + get_dimm_config(mci, i); /* add this new MC control structure to EDAC's list of MCs */ if (unlikely(edac_mc_add_mc(mci))) { From ba6c5c62eeb877da638e43f1282f778432142eec Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jul 2009 09:02:32 -0300 Subject: [PATCH 24/83] i7core_edac: maps all sockets as if ther are one MC controller Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 69eacc1b50d7..a93ebdf9c121 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -409,12 +409,12 @@ static int i7core_get_active_channels(u8 socket, unsigned *channels, return 0; } -static int get_dimm_config(struct mem_ctl_info *mci, u8 socket) +static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) { struct i7core_pvt *pvt = mci->pvt_info; struct csrow_info *csr; struct pci_dev *pdev; - int i, j, csrow = 0; + int i, j; unsigned long last_page = 0; enum edac_type mode; enum mem_type mtype; @@ -534,7 +534,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, u8 socket) npages = size << (20 - PAGE_SHIFT); #endif - csr = &mci->csrows[csrow]; + csr = &mci->csrows[*csrow]; csr->first_page = last_page + 1; last_page += npages; csr->last_page = last_page; @@ -542,7 +542,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, u8 socket) csr->page_mask = 0; csr->grain = 8; - csr->csrow_idx = csrow; + csr->csrow_idx = *csrow; csr->nr_channels = 1; csr->channels[0].chan_idx = i; @@ -565,7 +565,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, u8 socket) csr->edac_mode = mode; csr->mtype = mtype; - csrow++; + (*csrow)++; } pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]); @@ -1424,6 +1424,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, struct i7core_pvt *pvt; int num_channels = 0; int num_csrows = 0; + int csrow = 0; int dev_idx = id->driver_data; int rc, i; u8 sockets; @@ -1495,7 +1496,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, /* Get dimm basic config */ for (i = 0; i < sockets; i++) - get_dimm_config(mci, i); + get_dimm_config(mci, &csrow, i); /* add this new MC control structure to EDAC's list of MCs */ if (unlikely(edac_mc_add_mc(mci))) { From 8a2f118e3a023a4e8cbe56a6e51f7b78fa8c76a0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jul 2009 19:01:08 -0300 Subject: [PATCH 25/83] i7core_edac: decode mcelog error and send it via edac interface Enriches mcelog error by using the encoded information at MCE status and misc registers (IA32_MCx_STATUS, IA32_MCx_MISC). Some fixes are still needed here, in order to properly fill the EDAC fields. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 94 ++++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index a93ebdf9c121..4397a3171c62 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1319,33 +1319,75 @@ static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket) pvt->last_ce_count[socket][0] = new0; } +/* + * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32 + * Architectures Software Developer’s Manual Volume 3B. + * The MCA registers are the following ones: + * struct mce field MCA Register + * m->status MSR_IA32_MC0_STATUS + * m->addr MSR_IA32_MC0_ADDR + * m->misc MSR_IA32_MC0_MISC + * m->mcgstatus MSR_IA32_MCG_STATUS + * In the case of Nehalem, the error information is masked at .status and .misc + * fields + */ static void i7core_mce_output_error(struct mem_ctl_info *mci, struct mce *m) { - debugf0("CPU %d: Machine Check Exception: %16Lx" - "Bank %d: %016Lx\n", - m->cpu, m->mcgstatus, m->bank, m->status); - if (m->ip) { - debugf0("RIP%s %02x:<%016Lx>\n", - !(m->mcgstatus & MCG_STATUS_EIPV) ? " !INEXACT!" : "", - m->cs, m->ip); - } - printk(KERN_EMERG "TSC %llx ", m->tsc); - if (m->addr) - printk("ADDR %llx ", m->addr); - if (m->misc) - printk("MISC %llx ", m->misc); + char *type="NON-FATAL"; + char *err, *msg; + unsigned long error = m->status & 0x1ff0000l; + u32 core_err_cnt = (m->status >> 38) && 0x7fff; + u32 dimm = (m->misc >> 16) & 0x3; + u32 channel = (m->misc >> 18) & 0x3; + u32 syndrome = m->misc >> 32; + u32 errnum = find_first_bit(&error, 32); -#if 0 - snprintf(msg, sizeof(msg), - "%s (Branch=%d DRAM-Bank=%d Buffer ID = %d RDWR=%s " - "RAS=%d CAS=%d %s Err=0x%lx (%s))", - type, branch >> 1, bank, buf_id, rdwr_str(rdwr), ras, cas, - type, allErrors, error_name[errnum]); + switch (errnum) { + case 16: + err = "read ECC error"; + break; + case 17: + err = "RAS ECC error"; + break; + case 18: + err = "write parity error"; + break; + case 19: + err = "redundacy loss"; + break; + case 20: + err = "reserved"; + break; + case 21: + err = "memory range error"; + break; + case 22: + err = "RTID out of range"; + break; + case 23: + err = "address parity error"; + break; + case 24: + err = "byte enable parity error"; + break; + default: + err = "unknown"; + } + + msg = kasprintf(GFP_ATOMIC, + "%s (addr = 0x%08llx Bank=0x%08x, Dimm=%d, Channel=%d, " + "syndrome=0x%08x total error count=%d Err=%d (%s))\n", + type, (long long) m->addr, m->bank, dimm, channel, + syndrome, core_err_cnt,errnum, err); + + debugf0("%s", msg); /* Call the helper to output message */ - edac_mc_handle_fbd_ue(mci, rank, channel, channel + 1, msg); -#endif + edac_mc_handle_fbd_ue(mci, 0 /* FIXME: should be rank here */, + 0, 0 /* FIXME: should be channel here */, msg); + + kfree(msg); } /* @@ -1398,6 +1440,13 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) debugf0(__FILE__ ": %s()\n", __func__); + /* + * Just let mcelog handle it if the error is + * outside the memory controller + */ + if (((mce->status & 0xffff) >> 7) != 1) + return 0; + spin_lock_irqsave(&pvt->mce_lock, flags); if (pvt->mce_count < MCE_LOG_LEN) { memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce)); @@ -1406,8 +1455,7 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) spin_unlock_irqrestore(&pvt->mce_lock, flags); /* Advice mcelog that the error were handled */ -// return 1; - return 0; // Let's duplicate the log + return 1; } /* From f237fcf2b7560be33386255042dc11167ca486d5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 15 Jul 2009 19:53:24 -0300 Subject: [PATCH 26/83] i7core_edac: some fixes at memory error parser m->bank is not related to the memory bank but, instead, to the MCA Error register bank. Fix it accordingly. While here, improves the comments for Nehalem bank. A later fix is needed, in order to get bank/rank information from MCA error log. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 4397a3171c62..67822976992e 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1322,12 +1322,13 @@ static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket) /* * According with tables E-11 and E-12 of chapter E.3.3 of Intel 64 and IA-32 * Architectures Software Developer’s Manual Volume 3B. - * The MCA registers are the following ones: + * Nehalem are defined as family 0x06, model 0x1a + * + * The MCA registers used here are the following ones: * struct mce field MCA Register - * m->status MSR_IA32_MC0_STATUS - * m->addr MSR_IA32_MC0_ADDR - * m->misc MSR_IA32_MC0_MISC - * m->mcgstatus MSR_IA32_MCG_STATUS + * m->status MSR_IA32_MC8_STATUS + * m->addr MSR_IA32_MC8_ADDR + * m->misc MSR_IA32_MC8_MISC * In the case of Nehalem, the error information is masked at .status and .misc * fields */ @@ -1375,10 +1376,11 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, err = "unknown"; } + /* FIXME: should convert addr into bank and rank information */ msg = kasprintf(GFP_ATOMIC, - "%s (addr = 0x%08llx Bank=0x%08x, Dimm=%d, Channel=%d, " - "syndrome=0x%08x total error count=%d Err=%d (%s))\n", - type, (long long) m->addr, m->bank, dimm, channel, + "%s (addr = 0x%08llx Dimm=%d, Channel=%d, " + "syndrome=0x%08x, count=%d Err=%d (%s))\n", + type, (long long) m->addr, dimm, channel, syndrome, core_err_cnt,errnum, err); debugf0("%s", msg); @@ -1447,6 +1449,10 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) if (((mce->status & 0xffff) >> 7) != 1) return 0; + /* Bank 8 registers are the only ones that we know how to handle */ + if (mce->bank != 8) + return 0; + spin_lock_irqsave(&pvt->mce_lock, flags); if (pvt->mce_count < MCE_LOG_LEN) { memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce)); From 310cbb7284fab9fc9cbb6bb893e51c414e15bba3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 17 Jul 2009 00:09:10 -0300 Subject: [PATCH 27/83] i7core: fix probing on Xeon55xx Xeon55xx fails to probe with this error message: EDAC DEBUG: in drivers/edac/i7core_edac.c, line at 1660: MC: drivers/edac/i7core_edac.c: i7core_init() EDAC i7core: Device not found: dev 00:00.0 PCI ID 8086:2c41 i7core_edac: probe of 0000:00:14.0 failed with error -22 This is due to the fact that, on Xeon35xx (and i7core), device 00.0 has PCI ID 8086:2c40. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 23 ++++++++++++++++++++--- include/linux/pci_ids.h | 1 + 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 67822976992e..e2f6dfdca841 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -227,9 +227,6 @@ struct i7core_dev_info { .dev_id = (device_id) struct pci_id_descr pci_devs[] = { - /* Generic Non-core registers */ - { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) }, - /* Memory controller */ { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) }, { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) }, @@ -253,6 +250,16 @@ struct pci_id_descr pci_devs[] = { { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR) }, { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK) }, { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC) }, + + /* Generic Non-core registers */ + /* + * This is the PCI device on i7core and on Xeon 35xx (8086:2c41) + * On Xeon 55xx, however, it has a different id (8086:2c40). So, + * the probing code needs to test for the other address in case of + * failure of this one + */ + { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) }, + }; #define N_DEVS ARRAY_SIZE(pci_devs) @@ -1138,6 +1145,16 @@ static int i7core_get_devices(void) pci_devs[i].dev_id, NULL); } + /* + * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs + * is at addr 8086:2c40, instead of 8086:2c41. So, we need + * to probe for the alternate address in case of failure + */ + if (pci_devs[i].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE + && !pdev) + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, NULL); + if (likely(pdev)) { bus = pdev->bus->number; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 12c3da6ef14d..bf6db4814c27 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2549,6 +2549,7 @@ #define PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK 0x2c32 #define PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC 0x2c33 #define PCI_DEVICE_ID_INTEL_I7_NOCORE 0x2c41 +#define PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT 0x2c40 #define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 #define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 #define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a From c5d34528696acadc40d2ba7601dbf35d65b74ad5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 17 Jul 2009 10:28:15 -0300 Subject: [PATCH 28/83] i7core: check if the memory error is fatal or non-fatal Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index e2f6dfdca841..08149d5addff 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1352,7 +1352,7 @@ static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket) static void i7core_mce_output_error(struct mem_ctl_info *mci, struct mce *m) { - char *type="NON-FATAL"; + char *type; char *err, *msg; unsigned long error = m->status & 0x1ff0000l; u32 core_err_cnt = (m->status >> 38) && 0x7fff; @@ -1361,6 +1361,11 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, u32 syndrome = m->misc >> 32; u32 errnum = find_first_bit(&error, 32); + if (m->mcgstatus & 1) + type = "FATAL"; + else + type = "NON_FATAL"; + switch (errnum) { case 16: err = "read ECC error"; @@ -1454,7 +1459,8 @@ static void i7core_check_error(struct mem_ctl_info *mci) */ static int i7core_mce_check_error(void *priv, struct mce *mce) { - struct i7core_pvt *pvt = priv; + struct mem_ctl_info *mci = priv; + struct i7core_pvt *pvt = mci->pvt_info; unsigned long flags; debugf0(__FILE__ ": %s()\n", __func__); @@ -1477,6 +1483,10 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) } spin_unlock_irqrestore(&pvt->mce_lock, flags); + /* Handle fatal errors immediately */ + if (mce->mcgstatus & 1) + i7core_check_error(mci); + /* Advice mcelog that the error were handled */ return 1; } @@ -1601,7 +1611,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, pvt->inject.col = -1; /* Registers on edac_mce in order to receive memory errors */ - pvt->edac_mce.priv = pvt; + pvt->edac_mce.priv = mci; pvt->edac_mce.check_error = i7core_mce_check_error; spin_lock_init(&pvt->mce_lock); From a639539fa28531924c6b5e0f3963cc63d060947d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 17 Jul 2009 10:54:23 -0300 Subject: [PATCH 29/83] i7core: enrich error information based on memory transaction type Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 08149d5addff..9f39d3d5502e 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1352,9 +1352,9 @@ static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket) static void i7core_mce_output_error(struct mem_ctl_info *mci, struct mce *m) { - char *type; - char *err, *msg; + char *type, *optype, *err, *msg; unsigned long error = m->status & 0x1ff0000l; + u32 optypenum = (m->status >> 4) & 0x07; u32 core_err_cnt = (m->status >> 38) && 0x7fff; u32 dimm = (m->misc >> 16) & 0x3; u32 channel = (m->misc >> 18) & 0x3; @@ -1366,6 +1366,27 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, else type = "NON_FATAL"; + switch (optypenum) { + case 0: + optype = "generic undef request"; + break; + case 1: + optype = "read error"; + break; + case 2: + optype = "write error"; + break; + case 3: + optype = "addr/cmd error"; + break; + case 4: + optype = "scrubbing error"; + break; + default: + optype = "reserved"; + break; + } + switch (errnum) { case 16: err = "read ECC error"; @@ -1400,10 +1421,11 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, /* FIXME: should convert addr into bank and rank information */ msg = kasprintf(GFP_ATOMIC, - "%s (addr = 0x%08llx Dimm=%d, Channel=%d, " - "syndrome=0x%08x, count=%d Err=%d (%s))\n", + "%s (addr = 0x%08llx, Dimm=%d, Channel=%d, " + "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n", type, (long long) m->addr, dimm, channel, - syndrome, core_err_cnt,errnum, err); + syndrome, core_err_cnt, (long long)m->status, + (long long)m->misc, optype, err); debugf0("%s", msg); From c77720b9544d8825ff5b9546d0ee038cfa4d4eb2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 18 Jul 2009 10:43:08 -0300 Subject: [PATCH 30/83] i7core: fix get_devices routine for Xeon55xx i7core_get_devices() were preparet to get just the first found device of each type. Due to that, on Xeon 55xx, only socket 1 were retrived. Rework i7core_get_devices() to clean it and to properly support Xeon 55xx. While here, fix a small typo. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 196 +++++++++++++++++++++---------------- 1 file changed, 113 insertions(+), 83 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 9f39d3d5502e..79636b58ec52 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -410,7 +410,7 @@ static int i7core_get_active_channels(u8 socket, unsigned *channels, } } - debugf0("Number of active channels on socked %d: %d\n", + debugf0("Number of active channels on socket %d: %d\n", socket, *channels); return 0; @@ -1126,107 +1126,137 @@ static void i7core_put_devices(void) * * Need to 'get' device 16 func 1 and func 2 */ -static int i7core_get_devices(void) +int i7core_get_onedevice(struct pci_dev **prev, int devno) { - int rc, i; struct pci_dev *pdev = NULL; u8 bus = 0; u8 socket = 0; - for (i = 0; i < N_DEVS; i++) { + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, + pci_devs[devno].dev_id, *prev); + + /* + * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses + * aren't announced by acpi. So, we need to use a legacy scan probing + * to detect them + */ + if (unlikely(!pdev && !devno && !prev)) { + pcibios_scan_specific_bus(254); + pcibios_scan_specific_bus(255); + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, - pci_devs[i].dev_id, NULL); + pci_devs[devno].dev_id, *prev); + } - if (!pdev && !i) { - pcibios_scan_specific_bus(254); - pcibios_scan_specific_bus(255); + /* + * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs + * is at addr 8086:2c40, instead of 8086:2c41. So, we need + * to probe for the alternate address in case of failure + */ + if (pci_devs[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev) + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev); - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, - pci_devs[i].dev_id, NULL); + if (!pdev) { + if (*prev) { + *prev = pdev; + return 0; } /* - * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs - * is at addr 8086:2c40, instead of 8086:2c41. So, we need - * to probe for the alternate address in case of failure + * Dev 3 function 2 only exists on chips with RDIMMs + * so, it is ok to not found it */ - if (pci_devs[i].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE - && !pdev) - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, NULL); - - if (likely(pdev)) { - bus = pdev->bus->number; - - if (bus == 0x3f) - socket = 0; - else - socket = 255 - bus; - - if (socket >= NUM_SOCKETS) { - i7core_printk(KERN_ERR, - "Found unexpected socket for " - "dev %02x:%02x.%d PCI ID %04x:%04x\n", - bus, pci_devs[i].dev, pci_devs[i].func, - PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id); - - rc = -ENODEV; - goto error; - } - - pci_devs[i].pdev[socket] = pdev; - } else { - i7core_printk(KERN_ERR, - "Device not found: " - "dev %02x:%02x.%d PCI ID %04x:%04x\n", - bus, pci_devs[i].dev, pci_devs[i].func, - PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id); - - /* Dev 3 function 2 only exists on chips with RDIMMs */ - if ((pci_devs[i].dev == 3) && (pci_devs[i].func == 2)) - continue; - - /* End of list, leave */ - rc = -ENODEV; - goto error; + if ((pci_devs[devno].dev == 3) && (pci_devs[devno].func == 2)) { + *prev = pdev; + return 0; } - /* Sanity check */ - if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[i].dev || - PCI_FUNC(pdev->devfn) != pci_devs[i].func)) { - i7core_printk(KERN_ERR, - "Device PCI ID %04x:%04x " - "has fn %d.%d instead of fn %d.%d\n", - PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), - pci_devs[i].dev, pci_devs[i].func); - rc = -EINVAL; - goto error; - } + i7core_printk(KERN_ERR, + "Device not found: dev %02x.%d PCI ID %04x:%04x\n", + pci_devs[devno].dev, pci_devs[devno].func, + PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id); - /* Be sure that the device is enabled */ - rc = pci_enable_device(pdev); - if (unlikely(rc < 0)) { - i7core_printk(KERN_ERR, - "Couldn't enable PCI ID %04x:%04x " - "fn %d.%d\n", - PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - goto error; - } + /* End of list, leave */ + return -ENODEV; + } + bus = pdev->bus->number; - i7core_printk(KERN_INFO, - "Registered socket %d " - "dev %02x:%02x.%d PCI ID %04x:%04x\n", - socket, bus, pci_devs[i].dev, pci_devs[i].func, - PCI_VENDOR_ID_INTEL, pci_devs[i].dev_id); + if (bus == 0x3f) + socket = 0; + else + socket = 255 - bus; + + if (socket >= NUM_SOCKETS) { + i7core_printk(KERN_ERR, + "Unexpected socket for " + "dev %02x:%02x.%d PCI ID %04x:%04x\n", + bus, pci_devs[devno].dev, pci_devs[devno].func, + PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id); + pci_dev_put(pdev); + return -ENODEV; } - return 0; + if (pci_devs[devno].pdev[socket]) { + i7core_printk(KERN_ERR, + "Duplicated device for " + "dev %02x:%02x.%d PCI ID %04x:%04x\n", + bus, pci_devs[devno].dev, pci_devs[devno].func, + PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id); + pci_dev_put(pdev); + return -ENODEV; + } -error: - i7core_put_devices(); - return -EINVAL; + pci_devs[devno].pdev[socket] = pdev; + + /* Sanity check */ + if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[devno].dev || + PCI_FUNC(pdev->devfn) != pci_devs[devno].func)) { + i7core_printk(KERN_ERR, + "Device PCI ID %04x:%04x " + "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n", + PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id, + bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), + bus, pci_devs[devno].dev, pci_devs[devno].func); + return -ENODEV; + } + + /* Be sure that the device is enabled */ + if (unlikely(pci_enable_device(pdev) < 0)) { + i7core_printk(KERN_ERR, + "Couldn't enable " + "dev %02x:%02x.%d PCI ID %04x:%04x\n", + bus, pci_devs[devno].dev, pci_devs[devno].func, + PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id); + return -ENODEV; + } + + i7core_printk(KERN_INFO, + "Registered socket %d " + "dev %02x:%02x.%d PCI ID %04x:%04x\n", + socket, bus, pci_devs[devno].dev, pci_devs[devno].func, + PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id); + + *prev = pdev; + + return 0; +} + +static int i7core_get_devices(void) +{ + int i; + struct pci_dev *pdev = NULL; + + for (i = 0; i < N_DEVS; i++) { + pdev = NULL; + do { + if (i7core_get_onedevice(&pdev, i) < 0) { + i7core_put_devices(); + return -ENODEV; + } + } while (pdev); + } + return 0; } static int mci_bind_devs(struct mem_ctl_info *mci) From ec6df24c15822e671801eeeb53758e14f3b28381 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 18 Jul 2009 10:44:30 -0300 Subject: [PATCH 31/83] i7core: better document i7core_get_active_channels() Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 79636b58ec52..bf0374062e3d 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -333,7 +333,6 @@ static inline int numcol(u32 col) return cols[col & 0x3]; } - /**************************************************************************** Memory check routines ****************************************************************************/ @@ -355,6 +354,23 @@ static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot, return NULL; } +/** + * i7core_get_active_channels() - gets the number of channels and csrows + * @socket: Quick Path Interconnect socket + * @channels: Number of channels that will be returned + * @csrows: Number of csrows found + * + * Since EDAC core needs to know in advance the number of available channels + * and csrows, in order to allocate memory for csrows/channels, it is needed + * to run two similar steps. At the first step, implemented on this function, + * it checks the number of csrows/channels present at one socket. + * this is used in order to properly allocate the size of mci components. + * + * It should be noticed that none of the current available datasheets explain + * or even mention how csrows are seen by the memory controller. So, we need + * to add a fake description for csrows. + * So, this driver is attributing one DIMM memory for one csrow. + */ static int i7core_get_active_channels(u8 socket, unsigned *channels, unsigned *csrows) { From 3a7dde7fcd0dd50df33e0e7070d4947551d767fc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 18 Jul 2009 12:20:04 -0300 Subject: [PATCH 32/83] i7core: add socket info at the debug msg Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index bf0374062e3d..5695413c2879 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1467,9 +1467,9 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, /* FIXME: should convert addr into bank and rank information */ msg = kasprintf(GFP_ATOMIC, - "%s (addr = 0x%08llx, Dimm=%d, Channel=%d, " + "%s (addr = 0x%08llx, socket=%d, Dimm=%d, Channel=%d, " "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n", - type, (long long) m->addr, dimm, channel, + type, (long long) m->addr, m->cpu, dimm, channel, syndrome, core_err_cnt, (long long)m->status, (long long)m->misc, optype, err); From 086271a0374bf0b9ce033aac9fb60530c421ad65 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 18 Jul 2009 12:22:28 -0300 Subject: [PATCH 33/83] i7core: remove some uneeded noisy debug messages Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 5695413c2879..fd00e0299ca1 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1494,8 +1494,6 @@ static void i7core_check_error(struct mem_ctl_info *mci) struct mce *m = NULL; unsigned long flags; - debugf0(__FILE__ ": %s()\n", __func__); - /* Copy all mce errors into a temporary buffer */ spin_lock_irqsave(&pvt->mce_lock, flags); if (pvt->mce_count) { @@ -1531,8 +1529,6 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) struct i7core_pvt *pvt = mci->pvt_info; unsigned long flags; - debugf0(__FILE__ ": %s()\n", __func__); - /* * Just let mcelog handle it if the error is * outside the memory controller From 17cb7b0cf78c14913c5410eff2ce03e1d9c8d958 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 20 Jul 2009 18:48:18 -0300 Subject: [PATCH 34/83] i7core_edac: Some cleanups at displayed info Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index fd00e0299ca1..86af14840b88 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -453,8 +453,8 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) pci_read_config_dword(pdev, MC_MAX_DOD, &pvt->info.max_dod); pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map); - debugf0("MC control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n", - pvt->info.mc_control, pvt->info.mc_status, + debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n", + socket, pvt->info.mc_control, pvt->info.mc_status, pvt->info.max_dod, pvt->info.ch_map); if (ECC_ENABLED(pvt)) { @@ -469,16 +469,14 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) } /* FIXME: need to handle the error codes */ - debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked\n", + debugf0("DOD Max limits: DIMMS: %d, %d-ranked, %d-banked " + "x%x x 0x%x\n", numdimms(pvt->info.max_dod), numrank(pvt->info.max_dod >> 2), numbank(pvt->info.max_dod >> 4)); - debugf0("DOD Max rows x colums = 0x%x x 0x%x\n", numrow(pvt->info.max_dod >> 6), numcol(pvt->info.max_dod >> 9)); - debugf0("Memory channel configuration:\n"); - for (i = 0; i < NUM_CHANS; i++) { u32 data, dimm_dod[3], value[8]; @@ -544,10 +542,9 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) pvt->channel[socket][i].dimms++; - debugf0("\tdimm %d (0x%08x) %d Mb offset: %x, " - "numbank: %d,\n\t\t" - "numrank: %d, numrow: %#x, numcol: %#x\n", - j, dimm_dod[j], size, + debugf0("\tdimm %d %d Mb offset: %x, " + "bank: %d, rank: %d, row: %#x, col: %#x\n", + j, size, RANKOFFSET(dimm_dod[j]), banks, ranks, rows, cols); @@ -599,9 +596,9 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) pci_read_config_dword(pdev, MC_SAG_CH_5, &value[5]); pci_read_config_dword(pdev, MC_SAG_CH_6, &value[6]); pci_read_config_dword(pdev, MC_SAG_CH_7, &value[7]); - debugf0("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i); + debugf1("\t[%i] DIVBY3\tREMOVED\tOFFSET\n", i); for (j = 0; j < 8; j++) - debugf0("\t\t%#x\t%#x\t%#x\n", + debugf1("\t\t%#x\t%#x\t%#x\n", (value[j] >> 27) & 0x1, (value[j] >> 24) & 0x7, (value[j] && ((1 << 24) - 1))); From 276b824c3095b09e8cb76f5709f56e9c6818ae85 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 22 Jul 2009 21:45:50 -0300 Subject: [PATCH 35/83] i7core_edac: some fixes at error injection code Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 112 ++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 86af14840b88..72859e87aeb2 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -32,9 +32,6 @@ #include "edac_core.h" -/* To use the new pci_[read/write]_config_qword instead of two dword */ -#define USE_QWORD 0 - /* * Alter this version for the module when modifications are made */ @@ -473,7 +470,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) "x%x x 0x%x\n", numdimms(pvt->info.max_dod), numrank(pvt->info.max_dod >> 2), - numbank(pvt->info.max_dod >> 4)); + numbank(pvt->info.max_dod >> 4), numrow(pvt->info.max_dod >> 6), numcol(pvt->info.max_dod >> 9)); @@ -646,7 +643,7 @@ static ssize_t i7core_inject_socket_store(struct mem_ctl_info *mci, int rc; rc = strict_strtoul(data, 10, &value); - if ((rc < 0) || (value > pvt->sockets)) + if ((rc < 0) || (value >= pvt->sockets)) return 0; pvt->inject.section = (u32) value; @@ -803,7 +800,7 @@ static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci, else return cmd - data; } else if (!strcasecmp(cmd, "dimm")) { - if (value < 4) + if (value < 3) pvt->inject.dimm = value; else return cmd - data; @@ -813,7 +810,7 @@ static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci, else return cmd - data; } else if (!strcasecmp(cmd, "bank")) { - if (value < 4) + if (value < 32) pvt->inject.bank = value; else return cmd - data; @@ -870,6 +867,28 @@ static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci, channel, dimm, bank, rank, page, col); } +static int write_and_test(struct pci_dev *dev, int where, u32 val) +{ + u32 read; + int count; + + for (count = 0; count < 10; count++) { + if (count) + msleep (100); + pci_write_config_dword(dev, where, val); + pci_read_config_dword(dev, where, &read); + + if (read == val) + return 0; + } + + debugf0("Error Injection Register 0x%02x: Tried to write 0x%08x, " + "but read: 0x%08x\n", where, val, read); + + return -EINVAL; +} + + /* * This routine prepares the Memory Controller for error injection. * The error will be injected when some process tries to write to the @@ -949,52 +968,6 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, else mask |= (pvt->inject.col & 0x3fffL); - /* Unlock writes to registers */ - pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket], - MC_CFG_CONTROL, 0x2); - msleep(100); - - /* Zeroes error count registers */ - pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4], - MC_TEST_ERR_RCV1, 0); - pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4], - MC_TEST_ERR_RCV0, 0); - pvt->ce_count_available[pvt->inject.socket] = 0; - - -#if USE_QWORD - pci_write_config_qword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], - MC_CHANNEL_ADDR_MATCH, mask); -#else - pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], - MC_CHANNEL_ADDR_MATCH, mask); - pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], - MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L); -#endif - -#if 1 -#if USE_QWORD - u64 rdmask; - pci_read_config_qword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], - MC_CHANNEL_ADDR_MATCH, &rdmask); - debugf0("Inject addr match write 0x%016llx, read: 0x%016llx\n", - mask, rdmask); -#else - u32 rdmask1, rdmask2; - - pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], - MC_CHANNEL_ADDR_MATCH, &rdmask1); - pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], - MC_CHANNEL_ADDR_MATCH + 4, &rdmask2); - - debugf0("Inject addr match write 0x%016llx, read: 0x%08x 0x%08x\n", - mask, rdmask1, rdmask2); -#endif -#endif - - pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], - MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask); - /* * bit 0: REPEAT_EN * bits 1-2: MASK_HALF_CACHELINE @@ -1006,13 +979,38 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, (pvt->inject.section & 0x3) << 1 | (pvt->inject.type & 0x6) << (3 - 1); - pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], - MC_CHANNEL_ERROR_MASK, injectmask); + /* Unlock writes to registers - this register is write only */ + pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket], + MC_CFG_CONTROL, 0x2); #if 0 - /* lock writes to registers */ - pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 0); + /* Zeroes error count registers */ + pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4], + MC_TEST_ERR_RCV1, 0); + pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4], + MC_TEST_ERR_RCV0, 0); + pvt->ce_count_available[pvt->inject.socket] = 0; #endif + + write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], + MC_CHANNEL_ADDR_MATCH, mask); + write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], + MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L); + + write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], + MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask); + + write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], + MC_CHANNEL_ERROR_MASK, injectmask); + + /* + * This is something undocumented, based on my tests + * Without writing 8 to this register, errors aren't injected. Not sure + * why. + */ + pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket], + MC_CFG_CONTROL, 8); + debugf0("Error inject addr match 0x%016llx, ecc 0x%08x," " inject 0x%08x\n", mask, pvt->inject.eccmask, injectmask); From 2068def56c09f2b24e6de04a1b84757a0fb07947 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 5 Aug 2009 19:28:27 -0300 Subject: [PATCH 36/83] i7core_edac: fix error codes for sysfs error injection interface Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 72859e87aeb2..ab9d26ec19d7 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -644,7 +644,7 @@ static ssize_t i7core_inject_socket_store(struct mem_ctl_info *mci, rc = strict_strtoul(data, 10, &value); if ((rc < 0) || (value >= pvt->sockets)) - return 0; + return -EIO; pvt->inject.section = (u32) value; return count; @@ -676,7 +676,7 @@ static ssize_t i7core_inject_section_store(struct mem_ctl_info *mci, rc = strict_strtoul(data, 10, &value); if ((rc < 0) || (value > 3)) - return 0; + return -EIO; pvt->inject.section = (u32) value; return count; @@ -709,7 +709,7 @@ static ssize_t i7core_inject_type_store(struct mem_ctl_info *mci, rc = strict_strtoul(data, 10, &value); if ((rc < 0) || (value > 7)) - return 0; + return -EIO; pvt->inject.type = (u32) value; return count; @@ -744,7 +744,7 @@ static ssize_t i7core_inject_eccmask_store(struct mem_ctl_info *mci, rc = strict_strtoul(data, 10, &value); if (rc < 0) - return 0; + return -EIO; pvt->inject.eccmask = (u32) value; return count; From 4157d9f55435331deef01ba8a9a47f248c042fb2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 5 Aug 2009 20:27:15 -0300 Subject: [PATCH 37/83] i7core_edac: fix error injection There were two stupid error injection bugs introduced by wrong cut-and-paste: one at socket store, and another at the error inject register. The last one were causing the code to not work at all. While here, adds debug messages to allow seeing what registers are being set while sending error injection. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index ab9d26ec19d7..f16aac253654 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -625,7 +625,7 @@ static int disable_inject(struct mem_ctl_info *mci) return -ENODEV; pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], - MC_CHANNEL_ERROR_MASK, 0); + MC_CHANNEL_ERROR_INJECT, 0); return 0; } @@ -646,7 +646,7 @@ static ssize_t i7core_inject_socket_store(struct mem_ctl_info *mci, if ((rc < 0) || (value >= pvt->sockets)) return -EIO; - pvt->inject.section = (u32) value; + pvt->inject.socket = (u32) value; return count; } @@ -872,6 +872,10 @@ static int write_and_test(struct pci_dev *dev, int where, u32 val) u32 read; int count; + debugf0("setting pci %02x:%02x.%x reg=%02x value=%08x\n", + dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + where, val); + for (count = 0; count < 10; count++) { if (count) msleep (100); @@ -882,8 +886,10 @@ static int write_and_test(struct pci_dev *dev, int where, u32 val) return 0; } - debugf0("Error Injection Register 0x%02x: Tried to write 0x%08x, " - "but read: 0x%08x\n", where, val, read); + i7core_printk(KERN_ERR, "Error during set pci %02x:%02x.%x reg=%02x " + "write=%08x. Read=%08x\n", + dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + where, val, read); return -EINVAL; } @@ -983,15 +989,6 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket], MC_CFG_CONTROL, 0x2); -#if 0 - /* Zeroes error count registers */ - pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4], - MC_TEST_ERR_RCV1, 0); - pci_write_config_dword(pvt->pci_mcr[pvt->inject.socket][4], - MC_TEST_ERR_RCV0, 0); - pvt->ce_count_available[pvt->inject.socket] = 0; -#endif - write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], MC_CHANNEL_ADDR_MATCH, mask); write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], @@ -1001,7 +998,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask); write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], - MC_CHANNEL_ERROR_MASK, injectmask); + MC_CHANNEL_ERROR_INJECT, injectmask); /* * This is something undocumented, based on my tests @@ -1026,7 +1023,7 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, u32 injectmask; pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], - MC_CHANNEL_ERROR_MASK, &injectmask); + MC_CHANNEL_ERROR_INJECT, &injectmask); debugf0("Inject error read: 0x%018x\n", injectmask); From 31983a04d686f9f90b356072089d8d677e40e776 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 5 Aug 2009 21:16:56 -0300 Subject: [PATCH 38/83] Documentation/edac.txt: Add Nehalem specific EDAC characteristics As Nehalem has a different binding to EDAC API, and its own different error injection code, documents it. Signed-off-by: Mauro Carvalho Chehab --- Documentation/edac.txt | 110 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/Documentation/edac.txt b/Documentation/edac.txt index 79c533223762..8bc320467c64 100644 --- a/Documentation/edac.txt +++ b/Documentation/edac.txt @@ -6,6 +6,8 @@ Written by Doug Thompson 7 Dec 2005 17 Jul 2007 Updated +(c) Mauro Carvalho Chehab +05 Aug 2009 Nehalem interface EDAC is maintained and written by: @@ -717,3 +719,111 @@ unique drivers for their hardware systems. The 'test_device_edac' sample driver is located at the bluesmoke.sourceforge.net project site for EDAC. +======================================================================= +NEHALEM USAGE OF EDAC APIs + +This chapter documents some EXPERIMENTAL mappings for EDAC API to handle +Nehalem EDAC driver. They will likely be changed on future versions +of the driver. + +Due to the way Nehalem exports Memory Controller data, some adjustments +were done at i7core_edac driver. This chapter will cover those differences + +1) On Nehalem, there are one Memory Controller per Quick Patch Interconnect + (QPI). At the driver, the term "socket" means one QPI. It should also be + associated with the CPU physical socket. + + Each MC have 3 physical read channels, 3 physical write channels and + 3 logic channels. The driver currenty sees it as just 3 channels. + Each channel can have up to 3 DIMMs. + + The minimum known unity is DIMMs. There are no information about csrows. + As EDAC API maps the minimum unity is csrows, the driver exports one + DIMM per csrow. + + Currently, it also exports the several memory controllers as just one. This + limit will be removed on future versions of the driver. + +2) Nehalem MC has the hability to generate errors. The driver implements this + functionality via some error injection nodes: + + For injecting a memory error, there are some sysfs nodes, under + /sys/devices/system/edac/mc/mc0/: + + inject_addrmatch: + Controls the error injection mask register. It is possible to specify + several characteristics of the address to match an error code: + dimm = the affected dimm. Numbers are relative to a channel; + rank = the memory rank; + channel = the channel that will generate an error; + bank = the affected bank; + page = the page address; + column (or col) = the address column. + each of the above values can be set to "any" to match any valid value. + + At driver init, all values are set to any. + + For example, to generate an error at rank 1 of dimm 2, for any channel, + any bank, any page, any column: + echo "dimm:2 rank:1" >/sys/devices/system/edac/mc/mc0/inject_addrmatch + + To return to the default behaviour of matching any, you can do: + echo "dimm:any rank:any" >/sys/devices/system/edac/mc/mc0/inject_addrmatch + + inject_eccmask: + specifies what bits will have troubles, + + inject_section: + specifies what ECC cache section will get the error: + 3 for both + 2 for the highest + 1 for the lowest + + inject_socket: + specifies what QPI (or processor socket) will generate the error. + on Xeon 35xx, it should be 0. + on Xeon 55xx, it should be 0 or 1. + + inject_type: + specifies the type of error, being a combination of the following bits: + bit 0 - repeat + bit 1 - ecc + bit 2 - parity + + inject_enable starts the error generation when something different + than 0 is written. + + All inject vars can be read. root permission is needed for write. + + Datasheet states that the error will only be generated after a write on an + address that matches inject_addrmatch. It seems, however, that reading will + also produce an error. + + For example, the following code will generate an error for any write access + at socket 0, on any DIMM/address on channel 2: + + echo "channel:2" > /sys/devices/system/edac/mc/mc0/inject_addrmatch + echo 2 >/sys/devices/system/edac/mc/mc0/inject_type + echo 64 >/sys/devices/system/edac/mc/mc0/inject_eccmask + echo 3 >/sys/devices/system/edac/mc/mc0/inject_section + echo 0 >/sys/devices/system/edac/mc/mc0/inject_socket + echo 1 >/sys/devices/system/edac/mc/mc0/inject_enable + dd if=/dev/mem of=/dev/null seek=16k bs=4k count=1 >& /dev/null + + The generated error message will look like: + + EDAC MC0: UE row 0, channel-a= 0 channel-b= 0 labels "-": NON_FATAL (addr = 0x0075b980, socket=0, Dimm=0, Channel=2, syndrome=0x00000040, count=1, Err=8c0000400001009f:4000080482 (read error: read ECC error)) + +3) Nehalem specific Corrected Error memory counters + + Nehalem have some registers to count memory errors, reporting it on a + way that it is different from what EDAC API allows. Due to that, a + separate sysfs note were created to handle such counters. + + They can be read by looking at the contents of "corrected_error_counts" + counter: + + $ cat /sys/devices/system/edac/mc/mc0/corrected_error_counts + dimm0: 15866 + dimm1: 0 + dimm2: 27285 From b990538a78ea84e89551ccaddf182beb5e16e6d2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 5 Aug 2009 21:36:35 -0300 Subject: [PATCH 39/83] i7core_edac: CodingSyle fixes/cleanups No functional changes. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 50 ++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index f16aac253654..86037a601b54 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -38,10 +38,6 @@ #define I7CORE_REVISION " Ver: 1.0.0 " __DATE__ #define EDAC_MOD_STR "i7core_edac" -/* HACK: temporary, just to enable all logs, for now */ -#undef debugf0 -#define debugf0(fmt, arg...) edac_printk(KERN_INFO, "i7core", fmt, ##arg) - /* * Debug macros */ @@ -105,6 +101,7 @@ #define REPEAT_EN 0x01 /* OFFSETS for Devices 4,5 and 6 Function 1 */ + #define MC_DOD_CH_DIMM0 0x48 #define MC_DOD_CH_DIMM1 0x4c #define MC_DOD_CH_DIMM2 0x50 @@ -227,7 +224,7 @@ struct pci_id_descr pci_devs[] = { /* Memory controller */ { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) }, { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) }, - { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM is supported */ + { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM */ { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) }, /* Channel 0 */ @@ -878,7 +875,7 @@ static int write_and_test(struct pci_dev *dev, int where, u32 val) for (count = 0; count < 10; count++) { if (count) - msleep (100); + msleep(100); pci_write_config_dword(dev, where, val); pci_read_config_dword(dev, where, &read); @@ -894,7 +891,6 @@ static int write_and_test(struct pci_dev *dev, int where, u32 val) return -EINVAL; } - /* * This routine prepares the Memory Controller for error injection. * The error will be injected when some process tries to write to the @@ -1326,7 +1322,7 @@ static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket) int new0, new1, new2; if (!pvt->pci_mcr[socket][4]) { - debugf0("%s MCR registers not found\n",__func__); + debugf0("%s MCR registers not found\n", __func__); return; } @@ -1405,24 +1401,24 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, type = "NON_FATAL"; switch (optypenum) { - case 0: - optype = "generic undef request"; - break; - case 1: - optype = "read error"; - break; - case 2: - optype = "write error"; - break; - case 3: - optype = "addr/cmd error"; - break; - case 4: - optype = "scrubbing error"; - break; - default: - optype = "reserved"; - break; + case 0: + optype = "generic undef request"; + break; + case 1: + optype = "read error"; + break; + case 2: + optype = "write error"; + break; + case 3: + optype = "addr/cmd error"; + break; + case 4: + optype = "scrubbing error"; + break; + default: + optype = "reserved"; + break; } switch (errnum) { @@ -1672,7 +1668,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, spin_lock_init(&pvt->mce_lock); rc = edac_mce_register(&pvt->edac_mce); - if (unlikely (rc < 0)) { + if (unlikely(rc < 0)) { debugf0("MC: " __FILE__ ": %s(): failed edac_mce_register()\n", __func__); goto fail1; From 3ef288a98307adc9d3f83321b26281567f348ec6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 2 Sep 2009 23:43:33 -0300 Subject: [PATCH 40/83] i7core_edac: Print an error message if pci register fails Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 86037a601b54..26205e2efecf 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1743,7 +1743,13 @@ static int __init i7core_init(void) pci_rc = pci_register_driver(&i7core_driver); - return (pci_rc < 0) ? pci_rc : 0; + if (pci_rc >= 0) + return 0; + + i7core_printk(KERN_ERR, "Failed to register device with error %d.\n", + pci_rc); + + return pci_rc; } /* From 61053fdedb2080dadc18dc37abbba90d2e74bc03 Mon Sep 17 00:00:00 2001 From: Keith Mannthey Date: Wed, 2 Sep 2009 23:46:59 -0300 Subject: [PATCH 41/83] i7core_edac: Fix ecc enable shift From: Keith Mannthey Simple correction to a shift value. ECC_ENABLED is bit 4 of MC_STATUS, Dev 3 Fun 0 Offset 0x4c This correctly identifies the state of the ECC at the machine. Signed-off-by: Keith Mannthey Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 26205e2efecf..87d5695f5fb0 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -286,7 +286,7 @@ static struct edac_pci_ctl_info *i7core_pci; #define ECCx8(pvt) ((pvt)->info.mc_control & (1 << 1)) /* MC_STATUS bits */ -#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 3)) +#define ECC_ENABLED(pvt) ((pvt)->info.mc_status & (1 << 4)) #define CH_DISABLED(pvt, ch) ((pvt)->info.mc_status & (1 << ch)) /* MC_MAX_DOD read functions */ From b4e8f0b6eaa1e99f1a64e539466a8ee2fb521d62 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 2 Sep 2009 23:49:59 -0300 Subject: [PATCH 42/83] i7core_edac: Use Device 3 function 2 to report errors with RDIMM's Nehalem and upper chipsets provide an special device that has corrected memory error counters detected with registered dimms. This device is only seen if there are registered memories plugged. After this patch, on a machine fully equiped with RDIMM's, it will use the Device 3 function 2 to count corrected errors instead on relying at mcelog. For unregistered DIMMs, it will keep the old behavior, counting errors via mcelog. This patch were developed together with Keith Mannthey Signed-off-by: Keith Mannthey Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 208 +++++++++++++++++++++++++++++++------ 1 file changed, 178 insertions(+), 30 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 87d5695f5fb0..4758c208f39a 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -73,6 +73,18 @@ #define DIMM1_COR_ERR(r) (((r) >> 16) & 0x7fff) #define DIMM0_COR_ERR(r) ((r) & 0x7fff) +/* OFFSETS for Device 3 Function 2, as inicated on Xeon 5500 datasheet */ +#define MC_COR_ECC_CNT_0 0x80 +#define MC_COR_ECC_CNT_1 0x84 +#define MC_COR_ECC_CNT_2 0x88 +#define MC_COR_ECC_CNT_3 0x8c +#define MC_COR_ECC_CNT_4 0x90 +#define MC_COR_ECC_CNT_5 0x94 + +#define DIMM_TOP_COR_ERR(r) (((r) >> 16) & 0x7fff) +#define DIMM_BOT_COR_ERR(r) ((r) & 0x7fff) + + /* OFFSETS for Devices 4,5 and 6 Function 0 */ #define MC_CHANNEL_DIMM_INIT_PARAMS 0x58 @@ -194,13 +206,20 @@ struct i7core_pvt { struct i7core_inject inject; struct i7core_channel channel[NUM_SOCKETS][NUM_CHANS]; + unsigned int is_registered:1; /* true if all memories are RDIMMs */ + int sockets; /* Number of sockets */ int channels; /* Number of active channels */ int ce_count_available[NUM_SOCKETS]; - /* ECC corrected errors counts per dimm */ - unsigned long ce_count[NUM_SOCKETS][MAX_DIMMS]; - int last_ce_count[NUM_SOCKETS][MAX_DIMMS]; + int csrow_map[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS]; + + /* ECC corrected errors counts per udimm */ + unsigned long udimm_ce_count[NUM_SOCKETS][MAX_DIMMS]; + int udimm_last_ce_count[NUM_SOCKETS][MAX_DIMMS]; + /* ECC corrected errors counts per rdimm */ + unsigned long rdimm_ce_count[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS]; + int rdimm_last_ce_count[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS]; /* mcelog glue */ struct edac_mce edac_mce; @@ -471,6 +490,8 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) numrow(pvt->info.max_dod >> 6), numcol(pvt->info.max_dod >> 9)); + pvt->is_registered = 1; + for (i = 0; i < NUM_CHANS; i++) { u32 data, dimm_dod[3], value[8]; @@ -492,8 +513,14 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) if (data & REGISTERED_DIMM) mtype = MEM_RDDR3; - else + else { mtype = MEM_DDR3; + /* + * FIXME: Currently, the driver will use dev 3:2 + * counter registers only if all memories are registered + */ + pvt->is_registered = 0; + } #if 0 if (data & THREE_DIMMS_PRESENT) pvt->channel[i].dimms = 3; @@ -562,6 +589,8 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) csr->channels[0].chan_idx = i; csr->channels[0].ce_count = 0; + pvt->csrow_map[socket][i][j] = *csrow; + switch (banks) { case 4: csr->dtype = DEV_X4; @@ -1031,19 +1060,31 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data) { - unsigned i, count, total = 0; + unsigned i, j, count, total = 0; struct i7core_pvt *pvt = mci->pvt_info; for (i = 0; i < pvt->sockets; i++) { - if (!pvt->ce_count_available[i]) + if (!pvt->ce_count_available[i]) { count = sprintf(data, "socket 0 data unavailable\n"); - else + continue; + } + if (!pvt->is_registered) count = sprintf(data, "socket %d, dimm0: %lu\n" "dimm1: %lu\ndimm2: %lu\n", i, - pvt->ce_count[i][0], - pvt->ce_count[i][1], - pvt->ce_count[i][2]); + pvt->udimm_ce_count[i][0], + pvt->udimm_ce_count[i][1], + pvt->udimm_ce_count[i][2]); + else + for (j = 0; j < NUM_CHANS; j++) { + count = sprintf(data, "socket %d, channel %d" + "dimm0: %lu\n" + "dimm1: %lu\ndimm2: %lu\n", + i, j, + pvt->rdimm_ce_count[i][j][0], + pvt->rdimm_ce_count[i][j][1], + pvt->rdimm_ce_count[i][j][2]); + } data += count; total += count; } @@ -1308,6 +1349,103 @@ static int mci_bind_devs(struct mem_ctl_info *mci) /**************************************************************************** Error check routines ****************************************************************************/ +static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci, int socket, + int chan, int dimm, int add) +{ + char *msg; + struct i7core_pvt *pvt = mci->pvt_info; + int row = pvt->csrow_map[socket][chan][dimm], i; + + for (i = 0; i < add; i++) { + msg = kasprintf(GFP_KERNEL, "Corrected error " + "(Socket=%d channel=%d dimm=%d", + socket, chan, dimm); + + edac_mc_handle_fbd_ce(mci, row, 0, msg); + kfree (msg); + } +} + +static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci, + int socket, int chan, int new0, int new1, int new2) +{ + struct i7core_pvt *pvt = mci->pvt_info; + int add0 = 0, add1 = 0, add2 = 0; + /* Updates CE counters if it is not the first time here */ + if (pvt->ce_count_available[socket]) { + /* Updates CE counters */ + + add2 = new2 - pvt->rdimm_last_ce_count[socket][chan][2]; + add1 = new1 - pvt->rdimm_last_ce_count[socket][chan][1]; + add0 = new0 - pvt->rdimm_last_ce_count[socket][chan][0]; + + if (add2 < 0) + add2 += 0x7fff; + pvt->rdimm_ce_count[socket][chan][2] += add2; + + if (add1 < 0) + add1 += 0x7fff; + pvt->rdimm_ce_count[socket][chan][1] += add1; + + if (add0 < 0) + add0 += 0x7fff; + pvt->rdimm_ce_count[socket][chan][0] += add0; + } else + pvt->ce_count_available[socket] = 1; + + /* Store the new values */ + pvt->rdimm_last_ce_count[socket][chan][2] = new2; + pvt->rdimm_last_ce_count[socket][chan][1] = new1; + pvt->rdimm_last_ce_count[socket][chan][0] = new0; + + /*updated the edac core */ + if (add0 != 0) + i7core_rdimm_update_csrow(mci, socket, chan, 0, add0); + if (add1 != 0) + i7core_rdimm_update_csrow(mci, socket, chan, 1, add1); + if (add2 != 0) + i7core_rdimm_update_csrow(mci, socket, chan, 2, add2); + +} + +static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci, u8 socket) +{ + struct i7core_pvt *pvt = mci->pvt_info; + u32 rcv[3][2]; + int i, new0, new1, new2; + + /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/ + pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_0, + &rcv[0][0]); + pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_1, + &rcv[0][1]); + pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_2, + &rcv[1][0]); + pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_3, + &rcv[1][1]); + pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_4, + &rcv[2][0]); + pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_5, + &rcv[2][1]); + for (i = 0 ; i < 3; i++) { + debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n", + (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]); + /*if the channel has 3 dimms*/ + if (pvt->channel[socket][i].dimms > 2) { + new0 = DIMM_BOT_COR_ERR(rcv[i][0]); + new1 = DIMM_TOP_COR_ERR(rcv[i][0]); + new2 = DIMM_BOT_COR_ERR(rcv[i][1]); + } else { + new0 = DIMM_TOP_COR_ERR(rcv[i][0]) + + DIMM_BOT_COR_ERR(rcv[i][0]); + new1 = DIMM_TOP_COR_ERR(rcv[i][1]) + + DIMM_BOT_COR_ERR(rcv[i][1]); + new2 = 0; + } + + i7core_rdimm_update_ce_count(mci, socket, i, new0, new1, new2); + } +} /* This function is based on the device 3 function 4 registers as described on: * Intel Xeon Processor 5500 Series Datasheet Volume 2 @@ -1315,7 +1453,7 @@ static int mci_bind_devs(struct mem_ctl_info *mci) * also available at: * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf */ -static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket) +static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci, u8 socket) { struct i7core_pvt *pvt = mci->pvt_info; u32 rcv1, rcv0; @@ -1326,7 +1464,7 @@ static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket) return; } - /* Corrected error reads */ + /* Corrected test errors */ pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1); pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0); @@ -1335,39 +1473,38 @@ static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket) new1 = DIMM1_COR_ERR(rcv0); new0 = DIMM0_COR_ERR(rcv0); -#if 0 - debugf2("%s CE rcv1=0x%08x rcv0=0x%08x, %d %d %d\n", - (pvt->ce_count_available ? "UPDATE" : "READ"), - rcv1, rcv0, new0, new1, new2); -#endif - /* Updates CE counters if it is not the first time here */ if (pvt->ce_count_available[socket]) { /* Updates CE counters */ int add0, add1, add2; - add2 = new2 - pvt->last_ce_count[socket][2]; - add1 = new1 - pvt->last_ce_count[socket][1]; - add0 = new0 - pvt->last_ce_count[socket][0]; + add2 = new2 - pvt->udimm_last_ce_count[socket][2]; + add1 = new1 - pvt->udimm_last_ce_count[socket][1]; + add0 = new0 - pvt->udimm_last_ce_count[socket][0]; if (add2 < 0) add2 += 0x7fff; - pvt->ce_count[socket][2] += add2; + pvt->udimm_ce_count[socket][2] += add2; if (add1 < 0) add1 += 0x7fff; - pvt->ce_count[socket][1] += add1; + pvt->udimm_ce_count[socket][1] += add1; if (add0 < 0) add0 += 0x7fff; - pvt->ce_count[socket][0] += add0; + pvt->udimm_ce_count[socket][0] += add0; + + if (add0 | add1 | add2) + i7core_printk(KERN_ERR, "New Corrected error(s): " + "dimm0: +%d, dimm1: +%d, dimm2 +%d\n", + add0, add1, add2); } else pvt->ce_count_available[socket] = 1; /* Store the new values */ - pvt->last_ce_count[socket][2] = new2; - pvt->last_ce_count[socket][1] = new1; - pvt->last_ce_count[socket][0] = new0; + pvt->udimm_last_ce_count[socket][2] = new2; + pvt->udimm_last_ce_count[socket][1] = new1; + pvt->udimm_last_ce_count[socket][0] = new0; } /* @@ -1386,6 +1523,7 @@ static void check_mc_test_err(struct mem_ctl_info *mci, u8 socket) static void i7core_mce_output_error(struct mem_ctl_info *mci, struct mce *m) { + struct i7core_pvt *pvt = mci->pvt_info; char *type, *optype, *err, *msg; unsigned long error = m->status & 0x1ff0000l; u32 optypenum = (m->status >> 4) & 0x07; @@ -1394,6 +1532,7 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, u32 channel = (m->misc >> 18) & 0x3; u32 syndrome = m->misc >> 32; u32 errnum = find_first_bit(&error, 32); + int csrow; if (m->mcgstatus & 1) type = "FATAL"; @@ -1463,9 +1602,15 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, debugf0("%s", msg); + csrow = pvt->csrow_map[m->cpu][channel][dimm]; + /* Call the helper to output message */ - edac_mc_handle_fbd_ue(mci, 0 /* FIXME: should be rank here */, - 0, 0 /* FIXME: should be channel here */, msg); + if (m->mcgstatus & 1) + edac_mc_handle_fbd_ue(mci, csrow, 0, + 0 /* FIXME: should be channel here */, msg); + else if (!pvt->is_registered) + edac_mc_handle_fbd_ce(mci, csrow, + 0 /* FIXME: should be channel here */, msg); kfree(msg); } @@ -1502,7 +1647,10 @@ static void i7core_check_error(struct mem_ctl_info *mci) /* check memory count errors */ for (i = 0; i < pvt->sockets; i++) - check_mc_test_err(mci, i); + if (!pvt->is_registered) + i7core_udimm_check_mc_ecc_err(mci, i); + else + i7core_rdimm_check_mc_ecc_err(mci, i); } /* From 14d2c08343eecd13f6c6ec232c98b16762b97924 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 2 Sep 2009 23:52:36 -0300 Subject: [PATCH 43/83] i7core: Use registered memories per processor Instead of assuming that the entire machine has either registered or unregistered memories, do it at CPU socket based. While here, fix a bug at i7core_mce_output_error(), where the we're using m->cpu directly as if it would represent a socket. Instead, the proper socket_id is given by cpu_data[m->cpu].phys_proc_id. Signed-off-by: Mauro Carvalho Chehab --- --- drivers/edac/i7core_edac.c | 39 ++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 4758c208f39a..9f4e9d7d4dbf 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "edac_core.h" @@ -206,8 +207,6 @@ struct i7core_pvt { struct i7core_inject inject; struct i7core_channel channel[NUM_SOCKETS][NUM_CHANS]; - unsigned int is_registered:1; /* true if all memories are RDIMMs */ - int sockets; /* Number of sockets */ int channels; /* Number of active channels */ @@ -221,6 +220,8 @@ struct i7core_pvt { unsigned long rdimm_ce_count[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS]; int rdimm_last_ce_count[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS]; + unsigned int is_registered[NUM_SOCKETS]; + /* mcelog glue */ struct edac_mce edac_mce; struct mce mce_entry[MCE_LOG_LEN]; @@ -490,8 +491,6 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) numrow(pvt->info.max_dod >> 6), numcol(pvt->info.max_dod >> 9)); - pvt->is_registered = 1; - for (i = 0; i < NUM_CHANS; i++) { u32 data, dimm_dod[3], value[8]; @@ -513,14 +512,8 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) if (data & REGISTERED_DIMM) mtype = MEM_RDDR3; - else { + else mtype = MEM_DDR3; - /* - * FIXME: Currently, the driver will use dev 3:2 - * counter registers only if all memories are registered - */ - pvt->is_registered = 0; - } #if 0 if (data & THREE_DIMMS_PRESENT) pvt->channel[i].dimms = 3; @@ -1068,7 +1061,7 @@ static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data) count = sprintf(data, "socket 0 data unavailable\n"); continue; } - if (!pvt->is_registered) + if (!pvt->is_registered[i]) count = sprintf(data, "socket %d, dimm0: %lu\n" "dimm1: %lu\ndimm2: %lu\n", i, @@ -1310,7 +1303,9 @@ static int mci_bind_devs(struct mem_ctl_info *mci) struct pci_dev *pdev; int i, j, func, slot; + for (i = 0; i < pvt->sockets; i++) { + pvt->is_registered[i] = 0; for (j = 0; j < N_DEVS; j++) { pdev = pci_devs[j].pdev[i]; if (!pdev) @@ -1334,6 +1329,10 @@ static int mci_bind_devs(struct mem_ctl_info *mci) debugf0("Associated fn %d.%d, dev = %p, socket %d\n", PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev, i); + + if (PCI_SLOT(pdev->devfn) == 3 && + PCI_FUNC(pdev->devfn) == 2) + pvt->is_registered[i] = 1; } } @@ -1533,6 +1532,11 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, u32 syndrome = m->misc >> 32; u32 errnum = find_first_bit(&error, 32); int csrow; +#ifdef CONFIG_SMP + u32 socket_id = cpu_data[m->cpu].phys_proc_id; +#else + u32 socket_id = 0; +#endif if (m->mcgstatus & 1) type = "FATAL"; @@ -1596,19 +1600,22 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, msg = kasprintf(GFP_ATOMIC, "%s (addr = 0x%08llx, socket=%d, Dimm=%d, Channel=%d, " "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n", - type, (long long) m->addr, m->cpu, dimm, channel, + type, (long long) m->addr, socket_id, dimm, channel, syndrome, core_err_cnt, (long long)m->status, (long long)m->misc, optype, err); debugf0("%s", msg); - csrow = pvt->csrow_map[m->cpu][channel][dimm]; + if (socket_id < NUM_SOCKETS) + csrow = pvt->csrow_map[socket_id][channel][dimm]; + else + csrow = -1; /* Call the helper to output message */ if (m->mcgstatus & 1) edac_mc_handle_fbd_ue(mci, csrow, 0, 0 /* FIXME: should be channel here */, msg); - else if (!pvt->is_registered) + else if (!pvt->is_registered[socket_id]) edac_mc_handle_fbd_ce(mci, csrow, 0 /* FIXME: should be channel here */, msg); @@ -1647,7 +1654,7 @@ static void i7core_check_error(struct mem_ctl_info *mci) /* check memory count errors */ for (i = 0; i < pvt->sockets; i++) - if (!pvt->is_registered) + if (!pvt->is_registered[i]) i7core_udimm_check_mc_ecc_err(mci, i); else i7core_rdimm_check_mc_ecc_err(mci, i); From bc2d7245ff1c5466c877a0c32a7ec9563187a032 Mon Sep 17 00:00:00 2001 From: Keith Mannthey Date: Thu, 3 Sep 2009 00:05:05 -0300 Subject: [PATCH 44/83] i7core_edac: Probe on Xeons eariler On the Xeon 55XX series cpus the pci deives are not exposed via acpi so we much explicitly probe them to make the usable as a Linux PCI device. This moves the detection of this state to before pci_register_driver is called. Its present position was not working on my systems, the driver would complain about not finding a specific device. This patch allows the driver to load on my systems. Signed-off-by: Keith Mannthey Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 9f4e9d7d4dbf..9fe7ec762e68 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1158,6 +1158,23 @@ static void i7core_put_devices(void) pci_dev_put(pci_devs[j].pdev[i]); } +static void i7core_xeon_pci_fixup(void) +{ + struct pci_dev *pdev = NULL; + int i; + /* + * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses + * aren't announced by acpi. So, we need to use a legacy scan probing + * to detect them + */ + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, + pci_devs[0].dev_id, NULL); + if (unlikely(!pdev)) { + for (i = 0; i < NUM_SOCKETS; i ++) + pcibios_scan_specific_bus(255-i); + } +} + /* * i7core_get_devices Find and perform 'get' operation on the MCH's * device/functions we want to reference for this driver @@ -1173,19 +1190,6 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno) pdev = pci_get_device(PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id, *prev); - /* - * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core pci buses - * aren't announced by acpi. So, we need to use a legacy scan probing - * to detect them - */ - if (unlikely(!pdev && !devno && !prev)) { - pcibios_scan_specific_bus(254); - pcibios_scan_specific_bus(255); - - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, - pci_devs[devno].dev_id, *prev); - } - /* * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs * is at addr 8086:2c40, instead of 8086:2c41. So, we need @@ -1896,6 +1900,8 @@ static int __init i7core_init(void) /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); + i7core_xeon_pci_fixup(); + pci_rc = pci_register_driver(&i7core_driver); if (pci_rc >= 0) From 3a3bb4a647db4cb2468641df8da2ee9491784a9a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 3 Sep 2009 20:17:26 -0300 Subject: [PATCH 45/83] i7core_edac: Improve corrected_error_counts output for RDIMM Just cosmetics. instead of showing something like: socket 0, channel 2dimm0: 1 dimm1: 0 dimm2: 0 socket 1, channel 2dimm0: 0 dimm1: 0 dimm2: 0 Show: socket 0, channel 2 RDIMM0: 1 RDIMM1: 0 RDIMM2: 0 socket 0, channel 2 RDIMM0: 0 RDIMM1: 0 RDIMM2: 0 This is more synthetic and easier to parse. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 9fe7ec762e68..76f4e6d973af 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1070,9 +1070,9 @@ static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data) pvt->udimm_ce_count[i][2]); else for (j = 0; j < NUM_CHANS; j++) { - count = sprintf(data, "socket %d, channel %d" - "dimm0: %lu\n" - "dimm1: %lu\ndimm2: %lu\n", + count = sprintf(data, "socket %d, channel %d " + "RDIMM0: %lu " + "RDIMM1: %lu RDIMM2: %lu\n", i, j, pvt->rdimm_ce_count[i][j][0], pvt->rdimm_ce_count[i][j][1], From a55456f3446d19853af54b64b3840312f46b6ea5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Sep 2009 00:47:21 -0300 Subject: [PATCH 46/83] i7core: temporary workaround to allow it to compile against 2.6.30 Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 76f4e6d973af..af222ffcfdfc 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1536,8 +1536,10 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, u32 syndrome = m->misc >> 32; u32 errnum = find_first_bit(&error, 32); int csrow; -#ifdef CONFIG_SMP - u32 socket_id = cpu_data[m->cpu].phys_proc_id; +/* FIXME */ +//#ifdef CONFIG_SMP +#if 0 + u32 socket_id = per_cpu(cpu_data, cpu).phys_proc_id; #else u32 socket_id = 0; #endif From 66607706cee7b6901aa0509198f075859c93ec6a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Sep 2009 00:52:11 -0300 Subject: [PATCH 47/83] Dynamically allocate memory for PCI devices Instead of using a static table assuming always 2 CPU sockets, allocate space dynamically for Nehalem PCI devs. This patch is part of a series of patches that changes i7core_edac to allow more than 2 sockets and to properly report one memory controller per socket. --- drivers/edac/i7core_edac.c | 175 ++++++++++++++++++++++++------------- 1 file changed, 114 insertions(+), 61 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index af222ffcfdfc..7bcb5993b501 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -192,10 +192,9 @@ struct i7core_channel { }; struct pci_id_descr { - int dev; - int func; - int dev_id; - struct pci_dev *pdev[NUM_SOCKETS]; + int dev; + int func; + int dev_id; }; struct i7core_pvt { @@ -229,6 +228,17 @@ struct i7core_pvt { spinlock_t mce_lock; }; +struct i7core_dev { + struct list_head list; + + int socket; + struct pci_dev **pdev; +}; + +/* Static vars */ +static LIST_HEAD(i7core_edac_list); +static DEFINE_MUTEX(i7core_edac_lock); + /* Device name and register DID (Device ID) */ struct i7core_dev_info { const char *ctl_name; /* name for this device */ @@ -240,7 +250,7 @@ struct i7core_dev_info { .func = (function), \ .dev_id = (device_id) -struct pci_id_descr pci_devs[] = { +struct pci_id_descr pci_dev_descr[] = { /* Memory controller */ { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) }, { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) }, @@ -275,11 +285,10 @@ struct pci_id_descr pci_devs[] = { { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) }, }; -#define N_DEVS ARRAY_SIZE(pci_devs) +#define N_DEVS ARRAY_SIZE(pci_dev_descr) /* * pci_device_id table for which devices we are looking for - * This should match the first device at pci_devs table */ static const struct pci_device_id i7core_pci_tbl[] __devinitdata = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)}, @@ -288,7 +297,7 @@ static const struct pci_device_id i7core_pci_tbl[] __devinitdata = { /* Table of devices attributes supported by this driver */ -static const struct i7core_dev_info i7core_devs[] = { +static const struct i7core_dev_info i7core_probe_devs[] = { { .ctl_name = "i7 Core", .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR, @@ -347,21 +356,37 @@ static inline int numcol(u32 col) return cols[col & 0x3]; } +static struct i7core_dev *get_i7core_dev(int socket) +{ + struct i7core_dev *i7core_dev; + + list_for_each_entry(i7core_dev, &i7core_edac_list, list) { + if (i7core_dev->socket == socket) + return i7core_dev; + } + + return NULL; +} + /**************************************************************************** Memory check routines ****************************************************************************/ static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot, unsigned func) { + struct i7core_dev *i7core_dev = get_i7core_dev(socket); int i; + if (!i7core_dev) + return NULL; + for (i = 0; i < N_DEVS; i++) { - if (!pci_devs[i].pdev[socket]) + if (!i7core_dev->pdev[i]) continue; - if (PCI_SLOT(pci_devs[i].pdev[socket]->devfn) == slot && - PCI_FUNC(pci_devs[i].pdev[socket]->devfn) == func) { - return pci_devs[i].pdev[socket]; + if (PCI_SLOT(i7core_dev->pdev[i]->devfn) == slot && + PCI_FUNC(i7core_dev->pdev[i]->devfn) == func) { + return i7core_dev->pdev[i]; } } @@ -1153,9 +1178,18 @@ static void i7core_put_devices(void) { int i, j; - for (i = 0; i < NUM_SOCKETS; i++) + for (i = 0; i < NUM_SOCKETS; i++) { + struct i7core_dev *i7core_dev = get_i7core_dev(i); + if (!i7core_dev) + continue; + for (j = 0; j < N_DEVS; j++) - pci_dev_put(pci_devs[j].pdev[i]); + pci_dev_put(i7core_dev->pdev[j]); + + list_del(&i7core_dev->list); + kfree(i7core_dev->pdev); + kfree(i7core_dev); + } } static void i7core_xeon_pci_fixup(void) @@ -1168,7 +1202,7 @@ static void i7core_xeon_pci_fixup(void) * to detect them */ pdev = pci_get_device(PCI_VENDOR_ID_INTEL, - pci_devs[0].dev_id, NULL); + pci_dev_descr[0].dev_id, NULL); if (unlikely(!pdev)) { for (i = 0; i < NUM_SOCKETS; i ++) pcibios_scan_specific_bus(255-i); @@ -1183,19 +1217,21 @@ static void i7core_xeon_pci_fixup(void) */ int i7core_get_onedevice(struct pci_dev **prev, int devno) { + struct i7core_dev *i7core_dev; + struct pci_dev *pdev = NULL; u8 bus = 0; u8 socket = 0; pdev = pci_get_device(PCI_VENDOR_ID_INTEL, - pci_devs[devno].dev_id, *prev); + pci_dev_descr[devno].dev_id, *prev); /* * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs * is at addr 8086:2c40, instead of 8086:2c41. So, we need * to probe for the alternate address in case of failure */ - if (pci_devs[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev) + if (pci_dev_descr[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev) pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev); @@ -1209,15 +1245,15 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno) * Dev 3 function 2 only exists on chips with RDIMMs * so, it is ok to not found it */ - if ((pci_devs[devno].dev == 3) && (pci_devs[devno].func == 2)) { + if ((pci_dev_descr[devno].dev == 3) && (pci_dev_descr[devno].func == 2)) { *prev = pdev; return 0; } i7core_printk(KERN_ERR, "Device not found: dev %02x.%d PCI ID %04x:%04x\n", - pci_devs[devno].dev, pci_devs[devno].func, - PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id); + pci_dev_descr[devno].dev, pci_dev_descr[devno].func, + PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id); /* End of list, leave */ return -ENODEV; @@ -1229,37 +1265,40 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno) else socket = 255 - bus; - if (socket >= NUM_SOCKETS) { - i7core_printk(KERN_ERR, - "Unexpected socket for " - "dev %02x:%02x.%d PCI ID %04x:%04x\n", - bus, pci_devs[devno].dev, pci_devs[devno].func, - PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id); - pci_dev_put(pdev); - return -ENODEV; + i7core_dev = get_i7core_dev(socket); + if (!i7core_dev) { + i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL); + if (!i7core_dev) + return -ENOMEM; + i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * N_DEVS, + GFP_KERNEL); + if (!i7core_dev->pdev) + return -ENOMEM; + i7core_dev->socket = socket; + list_add_tail(&i7core_dev->list, &i7core_edac_list); } - if (pci_devs[devno].pdev[socket]) { + if (i7core_dev->pdev[devno]) { i7core_printk(KERN_ERR, "Duplicated device for " "dev %02x:%02x.%d PCI ID %04x:%04x\n", - bus, pci_devs[devno].dev, pci_devs[devno].func, - PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id); + bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func, + PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id); pci_dev_put(pdev); return -ENODEV; } - pci_devs[devno].pdev[socket] = pdev; + i7core_dev->pdev[devno] = pdev; /* Sanity check */ - if (unlikely(PCI_SLOT(pdev->devfn) != pci_devs[devno].dev || - PCI_FUNC(pdev->devfn) != pci_devs[devno].func)) { + if (unlikely(PCI_SLOT(pdev->devfn) != pci_dev_descr[devno].dev || + PCI_FUNC(pdev->devfn) != pci_dev_descr[devno].func)) { i7core_printk(KERN_ERR, "Device PCI ID %04x:%04x " "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n", - PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id, + PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id, bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), - bus, pci_devs[devno].dev, pci_devs[devno].func); + bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func); return -ENODEV; } @@ -1268,27 +1307,29 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno) i7core_printk(KERN_ERR, "Couldn't enable " "dev %02x:%02x.%d PCI ID %04x:%04x\n", - bus, pci_devs[devno].dev, pci_devs[devno].func, - PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id); + bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func, + PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id); return -ENODEV; } i7core_printk(KERN_INFO, "Registered socket %d " "dev %02x:%02x.%d PCI ID %04x:%04x\n", - socket, bus, pci_devs[devno].dev, pci_devs[devno].func, - PCI_VENDOR_ID_INTEL, pci_devs[devno].dev_id); + socket, bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func, + PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id); *prev = pdev; return 0; } -static int i7core_get_devices(void) +static int i7core_get_devices(u8 *sockets) { int i; struct pci_dev *pdev = NULL; + struct i7core_dev *i7core_dev = NULL; + *sockets = 0; for (i = 0; i < N_DEVS; i++) { pdev = NULL; do { @@ -1298,6 +1339,12 @@ static int i7core_get_devices(void) } } while (pdev); } + + list_for_each_entry(i7core_dev, &i7core_edac_list, list) { + if (i7core_dev->socket + 1 > *sockets) + *sockets = i7core_dev->socket + 1; + } + return 0; } @@ -1307,11 +1354,15 @@ static int mci_bind_devs(struct mem_ctl_info *mci) struct pci_dev *pdev; int i, j, func, slot; - for (i = 0; i < pvt->sockets; i++) { + struct i7core_dev *i7core_dev = get_i7core_dev(i); + + if (!i7core_dev) + continue; + pvt->is_registered[i] = 0; for (j = 0; j < N_DEVS; j++) { - pdev = pci_devs[j].pdev[i]; + pdev = i7core_dev->pdev[j]; if (!pdev) continue; @@ -1723,20 +1774,17 @@ static int __devinit i7core_probe(struct pci_dev *pdev, int rc, i; u8 sockets; - if (unlikely(dev_idx >= ARRAY_SIZE(i7core_devs))) + /* + * FIXME: All memory controllers are allocated at the first pass. + */ + if (unlikely(dev_idx >= 1)) return -EINVAL; /* get the pci devices we want to reserve for our use */ - rc = i7core_get_devices(); + mutex_lock(&i7core_edac_lock); + rc = i7core_get_devices(&sockets); if (unlikely(rc < 0)) - return rc; - - sockets = 1; - for (i = NUM_SOCKETS - 1; i > 0; i--) - if (pci_devs[0].pdev[i]) { - sockets = i + 1; - break; - } + goto fail0; for (i = 0; i < sockets; i++) { int channels; @@ -1745,7 +1793,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, /* Check the number of active and not disabled channels */ rc = i7core_get_active_channels(i, &channels, &csrows); if (unlikely(rc < 0)) - goto fail0; + goto fail1; num_channels += channels; num_csrows += csrows; @@ -1755,7 +1803,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0); if (unlikely(!mci)) { rc = -ENOMEM; - goto fail0; + goto fail1; } debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); @@ -1776,7 +1824,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, mci->edac_cap = EDAC_FLAG_NONE; mci->mod_name = "i7core_edac.c"; mci->mod_ver = I7CORE_REVISION; - mci->ctl_name = i7core_devs[dev_idx].ctl_name; + mci->ctl_name = i7core_probe_devs[dev_idx].ctl_name; mci->dev_name = pci_name(pdev); mci->ctl_page_to_phys = NULL; mci->mc_driver_sysfs_attributes = i7core_inj_attrs; @@ -1786,7 +1834,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, /* Store pci devices at mci for faster access */ rc = mci_bind_devs(mci); if (unlikely(rc < 0)) - goto fail1; + goto fail2; /* Get dimm basic config */ for (i = 0; i < sockets; i++) @@ -1801,7 +1849,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, */ rc = -EINVAL; - goto fail1; + goto fail2; } /* allocating generic PCI control info */ @@ -1832,18 +1880,21 @@ static int __devinit i7core_probe(struct pci_dev *pdev, if (unlikely(rc < 0)) { debugf0("MC: " __FILE__ ": %s(): failed edac_mce_register()\n", __func__); - goto fail1; + goto fail2; } i7core_printk(KERN_INFO, "Driver loaded.\n"); + mutex_unlock(&i7core_edac_lock); return 0; -fail1: +fail2: edac_mc_free(mci); -fail0: +fail1: i7core_put_devices(); +fail0: + mutex_unlock(&i7core_edac_lock); return rc; } @@ -1871,7 +1922,9 @@ static void __devexit i7core_remove(struct pci_dev *pdev) edac_mce_unregister(&pvt->edac_mce); /* retrieve references to resources, and free those resources */ + mutex_lock(&i7core_edac_lock); i7core_put_devices(); + mutex_unlock(&i7core_edac_lock); edac_mc_free(mci); } From f47429494fd50c0b7396fe3f8a26ea638b47c5ba Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Sep 2009 02:35:08 -0300 Subject: [PATCH 48/83] i7core_edac: create one mc per socket/QPI Instead of creating just one memory controller, create one per socket (e. g. per Quick Link Path Interconnect). This better reflects the Nehalem architecture. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 507 +++++++++++++++++-------------------- 1 file changed, 228 insertions(+), 279 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 7bcb5993b501..5bc316b8e805 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -29,10 +29,20 @@ #include #include #include +#include #include #include "edac_core.h" +/* + * This is used for Nehalem-EP and Nehalem-EX devices, where the non-core + * registers start at bus 255, and are not reported by BIOS. + * We currently find devices with only 2 sockets. In order to support more QPI + * Quick Path Interconnect, just increment this number. + */ +#define MAX_SOCKET_BUSES 2 + + /* * Alter this version for the module when modifications are made */ @@ -162,7 +172,6 @@ #define NUM_CHANS 3 #define MAX_DIMMS 3 /* Max DIMMS per channel */ -#define NUM_SOCKETS 2 /* Max number of MC sockets */ #define MAX_MCR_FUNC 4 #define MAX_CHAN_FUNC 3 @@ -177,7 +186,6 @@ struct i7core_info { struct i7core_inject { int enable; - u8 socket; u32 section; u32 type; u32 eccmask; @@ -197,29 +205,37 @@ struct pci_id_descr { int dev_id; }; +struct i7core_dev { + struct list_head list; + u8 socket; + struct pci_dev **pdev; + struct mem_ctl_info *mci; +}; + struct i7core_pvt { - struct pci_dev *pci_noncore[NUM_SOCKETS]; - struct pci_dev *pci_mcr[NUM_SOCKETS][MAX_MCR_FUNC + 1]; - struct pci_dev *pci_ch[NUM_SOCKETS][NUM_CHANS][MAX_CHAN_FUNC + 1]; + struct pci_dev *pci_noncore; + struct pci_dev *pci_mcr[MAX_MCR_FUNC + 1]; + struct pci_dev *pci_ch[NUM_CHANS][MAX_CHAN_FUNC + 1]; + + struct i7core_dev *i7core_dev; struct i7core_info info; struct i7core_inject inject; - struct i7core_channel channel[NUM_SOCKETS][NUM_CHANS]; + struct i7core_channel channel[NUM_CHANS]; - int sockets; /* Number of sockets */ - int channels; /* Number of active channels */ + int channels; /* Number of active channels */ - int ce_count_available[NUM_SOCKETS]; - int csrow_map[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS]; + int ce_count_available; + int csrow_map[NUM_CHANS][MAX_DIMMS]; /* ECC corrected errors counts per udimm */ - unsigned long udimm_ce_count[NUM_SOCKETS][MAX_DIMMS]; - int udimm_last_ce_count[NUM_SOCKETS][MAX_DIMMS]; + unsigned long udimm_ce_count[MAX_DIMMS]; + int udimm_last_ce_count[MAX_DIMMS]; /* ECC corrected errors counts per rdimm */ - unsigned long rdimm_ce_count[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS]; - int rdimm_last_ce_count[NUM_SOCKETS][NUM_CHANS][MAX_DIMMS]; + unsigned long rdimm_ce_count[NUM_CHANS][MAX_DIMMS]; + int rdimm_last_ce_count[NUM_CHANS][MAX_DIMMS]; - unsigned int is_registered[NUM_SOCKETS]; + unsigned int is_registered; /* mcelog glue */ struct edac_mce edac_mce; @@ -228,22 +244,10 @@ struct i7core_pvt { spinlock_t mce_lock; }; -struct i7core_dev { - struct list_head list; - - int socket; - struct pci_dev **pdev; -}; - /* Static vars */ static LIST_HEAD(i7core_edac_list); static DEFINE_MUTEX(i7core_edac_lock); - -/* Device name and register DID (Device ID) */ -struct i7core_dev_info { - const char *ctl_name; /* name for this device */ - u16 fsb_mapping_errors; /* DID for the branchmap,control */ -}; +static u8 max_num_sockets; #define PCI_DESCR(device, function, device_id) \ .dev = (device), \ @@ -295,15 +299,6 @@ static const struct pci_device_id i7core_pci_tbl[] __devinitdata = { {0,} /* 0 terminated list. */ }; - -/* Table of devices attributes supported by this driver */ -static const struct i7core_dev_info i7core_probe_devs[] = { - { - .ctl_name = "i7 Core", - .fsb_mapping_errors = PCI_DEVICE_ID_INTEL_I7_MCR, - }, -}; - static struct edac_pci_ctl_info *i7core_pci; /**************************************************************************** @@ -356,7 +351,7 @@ static inline int numcol(u32 col) return cols[col & 0x3]; } -static struct i7core_dev *get_i7core_dev(int socket) +static struct i7core_dev *get_i7core_dev(u8 socket) { struct i7core_dev *i7core_dev; @@ -471,18 +466,19 @@ static int i7core_get_active_channels(u8 socket, unsigned *channels, return 0; } -static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) +static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) { struct i7core_pvt *pvt = mci->pvt_info; struct csrow_info *csr; struct pci_dev *pdev; int i, j; + u8 socket = pvt->i7core_dev->socket; unsigned long last_page = 0; enum edac_type mode; enum mem_type mtype; /* Get data from the MC register, function 0 */ - pdev = pvt->pci_mcr[socket][0]; + pdev = pvt->pci_mcr[0]; if (!pdev) return -ENODEV; @@ -529,10 +525,10 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) } /* Devices 4-6 function 0 */ - pci_read_config_dword(pvt->pci_ch[socket][i][0], + pci_read_config_dword(pvt->pci_ch[i][0], MC_CHANNEL_DIMM_INIT_PARAMS, &data); - pvt->channel[socket][i].ranks = (data & QUAD_RANK_PRESENT) ? + pvt->channel[i].ranks = (data & QUAD_RANK_PRESENT) ? 4 : 2; if (data & REGISTERED_DIMM) @@ -549,11 +545,11 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) #endif /* Devices 4-6 function 1 */ - pci_read_config_dword(pvt->pci_ch[socket][i][1], + pci_read_config_dword(pvt->pci_ch[i][1], MC_DOD_CH_DIMM0, &dimm_dod[0]); - pci_read_config_dword(pvt->pci_ch[socket][i][1], + pci_read_config_dword(pvt->pci_ch[i][1], MC_DOD_CH_DIMM1, &dimm_dod[1]); - pci_read_config_dword(pvt->pci_ch[socket][i][1], + pci_read_config_dword(pvt->pci_ch[i][1], MC_DOD_CH_DIMM2, &dimm_dod[2]); debugf0("Ch%d phy rd%d, wr%d (0x%08x): " @@ -561,7 +557,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) i, RDLCH(pvt->info.ch_map, i), WRLCH(pvt->info.ch_map, i), data, - pvt->channel[socket][i].ranks, + pvt->channel[i].ranks, (data & REGISTERED_DIMM) ? 'R' : 'U'); for (j = 0; j < 3; j++) { @@ -579,7 +575,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) /* DDR3 has 8 I/O banks */ size = (rows * cols * banks * ranks) >> (20 - 3); - pvt->channel[socket][i].dimms++; + pvt->channel[i].dimms++; debugf0("\tdimm %d %d Mb offset: %x, " "bank: %d, rank: %d, row: %#x, col: %#x\n", @@ -607,7 +603,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow, u8 socket) csr->channels[0].chan_idx = i; csr->channels[0].ce_count = 0; - pvt->csrow_map[socket][i][j] = *csrow; + pvt->csrow_map[i][j] = *csrow; switch (banks) { case 4: @@ -665,42 +661,15 @@ static int disable_inject(struct mem_ctl_info *mci) pvt->inject.enable = 0; - if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0]) + if (!pvt->pci_ch[pvt->inject.channel][0]) return -ENODEV; - pci_write_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], + pci_write_config_dword(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ERROR_INJECT, 0); return 0; } -/* - * i7core inject inject.socket - * - * accept and store error injection inject.socket value - */ -static ssize_t i7core_inject_socket_store(struct mem_ctl_info *mci, - const char *data, size_t count) -{ - struct i7core_pvt *pvt = mci->pvt_info; - unsigned long value; - int rc; - - rc = strict_strtoul(data, 10, &value); - if ((rc < 0) || (value >= pvt->sockets)) - return -EIO; - - pvt->inject.socket = (u32) value; - return count; -} - -static ssize_t i7core_inject_socket_show(struct mem_ctl_info *mci, - char *data) -{ - struct i7core_pvt *pvt = mci->pvt_info; - return sprintf(data, "%d\n", pvt->inject.socket); -} - /* * i7core inject inject.section * @@ -965,7 +934,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, int rc; long enable; - if (!pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0]) + if (!pvt->pci_ch[pvt->inject.channel][0]) return 0; rc = strict_strtoul(data, 10, &enable); @@ -983,7 +952,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, if (pvt->inject.dimm < 0) mask |= 1L << 41; else { - if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2) + if (pvt->channel[pvt->inject.channel].dimms > 2) mask |= (pvt->inject.dimm & 0x3L) << 35; else mask |= (pvt->inject.dimm & 0x1L) << 36; @@ -993,7 +962,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, if (pvt->inject.rank < 0) mask |= 1L << 40; else { - if (pvt->channel[pvt->inject.socket][pvt->inject.channel].dimms > 2) + if (pvt->channel[pvt->inject.channel].dimms > 2) mask |= (pvt->inject.rank & 0x1L) << 34; else mask |= (pvt->inject.rank & 0x3L) << 34; @@ -1029,18 +998,18 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, (pvt->inject.type & 0x6) << (3 - 1); /* Unlock writes to registers - this register is write only */ - pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket], + pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 0x2); - write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], + write_and_test(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ADDR_MATCH, mask); - write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], + write_and_test(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ADDR_MATCH + 4, mask >> 32L); - write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], + write_and_test(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ERROR_MASK, pvt->inject.eccmask); - write_and_test(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], + write_and_test(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ERROR_INJECT, injectmask); /* @@ -1048,7 +1017,7 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, * Without writing 8 to this register, errors aren't injected. Not sure * why. */ - pci_write_config_dword(pvt->pci_noncore[pvt->inject.socket], + pci_write_config_dword(pvt->pci_noncore, MC_CFG_CONTROL, 8); debugf0("Error inject addr match 0x%016llx, ecc 0x%08x," @@ -1065,7 +1034,7 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, struct i7core_pvt *pvt = mci->pvt_info; u32 injectmask; - pci_read_config_dword(pvt->pci_ch[pvt->inject.socket][pvt->inject.channel][0], + pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ERROR_INJECT, &injectmask); debugf0("Inject error read: 0x%018x\n", injectmask); @@ -1078,34 +1047,30 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data) { - unsigned i, j, count, total = 0; + unsigned i, count, total = 0; struct i7core_pvt *pvt = mci->pvt_info; - for (i = 0; i < pvt->sockets; i++) { - if (!pvt->ce_count_available[i]) { - count = sprintf(data, "socket 0 data unavailable\n"); - continue; - } - if (!pvt->is_registered[i]) - count = sprintf(data, "socket %d, dimm0: %lu\n" - "dimm1: %lu\ndimm2: %lu\n", - i, - pvt->udimm_ce_count[i][0], - pvt->udimm_ce_count[i][1], - pvt->udimm_ce_count[i][2]); - else - for (j = 0; j < NUM_CHANS; j++) { - count = sprintf(data, "socket %d, channel %d " - "RDIMM0: %lu " - "RDIMM1: %lu RDIMM2: %lu\n", - i, j, - pvt->rdimm_ce_count[i][j][0], - pvt->rdimm_ce_count[i][j][1], - pvt->rdimm_ce_count[i][j][2]); - } - data += count; - total += count; + if (!pvt->ce_count_available) { + count = sprintf(data, "data unavailable\n"); + return 0; } + if (!pvt->is_registered) + count = sprintf(data, "all channels " + "UDIMM0: %lu UDIMM1: %lu UDIMM2: %lu\n", + pvt->udimm_ce_count[0], + pvt->udimm_ce_count[1], + pvt->udimm_ce_count[2]); + else + for (i = 0; i < NUM_CHANS; i++) { + count = sprintf(data, "channel %d RDIMM0: %lu " + "RDIMM1: %lu RDIMM2: %lu\n", + i, + pvt->rdimm_ce_count[i][0], + pvt->rdimm_ce_count[i][1], + pvt->rdimm_ce_count[i][2]); + } + data += count; + total += count; return total; } @@ -1115,13 +1080,6 @@ static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data) */ static struct mcidev_sysfs_attribute i7core_inj_attrs[] = { { - .attr = { - .name = "inject_socket", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = i7core_inject_socket_show, - .store = i7core_inject_socket_store, - }, { .attr = { .name = "inject_section", .mode = (S_IRUGO | S_IWUSR) @@ -1178,7 +1136,7 @@ static void i7core_put_devices(void) { int i, j; - for (i = 0; i < NUM_SOCKETS; i++) { + for (i = 0; i < max_num_sockets; i++) { struct i7core_dev *i7core_dev = get_i7core_dev(i); if (!i7core_dev) continue; @@ -1204,7 +1162,7 @@ static void i7core_xeon_pci_fixup(void) pdev = pci_get_device(PCI_VENDOR_ID_INTEL, pci_dev_descr[0].dev_id, NULL); if (unlikely(!pdev)) { - for (i = 0; i < NUM_SOCKETS; i ++) + for (i = 0; i < MAX_SOCKET_BUSES; i++) pcibios_scan_specific_bus(255-i); } } @@ -1323,13 +1281,11 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno) return 0; } -static int i7core_get_devices(u8 *sockets) +static int i7core_get_devices(void) { int i; struct pci_dev *pdev = NULL; - struct i7core_dev *i7core_dev = NULL; - *sockets = 0; for (i = 0; i < N_DEVS; i++) { pdev = NULL; do { @@ -1340,55 +1296,48 @@ static int i7core_get_devices(u8 *sockets) } while (pdev); } - list_for_each_entry(i7core_dev, &i7core_edac_list, list) { - if (i7core_dev->socket + 1 > *sockets) - *sockets = i7core_dev->socket + 1; - } - return 0; } -static int mci_bind_devs(struct mem_ctl_info *mci) +static int mci_bind_devs(struct mem_ctl_info *mci, + struct i7core_dev *i7core_dev) { struct i7core_pvt *pvt = mci->pvt_info; struct pci_dev *pdev; - int i, j, func, slot; + int i, func, slot; - for (i = 0; i < pvt->sockets; i++) { - struct i7core_dev *i7core_dev = get_i7core_dev(i); + /* Associates i7core_dev and mci for future usage */ + pvt->i7core_dev = i7core_dev; + i7core_dev->mci = mci; - if (!i7core_dev) + pvt->is_registered = 0; + for (i = 0; i < N_DEVS; i++) { + pdev = i7core_dev->pdev[i]; + if (!pdev) continue; - pvt->is_registered[i] = 0; - for (j = 0; j < N_DEVS; j++) { - pdev = i7core_dev->pdev[j]; - if (!pdev) - continue; - - func = PCI_FUNC(pdev->devfn); - slot = PCI_SLOT(pdev->devfn); - if (slot == 3) { - if (unlikely(func > MAX_MCR_FUNC)) - goto error; - pvt->pci_mcr[i][func] = pdev; - } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) { - if (unlikely(func > MAX_CHAN_FUNC)) - goto error; - pvt->pci_ch[i][slot - 4][func] = pdev; - } else if (!slot && !func) - pvt->pci_noncore[i] = pdev; - else + func = PCI_FUNC(pdev->devfn); + slot = PCI_SLOT(pdev->devfn); + if (slot == 3) { + if (unlikely(func > MAX_MCR_FUNC)) goto error; + pvt->pci_mcr[func] = pdev; + } else if (likely(slot >= 4 && slot < 4 + NUM_CHANS)) { + if (unlikely(func > MAX_CHAN_FUNC)) + goto error; + pvt->pci_ch[slot - 4][func] = pdev; + } else if (!slot && !func) + pvt->pci_noncore = pdev; + else + goto error; - debugf0("Associated fn %d.%d, dev = %p, socket %d\n", - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), - pdev, i); + debugf0("Associated fn %d.%d, dev = %p, socket %d\n", + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), + pdev, i7core_dev->socket); - if (PCI_SLOT(pdev->devfn) == 3 && - PCI_FUNC(pdev->devfn) == 2) - pvt->is_registered[i] = 1; - } + if (PCI_SLOT(pdev->devfn) == 3 && + PCI_FUNC(pdev->devfn) == 2) + pvt->is_registered = 1; } return 0; @@ -1403,17 +1352,17 @@ static int mci_bind_devs(struct mem_ctl_info *mci) /**************************************************************************** Error check routines ****************************************************************************/ -static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci, int socket, +static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci, int chan, int dimm, int add) { char *msg; struct i7core_pvt *pvt = mci->pvt_info; - int row = pvt->csrow_map[socket][chan][dimm], i; + int row = pvt->csrow_map[chan][dimm], i; for (i = 0; i < add; i++) { msg = kasprintf(GFP_KERNEL, "Corrected error " - "(Socket=%d channel=%d dimm=%d", - socket, chan, dimm); + "(Socket=%d channel=%d dimm=%d)", + pvt->i7core_dev->socket, chan, dimm); edac_mc_handle_fbd_ce(mci, row, 0, msg); kfree (msg); @@ -1421,71 +1370,71 @@ static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci, int socket, } static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci, - int socket, int chan, int new0, int new1, int new2) + int chan, int new0, int new1, int new2) { struct i7core_pvt *pvt = mci->pvt_info; int add0 = 0, add1 = 0, add2 = 0; /* Updates CE counters if it is not the first time here */ - if (pvt->ce_count_available[socket]) { + if (pvt->ce_count_available) { /* Updates CE counters */ - add2 = new2 - pvt->rdimm_last_ce_count[socket][chan][2]; - add1 = new1 - pvt->rdimm_last_ce_count[socket][chan][1]; - add0 = new0 - pvt->rdimm_last_ce_count[socket][chan][0]; + add2 = new2 - pvt->rdimm_last_ce_count[chan][2]; + add1 = new1 - pvt->rdimm_last_ce_count[chan][1]; + add0 = new0 - pvt->rdimm_last_ce_count[chan][0]; if (add2 < 0) add2 += 0x7fff; - pvt->rdimm_ce_count[socket][chan][2] += add2; + pvt->rdimm_ce_count[chan][2] += add2; if (add1 < 0) add1 += 0x7fff; - pvt->rdimm_ce_count[socket][chan][1] += add1; + pvt->rdimm_ce_count[chan][1] += add1; if (add0 < 0) add0 += 0x7fff; - pvt->rdimm_ce_count[socket][chan][0] += add0; + pvt->rdimm_ce_count[chan][0] += add0; } else - pvt->ce_count_available[socket] = 1; + pvt->ce_count_available = 1; /* Store the new values */ - pvt->rdimm_last_ce_count[socket][chan][2] = new2; - pvt->rdimm_last_ce_count[socket][chan][1] = new1; - pvt->rdimm_last_ce_count[socket][chan][0] = new0; + pvt->rdimm_last_ce_count[chan][2] = new2; + pvt->rdimm_last_ce_count[chan][1] = new1; + pvt->rdimm_last_ce_count[chan][0] = new0; /*updated the edac core */ if (add0 != 0) - i7core_rdimm_update_csrow(mci, socket, chan, 0, add0); + i7core_rdimm_update_csrow(mci, chan, 0, add0); if (add1 != 0) - i7core_rdimm_update_csrow(mci, socket, chan, 1, add1); + i7core_rdimm_update_csrow(mci, chan, 1, add1); if (add2 != 0) - i7core_rdimm_update_csrow(mci, socket, chan, 2, add2); + i7core_rdimm_update_csrow(mci, chan, 2, add2); } -static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci, u8 socket) +static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; u32 rcv[3][2]; int i, new0, new1, new2; /*Read DEV 3: FUN 2: MC_COR_ECC_CNT regs directly*/ - pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_0, + pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_0, &rcv[0][0]); - pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_1, + pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_1, &rcv[0][1]); - pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_2, + pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_2, &rcv[1][0]); - pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_3, + pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_3, &rcv[1][1]); - pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_4, + pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_4, &rcv[2][0]); - pci_read_config_dword(pvt->pci_mcr[socket][2], MC_COR_ECC_CNT_5, + pci_read_config_dword(pvt->pci_mcr[2], MC_COR_ECC_CNT_5, &rcv[2][1]); for (i = 0 ; i < 3; i++) { debugf3("MC_COR_ECC_CNT%d = 0x%x; MC_COR_ECC_CNT%d = 0x%x\n", (i * 2), rcv[i][0], (i * 2) + 1, rcv[i][1]); /*if the channel has 3 dimms*/ - if (pvt->channel[socket][i].dimms > 2) { + if (pvt->channel[i].dimms > 2) { new0 = DIMM_BOT_COR_ERR(rcv[i][0]); new1 = DIMM_TOP_COR_ERR(rcv[i][0]); new2 = DIMM_BOT_COR_ERR(rcv[i][1]); @@ -1497,7 +1446,7 @@ static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci, u8 socket) new2 = 0; } - i7core_rdimm_update_ce_count(mci, socket, i, new0, new1, new2); + i7core_rdimm_update_ce_count(mci, i, new0, new1, new2); } } @@ -1507,20 +1456,20 @@ static void i7core_rdimm_check_mc_ecc_err(struct mem_ctl_info *mci, u8 socket) * also available at: * http://www.arrownac.com/manufacturers/intel/s/nehalem/5500-datasheet-v2.pdf */ -static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci, u8 socket) +static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; u32 rcv1, rcv0; int new0, new1, new2; - if (!pvt->pci_mcr[socket][4]) { + if (!pvt->pci_mcr[4]) { debugf0("%s MCR registers not found\n", __func__); return; } /* Corrected test errors */ - pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV1, &rcv1); - pci_read_config_dword(pvt->pci_mcr[socket][4], MC_TEST_ERR_RCV0, &rcv0); + pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV1, &rcv1); + pci_read_config_dword(pvt->pci_mcr[4], MC_TEST_ERR_RCV0, &rcv0); /* Store the new values */ new2 = DIMM2_COR_ERR(rcv1); @@ -1528,37 +1477,37 @@ static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci, u8 socket) new0 = DIMM0_COR_ERR(rcv0); /* Updates CE counters if it is not the first time here */ - if (pvt->ce_count_available[socket]) { + if (pvt->ce_count_available) { /* Updates CE counters */ int add0, add1, add2; - add2 = new2 - pvt->udimm_last_ce_count[socket][2]; - add1 = new1 - pvt->udimm_last_ce_count[socket][1]; - add0 = new0 - pvt->udimm_last_ce_count[socket][0]; + add2 = new2 - pvt->udimm_last_ce_count[2]; + add1 = new1 - pvt->udimm_last_ce_count[1]; + add0 = new0 - pvt->udimm_last_ce_count[0]; if (add2 < 0) add2 += 0x7fff; - pvt->udimm_ce_count[socket][2] += add2; + pvt->udimm_ce_count[2] += add2; if (add1 < 0) add1 += 0x7fff; - pvt->udimm_ce_count[socket][1] += add1; + pvt->udimm_ce_count[1] += add1; if (add0 < 0) add0 += 0x7fff; - pvt->udimm_ce_count[socket][0] += add0; + pvt->udimm_ce_count[0] += add0; if (add0 | add1 | add2) i7core_printk(KERN_ERR, "New Corrected error(s): " "dimm0: +%d, dimm1: +%d, dimm2 +%d\n", add0, add1, add2); } else - pvt->ce_count_available[socket] = 1; + pvt->ce_count_available = 1; /* Store the new values */ - pvt->udimm_last_ce_count[socket][2] = new2; - pvt->udimm_last_ce_count[socket][1] = new1; - pvt->udimm_last_ce_count[socket][0] = new0; + pvt->udimm_last_ce_count[2] = new2; + pvt->udimm_last_ce_count[1] = new1; + pvt->udimm_last_ce_count[0] = new0; } /* @@ -1587,13 +1536,6 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, u32 syndrome = m->misc >> 32; u32 errnum = find_first_bit(&error, 32); int csrow; -/* FIXME */ -//#ifdef CONFIG_SMP -#if 0 - u32 socket_id = per_cpu(cpu_data, cpu).phys_proc_id; -#else - u32 socket_id = 0; -#endif if (m->mcgstatus & 1) type = "FATAL"; @@ -1655,24 +1597,21 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, /* FIXME: should convert addr into bank and rank information */ msg = kasprintf(GFP_ATOMIC, - "%s (addr = 0x%08llx, socket=%d, Dimm=%d, Channel=%d, " + "%s (addr = 0x%08llx, cpu=%d, Dimm=%d, Channel=%d, " "syndrome=0x%08x, count=%d, Err=%08llx:%08llx (%s: %s))\n", - type, (long long) m->addr, socket_id, dimm, channel, + type, (long long) m->addr, m->cpu, dimm, channel, syndrome, core_err_cnt, (long long)m->status, (long long)m->misc, optype, err); debugf0("%s", msg); - if (socket_id < NUM_SOCKETS) - csrow = pvt->csrow_map[socket_id][channel][dimm]; - else - csrow = -1; + csrow = pvt->csrow_map[channel][dimm]; /* Call the helper to output message */ if (m->mcgstatus & 1) edac_mc_handle_fbd_ue(mci, csrow, 0, 0 /* FIXME: should be channel here */, msg); - else if (!pvt->is_registered[socket_id]) + else if (!pvt->is_registered) edac_mc_handle_fbd_ce(mci, csrow, 0 /* FIXME: should be channel here */, msg); @@ -1695,12 +1634,14 @@ static void i7core_check_error(struct mem_ctl_info *mci) spin_lock_irqsave(&pvt->mce_lock, flags); if (pvt->mce_count) { m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC); + if (m) { count = pvt->mce_count; memcpy(m, &pvt->mce_entry, sizeof(*m) * count); } pvt->mce_count = 0; } + spin_unlock_irqrestore(&pvt->mce_lock, flags); /* proccess mcelog errors */ @@ -1710,11 +1651,10 @@ static void i7core_check_error(struct mem_ctl_info *mci) kfree(m); /* check memory count errors */ - for (i = 0; i < pvt->sockets; i++) - if (!pvt->is_registered[i]) - i7core_udimm_check_mc_ecc_err(mci, i); - else - i7core_rdimm_check_mc_ecc_err(mci, i); + if (!pvt->is_registered) + i7core_udimm_check_mc_ecc_err(mci); + else + i7core_rdimm_check_mc_ecc_err(mci); } /* @@ -1740,6 +1680,10 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) if (mce->bank != 8) return 0; + /* Only handle if it is the right mc controller */ + if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket) + return 0; + spin_lock_irqsave(&pvt->mce_lock, flags); if (pvt->mce_count < MCE_LOG_LEN) { memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce)); @@ -1755,63 +1699,26 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) return 1; } -/* - * i7core_probe Probe for ONE instance of device to see if it is - * present. - * return: - * 0 for FOUND a device - * < 0 for error code - */ -static int __devinit i7core_probe(struct pci_dev *pdev, - const struct pci_device_id *id) +static int i7core_register_mci(struct i7core_dev *i7core_dev, + int num_channels, int num_csrows) { struct mem_ctl_info *mci; struct i7core_pvt *pvt; - int num_channels = 0; - int num_csrows = 0; int csrow = 0; - int dev_idx = id->driver_data; - int rc, i; - u8 sockets; - - /* - * FIXME: All memory controllers are allocated at the first pass. - */ - if (unlikely(dev_idx >= 1)) - return -EINVAL; - - /* get the pci devices we want to reserve for our use */ - mutex_lock(&i7core_edac_lock); - rc = i7core_get_devices(&sockets); - if (unlikely(rc < 0)) - goto fail0; - - for (i = 0; i < sockets; i++) { - int channels; - int csrows; - - /* Check the number of active and not disabled channels */ - rc = i7core_get_active_channels(i, &channels, &csrows); - if (unlikely(rc < 0)) - goto fail1; - - num_channels += channels; - num_csrows += csrows; - } + int rc; /* allocate a new MC control structure */ mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0); - if (unlikely(!mci)) { - rc = -ENOMEM; - goto fail1; - } + if (unlikely(!mci)) + return -ENOMEM; debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); - mci->dev = &pdev->dev; /* record ptr to the generic device */ + /* record ptr to the generic device */ + mci->dev = &i7core_dev->pdev[0]->dev; + pvt = mci->pvt_info; memset(pvt, 0, sizeof(*pvt)); - pvt->sockets = sockets; mci->mc_idx = 0; /* @@ -1824,21 +1731,21 @@ static int __devinit i7core_probe(struct pci_dev *pdev, mci->edac_cap = EDAC_FLAG_NONE; mci->mod_name = "i7core_edac.c"; mci->mod_ver = I7CORE_REVISION; - mci->ctl_name = i7core_probe_devs[dev_idx].ctl_name; - mci->dev_name = pci_name(pdev); + mci->ctl_name = kasprintf(GFP_KERNEL, "i7 core #%d", + i7core_dev->socket); + mci->dev_name = pci_name(i7core_dev->pdev[0]); mci->ctl_page_to_phys = NULL; mci->mc_driver_sysfs_attributes = i7core_inj_attrs; /* Set the function pointer to an actual operation function */ mci->edac_check = i7core_check_error; /* Store pci devices at mci for faster access */ - rc = mci_bind_devs(mci); + rc = mci_bind_devs(mci, i7core_dev); if (unlikely(rc < 0)) - goto fail2; + goto fail; /* Get dimm basic config */ - for (i = 0; i < sockets; i++) - get_dimm_config(mci, &csrow, i); + get_dimm_config(mci, &csrow); /* add this new MC control structure to EDAC's list of MCs */ if (unlikely(edac_mc_add_mc(mci))) { @@ -1849,11 +1756,12 @@ static int __devinit i7core_probe(struct pci_dev *pdev, */ rc = -EINVAL; - goto fail2; + goto fail; } /* allocating generic PCI control info */ - i7core_pci = edac_pci_create_generic_ctl(&pdev->dev, EDAC_MOD_STR); + i7core_pci = edac_pci_create_generic_ctl(&i7core_dev->pdev[0]->dev, + EDAC_MOD_STR); if (unlikely(!i7core_pci)) { printk(KERN_WARNING "%s(): Unable to create PCI control\n", @@ -1880,7 +1788,50 @@ static int __devinit i7core_probe(struct pci_dev *pdev, if (unlikely(rc < 0)) { debugf0("MC: " __FILE__ ": %s(): failed edac_mce_register()\n", __func__); - goto fail2; + } + +fail: + edac_mc_free(mci); + return rc; +} + +/* + * i7core_probe Probe for ONE instance of device to see if it is + * present. + * return: + * 0 for FOUND a device + * < 0 for error code + */ +static int __devinit i7core_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int dev_idx = id->driver_data; + int rc; + struct i7core_dev *i7core_dev; + + /* + * FIXME: All memory controllers are allocated at the first pass. + */ + if (unlikely(dev_idx >= 1)) + return -EINVAL; + + /* get the pci devices we want to reserve for our use */ + mutex_lock(&i7core_edac_lock); + rc = i7core_get_devices(); + if (unlikely(rc < 0)) + goto fail0; + + list_for_each_entry(i7core_dev, &i7core_edac_list, list) { + int channels; + int csrows; + + /* Check the number of active and not disabled channels */ + rc = i7core_get_active_channels(i7core_dev->socket, + &channels, &csrows); + if (unlikely(rc < 0)) + goto fail1; + + i7core_register_mci(i7core_dev, channels, csrows); } i7core_printk(KERN_INFO, "Driver loaded.\n"); @@ -1888,9 +1839,6 @@ static int __devinit i7core_probe(struct pci_dev *pdev, mutex_unlock(&i7core_edac_lock); return 0; -fail2: - edac_mc_free(mci); - fail1: i7core_put_devices(); fail0: @@ -1926,6 +1874,7 @@ static void __devexit i7core_remove(struct pci_dev *pdev) i7core_put_devices(); mutex_unlock(&i7core_edac_lock); + kfree(mci->ctl_name); edac_mc_free(mci); } From 6c6aa3afdba2460cb668d4cb65c74dfa8eb43449 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Sep 2009 03:27:04 -0300 Subject: [PATCH 49/83] i7core_edac: sanity check: print a warning if a mcelog is ignored In thesis, the other mc controller should handle it. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 5bc316b8e805..335d9ed02c45 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1681,8 +1681,13 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) return 0; /* Only handle if it is the right mc controller */ - if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket) + if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket) { + debugf0("mc%d: ignoring mce log for socket %d. " + "Another mc should get it.\n", + pvt->i7core_dev->socket, + cpu_data(mce->cpu).phys_proc_id); return 0; + } spin_lock_irqsave(&pvt->mce_lock, flags); if (pvt->mce_count < MCE_LOG_LEN) { From d4c277957f4e8e6f2b626e2661cbbf9c76782e36 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Sep 2009 04:12:02 -0300 Subject: [PATCH 50/83] i7core_edac: a few fixes for multiple mc's Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 335d9ed02c45..2c30493eae0f 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1270,11 +1270,10 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno) return -ENODEV; } - i7core_printk(KERN_INFO, - "Registered socket %d " - "dev %02x:%02x.%d PCI ID %04x:%04x\n", - socket, bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func, - PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id); + debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n", + socket, bus, pci_dev_descr[devno].dev, + pci_dev_descr[devno].func, + PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id); *prev = pdev; @@ -1713,7 +1712,8 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev, int rc; /* allocate a new MC control structure */ - mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, 0); + mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, + i7core_dev->socket); if (unlikely(!mci)) return -ENOMEM; @@ -1724,7 +1724,6 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev, pvt = mci->pvt_info; memset(pvt, 0, sizeof(*pvt)); - mci->mc_idx = 0; /* * FIXME: how to handle RDDR3 at MCI level? It is possible to have @@ -1815,7 +1814,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, struct i7core_dev *i7core_dev; /* - * FIXME: All memory controllers are allocated at the first pass. + * All memory controllers are allocated at the first pass. */ if (unlikely(dev_idx >= 1)) return -EINVAL; @@ -1836,7 +1835,9 @@ static int __devinit i7core_probe(struct pci_dev *pdev, if (unlikely(rc < 0)) goto fail1; - i7core_register_mci(i7core_dev, channels, csrows); + rc = i7core_register_mci(i7core_dev, channels, csrows); + if (unlikely(rc < 0)) + goto fail1; } i7core_printk(KERN_INFO, "Driver loaded.\n"); @@ -1876,6 +1877,8 @@ static void __devexit i7core_remove(struct pci_dev *pdev) /* retrieve references to resources, and free those resources */ mutex_lock(&i7core_edac_lock); + + /* FIXME: This should put the devices only for this mci!!! */ i7core_put_devices(); mutex_unlock(&i7core_edac_lock); From c344436319e898784febbeeea71d1b0f65ef53ae Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Sep 2009 05:10:15 -0300 Subject: [PATCH 51/83] Documentation/edac.txt: Improve it to reflect the latest changes at the driver Signed-off-by: Mauro Carvalho Chehab --- Documentation/edac.txt | 72 ++++++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/Documentation/edac.txt b/Documentation/edac.txt index 8bc320467c64..bd3f8a3905af 100644 --- a/Documentation/edac.txt +++ b/Documentation/edac.txt @@ -730,25 +730,41 @@ Due to the way Nehalem exports Memory Controller data, some adjustments were done at i7core_edac driver. This chapter will cover those differences 1) On Nehalem, there are one Memory Controller per Quick Patch Interconnect - (QPI). At the driver, the term "socket" means one QPI. It should also be - associated with the CPU physical socket. + (QPI). At the driver, the term "socket" means one QPI. This is + associated with a physical CPU socket. Each MC have 3 physical read channels, 3 physical write channels and 3 logic channels. The driver currenty sees it as just 3 channels. Each channel can have up to 3 DIMMs. The minimum known unity is DIMMs. There are no information about csrows. - As EDAC API maps the minimum unity is csrows, the driver exports one + As EDAC API maps the minimum unity is csrows, the driver sequencially + maps channel/dimm into different csrows. + + For example, suposing the following layout: + Ch0 phy rd0, wr0 (0x063f4031): 2 ranks, UDIMMs + dimm 0 1024 Mb offset: 0, bank: 8, rank: 1, row: 0x4000, col: 0x400 + dimm 1 1024 Mb offset: 4, bank: 8, rank: 1, row: 0x4000, col: 0x400 + Ch1 phy rd1, wr1 (0x063f4031): 2 ranks, UDIMMs + dimm 0 1024 Mb offset: 0, bank: 8, rank: 1, row: 0x4000, col: 0x400 + Ch2 phy rd3, wr3 (0x063f4031): 2 ranks, UDIMMs + dimm 0 1024 Mb offset: 0, bank: 8, rank: 1, row: 0x4000, col: 0x400 + The driver will map it as: + csrow0: channel 0, dimm0 + csrow1: channel 0, dimm1 + csrow2: channel 1, dimm0 + csrow3: channel 2, dimm0 + +exports one DIMM per csrow. - Currently, it also exports the several memory controllers as just one. This - limit will be removed on future versions of the driver. + Each QPI is exported as a different memory controller. 2) Nehalem MC has the hability to generate errors. The driver implements this functionality via some error injection nodes: For injecting a memory error, there are some sysfs nodes, under - /sys/devices/system/edac/mc/mc0/: + /sys/devices/system/edac/mc/mc?/: inject_addrmatch: Controls the error injection mask register. It is possible to specify @@ -779,11 +795,6 @@ were done at i7core_edac driver. This chapter will cover those differences 2 for the highest 1 for the lowest - inject_socket: - specifies what QPI (or processor socket) will generate the error. - on Xeon 35xx, it should be 0. - on Xeon 55xx, it should be 0 or 1. - inject_type: specifies the type of error, being a combination of the following bits: bit 0 - repeat @@ -806,10 +817,12 @@ were done at i7core_edac driver. This chapter will cover those differences echo 2 >/sys/devices/system/edac/mc/mc0/inject_type echo 64 >/sys/devices/system/edac/mc/mc0/inject_eccmask echo 3 >/sys/devices/system/edac/mc/mc0/inject_section - echo 0 >/sys/devices/system/edac/mc/mc0/inject_socket echo 1 >/sys/devices/system/edac/mc/mc0/inject_enable dd if=/dev/mem of=/dev/null seek=16k bs=4k count=1 >& /dev/null + For socket 1, it is needed to replace "mc0" by "mc1" at the above + commands. + The generated error message will look like: EDAC MC0: UE row 0, channel-a= 0 channel-b= 0 labels "-": NON_FATAL (addr = 0x0075b980, socket=0, Dimm=0, Channel=2, syndrome=0x00000040, count=1, Err=8c0000400001009f:4000080482 (read error: read ECC error)) @@ -821,9 +834,36 @@ were done at i7core_edac driver. This chapter will cover those differences separate sysfs note were created to handle such counters. They can be read by looking at the contents of "corrected_error_counts" - counter: + counter. Due to hardware limits, the output is different on machines + with unregistered memories and machines with registered ones. + + With unregistered memories, it outputs: $ cat /sys/devices/system/edac/mc/mc0/corrected_error_counts - dimm0: 15866 - dimm1: 0 - dimm2: 27285 + all channels UDIMM0: 0 UDIMM1: 0 UDIMM2: 0 + + What happens here is that errors on different csrows, but at the same + dimm number will increment the same counter. + So, in this memory mapping: + csrow0: channel 0, dimm0 + csrow1: channel 0, dimm1 + csrow2: channel 1, dimm0 + csrow3: channel 2, dimm0 + The hardware will increment UDIMM0 for an error at either csrow0, csrow2 + or csrow3. + + With registered memories, it outputs: + + $cat /sys/devices/system/edac/mc/mc0/corrected_error_counts + channel 0 RDIMM0: 0 RDIMM1: 0 RDIMM2: 0 + channel 1 RDIMM0: 0 RDIMM1: 0 RDIMM2: 0 + channel 2 RDIMM0: 0 RDIMM1: 0 RDIMM2: 0 + + So, with registered memories, there's a direct map between a csrow and a + counter. + +4) Standard error counters + + The standard error counters are generated when an mcelog error is received + by the driver. Since it is counted by software, it is possible that some + errors could be lost. From d88b85072fa7d406f54c30ceeabcd37e5a2ec21a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Sep 2009 05:10:31 -0300 Subject: [PATCH 52/83] i7core_edac: Fix a bug when printing error counts with RDIMMs Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 2c30493eae0f..821e8a1a09cf 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1054,13 +1054,15 @@ static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data) count = sprintf(data, "data unavailable\n"); return 0; } - if (!pvt->is_registered) + if (!pvt->is_registered) { count = sprintf(data, "all channels " "UDIMM0: %lu UDIMM1: %lu UDIMM2: %lu\n", pvt->udimm_ce_count[0], pvt->udimm_ce_count[1], pvt->udimm_ce_count[2]); - else + data += count; + total += count; + } else { for (i = 0; i < NUM_CHANS; i++) { count = sprintf(data, "channel %d RDIMM0: %lu " "RDIMM1: %lu RDIMM2: %lu\n", @@ -1068,9 +1070,10 @@ static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data) pvt->rdimm_ce_count[i][0], pvt->rdimm_ce_count[i][1], pvt->rdimm_ce_count[i][2]); - } - data += count; - total += count; + data += count; + total += count; + } + } return total; } From 13d6e9b653e6f714024c67287c7d7eac54d8417b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Sep 2009 12:15:20 -0300 Subject: [PATCH 53/83] i7core_edac: at remove, don't remove all pci devices at once Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 821e8a1a09cf..c2266f820b0f 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1135,22 +1135,24 @@ static struct mcidev_sysfs_attribute i7core_inj_attrs[] = { * i7core_put_devices 'put' all the devices that we have * reserved via 'get' */ -static void i7core_put_devices(void) +static void i7core_put_devices(struct i7core_dev *i7core_dev) { - int i, j; + int i; - for (i = 0; i < max_num_sockets; i++) { - struct i7core_dev *i7core_dev = get_i7core_dev(i); - if (!i7core_dev) - continue; + for (i = 0; i < N_DEVS; i++) + pci_dev_put(i7core_dev->pdev[i]); - for (j = 0; j < N_DEVS; j++) - pci_dev_put(i7core_dev->pdev[j]); + list_del(&i7core_dev->list); + kfree(i7core_dev->pdev); + kfree(i7core_dev); +} - list_del(&i7core_dev->list); - kfree(i7core_dev->pdev); - kfree(i7core_dev); - } +static void i7core_put_all_devices(void) +{ + struct i7core_dev *i7core_dev; + + list_for_each_entry(i7core_dev, &i7core_edac_list, list) + i7core_put_devices(i7core_dev); } static void i7core_xeon_pci_fixup(void) @@ -1292,7 +1294,7 @@ static int i7core_get_devices(void) pdev = NULL; do { if (i7core_get_onedevice(&pdev, i) < 0) { - i7core_put_devices(); + i7core_put_all_devices(); return -ENODEV; } } while (pdev); @@ -1849,7 +1851,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, return 0; fail1: - i7core_put_devices(); + i7core_put_all_devices(); fail0: mutex_unlock(&i7core_edac_lock); return rc; @@ -1863,6 +1865,7 @@ static void __devexit i7core_remove(struct pci_dev *pdev) { struct mem_ctl_info *mci; struct i7core_pvt *pvt; + struct i7core_dev *i7core_dev; debugf0(__FILE__ ": %s()\n", __func__); @@ -1876,13 +1879,12 @@ static void __devexit i7core_remove(struct pci_dev *pdev) /* Unregisters on edac_mce in order to receive memory errors */ pvt = mci->pvt_info; + i7core_dev = pvt->i7core_dev; edac_mce_unregister(&pvt->edac_mce); /* retrieve references to resources, and free those resources */ mutex_lock(&i7core_edac_lock); - - /* FIXME: This should put the devices only for this mci!!! */ - i7core_put_devices(); + i7core_put_devices(i7core_dev); mutex_unlock(&i7core_edac_lock); kfree(mci->ctl_name); From 0f062792b48dc8389fb18cbfb9318625886644c7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Sep 2009 12:16:19 -0300 Subject: [PATCH 54/83] i7core_edac: remove static counter for max sockets The number of sockets is now fully dynamic. Get rid of this obsolete var. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index c2266f820b0f..391348bf93d2 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -247,7 +247,6 @@ struct i7core_pvt { /* Static vars */ static LIST_HEAD(i7core_edac_list); static DEFINE_MUTEX(i7core_edac_lock); -static u8 max_num_sockets; #define PCI_DESCR(device, function, device_id) \ .dev = (device), \ From 22e6bcbdcf9279321dbe646c5a234b816db12881 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Sep 2009 23:06:50 -0300 Subject: [PATCH 55/83] i7core_edac: change remove module strategy The old remove module stragegy didn't work on devices with multiple cores, since only one PCI device is used to open all mc's, due to Nehalem nature. Also, it were based at pdev value. However, this doesn't point to the pci device used at mci->dev. So, instead, it unregisters all devices at once, deleting them from the device list. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 55 ++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 391348bf93d2..c3fec5de3e51 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1138,11 +1138,18 @@ static void i7core_put_devices(struct i7core_dev *i7core_dev) { int i; - for (i = 0; i < N_DEVS; i++) - pci_dev_put(i7core_dev->pdev[i]); - - list_del(&i7core_dev->list); + debugf0(__FILE__ ": %s()\n", __func__); + for (i = 0; i < N_DEVS; i++) { + struct pci_dev *pdev = i7core_dev->pdev[i]; + if (!pdev) + continue; + debugf0("Removing dev %02x:%02x.%d\n", + pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + pci_dev_put(pdev); + } kfree(i7core_dev->pdev); + list_del(&i7core_dev->list); kfree(i7core_dev); } @@ -1863,31 +1870,39 @@ static int __devinit i7core_probe(struct pci_dev *pdev, static void __devexit i7core_remove(struct pci_dev *pdev) { struct mem_ctl_info *mci; - struct i7core_pvt *pvt; - struct i7core_dev *i7core_dev; + struct i7core_dev *i7core_dev, *tmp; debugf0(__FILE__ ": %s()\n", __func__); if (i7core_pci) edac_pci_release_generic_ctl(i7core_pci); + /* + * we have a trouble here: pdev value for removal will be wrong, since + * it will point to the X58 register used to detect that the machine + * is a Nehalem or upper design. However, due to the way several PCI + * devices are grouped together to provide MC functionality, we need + * to use a different method for releasing the devices + */ - mci = edac_mc_del_mc(&pdev->dev); - if (!mci) - return; - - /* Unregisters on edac_mce in order to receive memory errors */ - pvt = mci->pvt_info; - i7core_dev = pvt->i7core_dev; - edac_mce_unregister(&pvt->edac_mce); - - /* retrieve references to resources, and free those resources */ mutex_lock(&i7core_edac_lock); - i7core_put_devices(i7core_dev); - mutex_unlock(&i7core_edac_lock); + list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) { + mci = edac_mc_del_mc(&i7core_dev->pdev[0]->dev); + if (mci) { + struct i7core_pvt *pvt = mci->pvt_info; - kfree(mci->ctl_name); - edac_mc_free(mci); + i7core_dev = pvt->i7core_dev; + edac_mce_unregister(&pvt->edac_mce); + kfree(mci->ctl_name); + edac_mc_free(mci); + i7core_put_devices(i7core_dev); + } else { + i7core_printk(KERN_ERR, + "Couldn't find mci for socket %d\n", + i7core_dev->socket); + } + } + mutex_unlock(&i7core_edac_lock); } MODULE_DEVICE_TABLE(pci, i7core_pci_tbl); From 4253868034221db6e42dbbb61e0305fe1757f8da Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 24 Sep 2009 09:59:13 -0300 Subject: [PATCH 56/83] i7core_edac: We need to use list_for_each_entry_safe to avoid errors Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index c3fec5de3e51..2e4b0abf9d43 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1124,6 +1124,7 @@ static struct mcidev_sysfs_attribute i7core_inj_attrs[] = { .show = i7core_ce_regs_show, .store = NULL, }, + { .attr = { .name = NULL } } }; /**************************************************************************** @@ -1155,9 +1156,9 @@ static void i7core_put_devices(struct i7core_dev *i7core_dev) static void i7core_put_all_devices(void) { - struct i7core_dev *i7core_dev; + struct i7core_dev *i7core_dev, *tmp; - list_for_each_entry(i7core_dev, &i7core_edac_list, list) + list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) i7core_put_devices(i7core_dev); } From 4af91889e02c9933823ca8c62fc6f05dfd15f3bd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 24 Sep 2009 09:58:26 -0300 Subject: [PATCH 57/83] i7core_edac: Avoid printing a warning when debug is disabled Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 2e4b0abf9d43..0478cc85e920 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -471,7 +471,6 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) struct csrow_info *csr; struct pci_dev *pdev; int i, j; - u8 socket = pvt->i7core_dev->socket; unsigned long last_page = 0; enum edac_type mode; enum mem_type mtype; @@ -488,7 +487,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) pci_read_config_dword(pdev, MC_CHANNEL_MAPPER, &pvt->info.ch_map); debugf0("QPI %d control=0x%08x status=0x%08x dod=0x%08x map=0x%08x\n", - socket, pvt->info.mc_control, pvt->info.mc_status, + pvt->i7core_dev->socket, pvt->info.mc_control, pvt->info.mc_status, pvt->info.max_dod, pvt->info.ch_map); if (ECC_ENABLED(pvt)) { From 9fa2fc2e2d641df7d69dc4e06cf2552c44b58e95 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 23 Sep 2009 16:26:09 -0300 Subject: [PATCH 58/83] edac_core: Allow the creation of sysfs groups Currently, all sysfs nodes are stored at /sys/.*/mc. (regex) However, sometimes it is needed to create attribute groups. This patch extends edac_core to allow groups creation. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_core.h | 12 +++++- drivers/edac/edac_mc_sysfs.c | 83 ++++++++++++++++++++++++------------ 2 files changed, 67 insertions(+), 28 deletions(-) diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index 001b2e797fb3..97071ff1d22d 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h @@ -341,12 +341,22 @@ struct csrow_info { struct channel_info *channels; }; +struct mcidev_sysfs_group { + const char *name; + struct mcidev_sysfs_attribute *mcidev_attr; + struct kobject kobj; +}; + + /* mcidev_sysfs_attribute structure * used for driver sysfs attributes and in mem_ctl_info * sysfs top level entries */ struct mcidev_sysfs_attribute { - struct attribute attr; + struct attribute attr; + + struct mcidev_sysfs_group *grp; + ssize_t (*show)(struct mem_ctl_info *,char *); ssize_t (*store)(struct mem_ctl_info *, const char *,size_t); }; diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 418b65f1a1da..655aa1a1f4f9 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -728,26 +728,43 @@ void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) /* * edac_create_mci_instance_attributes - * create MC driver specific attributes at the topmost level - * directory of this mci instance. + * create MC driver specific attributes bellow an specified kobj + * This routine calls itself recursively, in order to create an entire + * object tree. */ -static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci) +static int edac_create_mci_instance_attributes( + struct mcidev_sysfs_attribute *sysfs_attrib, + struct kobject *kobj) { int err; - struct mcidev_sysfs_attribute *sysfs_attrib; - /* point to the start of the array and iterate over it - * adding each attribute listed to this mci instance's kobject - */ - sysfs_attrib = mci->mc_driver_sysfs_attributes; + while (sysfs_attrib) { + if (sysfs_attrib->grp) { + struct kobject *newkobj = &sysfs_attrib->grp->kobj; + debugf0("%s() grp %s\n", __func__, + sysfs_attrib->grp->name); + + err = kobject_init_and_add(newkobj, NULL, + kobj, + sysfs_attrib->grp->name); + if (err) + return err; + + err = edac_create_mci_instance_attributes( + sysfs_attrib->grp->mcidev_attr, newkobj); + if (err) + return err; + } else if (sysfs_attrib->attr.name) { + debugf0("%s() file %s\n", __func__, + sysfs_attrib->attr.name); + + err = sysfs_create_file(kobj, &sysfs_attrib->attr); + } else + break; - while (sysfs_attrib && sysfs_attrib->attr.name) { - err = sysfs_create_file(&mci->edac_mci_kobj, - (struct attribute*) sysfs_attrib); if (err) { return err; } - sysfs_attrib++; } @@ -759,19 +776,28 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci) * remove MC driver specific attributes at the topmost level * directory of this mci instance. */ -static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci) +static void edac_remove_mci_instance_attributes( + struct mcidev_sysfs_attribute *sysfs_attrib, + struct kobject *kobj) { - struct mcidev_sysfs_attribute *sysfs_attrib; - - /* point to the start of the array and iterate over it - * adding each attribute listed to this mci instance's kobject - */ - sysfs_attrib = mci->mc_driver_sysfs_attributes; - /* loop if there are attributes and until we hit a NULL entry */ - while (sysfs_attrib && sysfs_attrib->attr.name) { - sysfs_remove_file(&mci->edac_mci_kobj, - (struct attribute *) sysfs_attrib); + while (sysfs_attrib) { + if (sysfs_attrib->grp) { + struct kobject *newkobj = &sysfs_attrib->grp->kobj; + + debugf0("%s() grp %s\n", __func__, + sysfs_attrib->grp->name); + + edac_remove_mci_instance_attributes( + sysfs_attrib->grp->mcidev_attr, newkobj); + + kobject_put(newkobj); + } else if (sysfs_attrib->attr.name) { + debugf0("%s() file %s\n", __func__, + sysfs_attrib->attr.name); + sysfs_remove_file(kobj, &sysfs_attrib->attr); + } else + break; sysfs_attrib++; } } @@ -806,7 +832,9 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) * then create them now for the driver. */ if (mci->mc_driver_sysfs_attributes) { - err = edac_create_mci_instance_attributes(mci); + err = edac_create_mci_instance_attributes( + mci->mc_driver_sysfs_attributes, + &mci->edac_mci_kobj); if (err) { debugf1("%s() failure to create mci attributes\n", __func__); @@ -841,7 +869,8 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) } /* remove the mci instance's attributes, if any */ - edac_remove_mci_instance_attributes(mci); + edac_remove_mci_instance_attributes( + mci->mc_driver_sysfs_attributes, &mci->edac_mci_kobj); /* remove the symlink */ sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK); @@ -875,8 +904,8 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) debugf0("%s() remove_mci_instance\n", __func__); /* remove this mci instance's attribtes */ - edac_remove_mci_instance_attributes(mci); - + edac_remove_mci_instance_attributes(mci->mc_driver_sysfs_attributes, + &mci->edac_mci_kobj); debugf0("%s() unregister this mci kobj\n", __func__); /* unregister this instance's kobject */ From a5538e531fc1e00ac7185dcfcebf33c37b5d742e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 23 Sep 2009 18:56:47 -0300 Subject: [PATCH 59/83] i7core_edac: Add support for sysfs addrmatch group Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 173 +++++++++++++++---------------------- 1 file changed, 70 insertions(+), 103 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 0478cc85e920..afa5281e8df9 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -778,105 +778,59 @@ static ssize_t i7core_inject_eccmask_show(struct mem_ctl_info *mci, * 23:16 and 31:24). Flipping bits in two symbol pairs will cause an * uncorrectable error to be injected. */ -static ssize_t i7core_inject_addrmatch_store(struct mem_ctl_info *mci, - const char *data, size_t count) -{ - struct i7core_pvt *pvt = mci->pvt_info; - char *cmd, *val; - long value; - int rc; - if (pvt->inject.enable) - disable_inject(mci); - - do { - cmd = strsep((char **) &data, ":"); - if (!cmd) - break; - val = strsep((char **) &data, " \n\t"); - if (!val) - return cmd - data; - - if (!strcasecmp(val, "any")) - value = -1; - else { - rc = strict_strtol(val, 10, &value); - if ((rc < 0) || (value < 0)) - return cmd - data; - } - - if (!strcasecmp(cmd, "channel")) { - if (value < 3) - pvt->inject.channel = value; - else - return cmd - data; - } else if (!strcasecmp(cmd, "dimm")) { - if (value < 3) - pvt->inject.dimm = value; - else - return cmd - data; - } else if (!strcasecmp(cmd, "rank")) { - if (value < 4) - pvt->inject.rank = value; - else - return cmd - data; - } else if (!strcasecmp(cmd, "bank")) { - if (value < 32) - pvt->inject.bank = value; - else - return cmd - data; - } else if (!strcasecmp(cmd, "page")) { - if (value <= 0xffff) - pvt->inject.page = value; - else - return cmd - data; - } else if (!strcasecmp(cmd, "col") || - !strcasecmp(cmd, "column")) { - if (value <= 0x3fff) - pvt->inject.col = value; - else - return cmd - data; - } - } while (1); - - return count; +#define DECLARE_ADDR_MATCH(param, limit) \ +static ssize_t i7core_inject_store_##param( \ + struct mem_ctl_info *mci, \ + const char *data, size_t count) \ +{ \ + struct i7core_pvt *pvt = mci->pvt_info; \ + long value; \ + int rc; \ + \ + if (pvt->inject.enable) \ + disable_inject(mci); \ + \ + if (!strcasecmp(data, "any")) \ + value = -1; \ + else { \ + rc = strict_strtoul(data, 10, &value); \ + if ((rc < 0) || (value >= limit)) \ + return -EIO; \ + } \ + \ + pvt->inject.param = value; \ + \ + return count; \ +} \ + \ +static ssize_t i7core_inject_show_##param( \ + struct mem_ctl_info *mci, \ + char *data) \ +{ \ + struct i7core_pvt *pvt = mci->pvt_info; \ + if (pvt->inject.param < 0) \ + return sprintf(data, "any\n"); \ + else \ + return sprintf(data, "%d\n", pvt->inject.param);\ } -static ssize_t i7core_inject_addrmatch_show(struct mem_ctl_info *mci, - char *data) -{ - struct i7core_pvt *pvt = mci->pvt_info; - char channel[4], dimm[4], bank[4], rank[4], page[7], col[7]; +#define ATTR_ADDR_MATCH(param) \ + { \ + .attr = { \ + .name = #param, \ + .mode = (S_IRUGO | S_IWUSR) \ + }, \ + .show = i7core_inject_show_##param, \ + .store = i7core_inject_store_##param, \ + } - if (pvt->inject.channel < 0) - sprintf(channel, "any"); - else - sprintf(channel, "%d", pvt->inject.channel); - if (pvt->inject.dimm < 0) - sprintf(dimm, "any"); - else - sprintf(dimm, "%d", pvt->inject.dimm); - if (pvt->inject.bank < 0) - sprintf(bank, "any"); - else - sprintf(bank, "%d", pvt->inject.bank); - if (pvt->inject.rank < 0) - sprintf(rank, "any"); - else - sprintf(rank, "%d", pvt->inject.rank); - if (pvt->inject.page < 0) - sprintf(page, "any"); - else - sprintf(page, "0x%04x", pvt->inject.page); - if (pvt->inject.col < 0) - sprintf(col, "any"); - else - sprintf(col, "0x%04x", pvt->inject.col); - - return sprintf(data, "channel: %s\ndimm: %s\nbank: %s\n" - "rank: %s\npage: %s\ncolumn: %s\n", - channel, dimm, bank, rank, page, col); -} +DECLARE_ADDR_MATCH(channel, 3); +DECLARE_ADDR_MATCH(dimm, 3); +DECLARE_ADDR_MATCH(rank, 4); +DECLARE_ADDR_MATCH(bank, 32); +DECLARE_ADDR_MATCH(page, 0x10000); +DECLARE_ADDR_MATCH(col, 0x4000); static int write_and_test(struct pci_dev *dev, int where, u32 val) { @@ -1079,7 +1033,25 @@ static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data) /* * Sysfs struct */ -static struct mcidev_sysfs_attribute i7core_inj_attrs[] = { + + +static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = { + ATTR_ADDR_MATCH(channel), + ATTR_ADDR_MATCH(dimm), + ATTR_ADDR_MATCH(rank), + ATTR_ADDR_MATCH(bank), + ATTR_ADDR_MATCH(page), + ATTR_ADDR_MATCH(col), + { .attr = { .name = NULL } } +}; + + +static struct mcidev_sysfs_group i7core_inject_addrmatch = { + .name = "inject_addrmatch", + .mcidev_attr = i7core_addrmatch_attrs, +}; + +static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = { { .attr = { .name = "inject_section", @@ -1102,12 +1074,7 @@ static struct mcidev_sysfs_attribute i7core_inj_attrs[] = { .show = i7core_inject_eccmask_show, .store = i7core_inject_eccmask_store, }, { - .attr = { - .name = "inject_addrmatch", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = i7core_inject_addrmatch_show, - .store = i7core_inject_addrmatch_store, + .grp = &i7core_inject_addrmatch, }, { .attr = { .name = "inject_enable", @@ -1750,7 +1717,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev, i7core_dev->socket); mci->dev_name = pci_name(i7core_dev->pdev[0]); mci->ctl_page_to_phys = NULL; - mci->mc_driver_sysfs_attributes = i7core_inj_attrs; + mci->mc_driver_sysfs_attributes = i7core_sysfs_attrs; /* Set the function pointer to an actual operation function */ mci->edac_check = i7core_check_error; From cc301b3ae3f615fe243f023e68e22b8298a6f883 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 24 Sep 2009 16:23:42 -0300 Subject: [PATCH 60/83] edac: store/show methods for device groups weren't working Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_core.h | 2 + drivers/edac/edac_mc_sysfs.c | 85 +++++++++++++++++++++++++++++++++--- drivers/edac/i7core_edac.c | 10 ++++- 3 files changed, 88 insertions(+), 9 deletions(-) diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index 97071ff1d22d..02bbbc9696d9 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h @@ -345,6 +345,8 @@ struct mcidev_sysfs_group { const char *name; struct mcidev_sysfs_attribute *mcidev_attr; struct kobject kobj; + + struct mem_ctl_info *mci; /* the parent */ }; diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 655aa1a1f4f9..6088ae6e8ea5 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -557,6 +557,8 @@ static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr, struct mem_ctl_info *mem_ctl_info = to_mci(kobj); struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); + debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); + if (mcidev_attr->show) return mcidev_attr->show(mem_ctl_info, buffer); @@ -569,6 +571,8 @@ static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, struct mem_ctl_info *mem_ctl_info = to_mci(kobj); struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); + debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); + if (mcidev_attr->store) return mcidev_attr->store(mem_ctl_info, buffer, count); @@ -726,32 +730,97 @@ void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) #define EDAC_DEVICE_SYMLINK "device" +#define grp_to_mci(k) (container_of(k, struct mcidev_sysfs_group, kobj)->mci) + +/* MCI show/store functions for top most object */ +static ssize_t inst_grp_show(struct kobject *kobj, struct attribute *attr, + char *buffer) +{ + struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj); + struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); + + debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); + + if (mcidev_attr->show) + return mcidev_attr->show(mem_ctl_info, buffer); + + return -EIO; +} + +static ssize_t inst_grp_store(struct kobject *kobj, struct attribute *attr, + const char *buffer, size_t count) +{ + struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj); + struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); + + debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); + + if (mcidev_attr->store) + return mcidev_attr->store(mem_ctl_info, buffer, count); + + return -EIO; +} + +/* No memory to release for this kobj */ +static void edac_inst_grp_release(struct kobject *kobj) +{ + struct mcidev_sysfs_group *grp; + struct mem_ctl_info *mci; + + debugf1("%s()\n", __func__); + + grp = container_of(kobj, struct mcidev_sysfs_group, kobj); + mci = grp->mci; + + kobject_put(&mci->edac_mci_kobj); +} + +/* Intermediate show/store table */ +static struct sysfs_ops inst_grp_ops = { + .show = inst_grp_show, + .store = inst_grp_store +}; + +/* the kobj_type instance for a instance group */ +static struct kobj_type ktype_inst_grp = { + .release = edac_inst_grp_release, + .sysfs_ops = &inst_grp_ops, + .default_attrs = (struct attribute **)default_csrow_attr, +}; + + /* * edac_create_mci_instance_attributes * create MC driver specific attributes bellow an specified kobj * This routine calls itself recursively, in order to create an entire * object tree. */ -static int edac_create_mci_instance_attributes( +static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, struct mcidev_sysfs_attribute *sysfs_attrib, struct kobject *kobj) { int err; + debugf1("%s()\n", __func__); + while (sysfs_attrib) { if (sysfs_attrib->grp) { struct kobject *newkobj = &sysfs_attrib->grp->kobj; - debugf0("%s() grp %s\n", __func__, - sysfs_attrib->grp->name); + debugf0("%s() grp %s, mci %p\n", __func__, + sysfs_attrib->grp->name, mci); - err = kobject_init_and_add(newkobj, NULL, + sysfs_attrib->grp->mci = mci; + + err = kobject_init_and_add(newkobj, &ktype_inst_grp, kobj, sysfs_attrib->grp->name); if (err) return err; - err = edac_create_mci_instance_attributes( - sysfs_attrib->grp->mcidev_attr, newkobj); + err = edac_create_mci_instance_attributes(mci, + sysfs_attrib->grp->mcidev_attr, + newkobj); + if (err) return err; } else if (sysfs_attrib->attr.name) { @@ -780,6 +849,8 @@ static void edac_remove_mci_instance_attributes( struct mcidev_sysfs_attribute *sysfs_attrib, struct kobject *kobj) { + debugf1("%s()\n", __func__); + /* loop if there are attributes and until we hit a NULL entry */ while (sysfs_attrib) { if (sysfs_attrib->grp) { @@ -832,7 +903,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) * then create them now for the driver. */ if (mci->mc_driver_sysfs_attributes) { - err = edac_create_mci_instance_attributes( + err = edac_create_mci_instance_attributes(mci, mci->mc_driver_sysfs_attributes, &mci->edac_mci_kobj); if (err) { diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index afa5281e8df9..e013004745de 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -784,10 +784,13 @@ static ssize_t i7core_inject_store_##param( \ struct mem_ctl_info *mci, \ const char *data, size_t count) \ { \ - struct i7core_pvt *pvt = mci->pvt_info; \ + struct i7core_pvt *pvt; \ long value; \ int rc; \ \ + debugf1("%s()\n", __func__); \ + pvt = mci->pvt_info; \ + \ if (pvt->inject.enable) \ disable_inject(mci); \ \ @@ -808,7 +811,10 @@ static ssize_t i7core_inject_show_##param( \ struct mem_ctl_info *mci, \ char *data) \ { \ - struct i7core_pvt *pvt = mci->pvt_info; \ + struct i7core_pvt *pvt; \ + \ + pvt = mci->pvt_info; \ + debugf1("%s() pvt=%p\n", __func__, pvt); \ if (pvt->inject.param < 0) \ return sprintf(data, "any\n"); \ else \ From c419d921e68c54232ce6d369a3b528cd7644b2ae Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 24 Sep 2009 16:36:32 -0300 Subject: [PATCH 61/83] edac: Don't create csrow entries on instance groups Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_mc_sysfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 6088ae6e8ea5..f689d7d6ab46 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -785,7 +785,6 @@ static struct sysfs_ops inst_grp_ops = { static struct kobj_type ktype_inst_grp = { .release = edac_inst_grp_release, .sysfs_ops = &inst_grp_ops, - .default_attrs = (struct attribute **)default_csrow_attr, }; @@ -806,13 +805,14 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, while (sysfs_attrib) { if (sysfs_attrib->grp) { struct kobject *newkobj = &sysfs_attrib->grp->kobj; + debugf0("%s() grp %s, mci %p\n", __func__, sysfs_attrib->grp->name, mci); sysfs_attrib->grp->mci = mci; err = kobject_init_and_add(newkobj, &ktype_inst_grp, - kobj, + &mci->edac_mci_kobj, sysfs_attrib->grp->name); if (err) return err; From f338d736910edf00e8426ee4322cfda585268d50 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 24 Sep 2009 17:25:43 -0300 Subject: [PATCH 62/83] i7core_edac: Convert UDIMM error counters into a proper sysfs group Instead of displaying 3 values at the same var, break it into 3 different sysfs nodes: /sys/devices/system/edac/mc/mc0/all_channel_counts/udimm0 /sys/devices/system/edac/mc/mc0/all_channel_counts/udimm1 /sys/devices/system/edac/mc/mc0/all_channel_counts/udimm2 For registered dimms, however, the error counters are already being displayed at: /sys/devices/system/edac/mc/mc0/csrow*/ce_count So, there's no need to add any extra sysfs nodes. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 85 +++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index e013004745de..97f6d1759c99 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1003,39 +1003,33 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, return sprintf(data, "%d\n", pvt->inject.enable); } -static ssize_t i7core_ce_regs_show(struct mem_ctl_info *mci, char *data) -{ - unsigned i, count, total = 0; - struct i7core_pvt *pvt = mci->pvt_info; - - if (!pvt->ce_count_available) { - count = sprintf(data, "data unavailable\n"); - return 0; - } - if (!pvt->is_registered) { - count = sprintf(data, "all channels " - "UDIMM0: %lu UDIMM1: %lu UDIMM2: %lu\n", - pvt->udimm_ce_count[0], - pvt->udimm_ce_count[1], - pvt->udimm_ce_count[2]); - data += count; - total += count; - } else { - for (i = 0; i < NUM_CHANS; i++) { - count = sprintf(data, "channel %d RDIMM0: %lu " - "RDIMM1: %lu RDIMM2: %lu\n", - i, - pvt->rdimm_ce_count[i][0], - pvt->rdimm_ce_count[i][1], - pvt->rdimm_ce_count[i][2]); - data += count; - total += count; - } - } - - return total; +#define DECLARE_COUNTER(param) \ +static ssize_t i7core_show_counter_##param( \ + struct mem_ctl_info *mci, \ + char *data) \ +{ \ + struct i7core_pvt *pvt = mci->pvt_info; \ + \ + debugf1("%s() \n", __func__); \ + if (!pvt->ce_count_available || (pvt->is_registered)) \ + return sprintf(data, "data unavailable\n"); \ + return sprintf(data, "%lu\n", \ + pvt->udimm_ce_count[param]); \ } +#define ATTR_COUNTER(param) \ + { \ + .attr = { \ + .name = __stringify(udimm##param), \ + .mode = (S_IRUGO | S_IWUSR) \ + }, \ + .show = i7core_show_counter_##param \ + } + +DECLARE_COUNTER(0); +DECLARE_COUNTER(1); +DECLARE_COUNTER(2); + /* * Sysfs struct */ @@ -1051,12 +1045,22 @@ static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = { { .attr = { .name = NULL } } }; - static struct mcidev_sysfs_group i7core_inject_addrmatch = { .name = "inject_addrmatch", .mcidev_attr = i7core_addrmatch_attrs, }; +static struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = { + ATTR_COUNTER(0), + ATTR_COUNTER(1), + ATTR_COUNTER(2), +}; + +static struct mcidev_sysfs_group i7core_udimm_counters = { + .name = "all_channel_counts", + .mcidev_attr = i7core_udimm_counters_attrs, +}; + static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = { { .attr = { @@ -1088,14 +1092,8 @@ static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = { }, .show = i7core_inject_enable_show, .store = i7core_inject_enable_store, - }, { - .attr = { - .name = "corrected_error_counts", - .mode = (S_IRUGO | S_IWUSR) - }, - .show = i7core_ce_regs_show, - .store = NULL, }, + { .attr = { .name = NULL } }, /* Reserved for udimm counters */ { .attr = { .name = NULL } } }; @@ -1323,6 +1321,15 @@ static int mci_bind_devs(struct mem_ctl_info *mci, pvt->is_registered = 1; } + /* + * Add extra nodes to count errors on udimm + * For registered memory, this is not needed, since the counters + * are already displayed at the standard locations + */ + if (!pvt->is_registered) + i7core_sysfs_attrs[ARRAY_SIZE(i7core_sysfs_attrs)-2].grp = + &i7core_udimm_counters; + return 0; error: From 35be95446734cbb10b088a6b38269ac4a8ac3a86 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 24 Sep 2009 17:28:50 -0300 Subject: [PATCH 63/83] Documentation/edac.txt: Reflect the sysfs changes at the document Signed-off-by: Mauro Carvalho Chehab --- Documentation/edac.txt | 56 ++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/Documentation/edac.txt b/Documentation/edac.txt index bd3f8a3905af..0b875e8da969 100644 --- a/Documentation/edac.txt +++ b/Documentation/edac.txt @@ -766,7 +766,7 @@ exports one For injecting a memory error, there are some sysfs nodes, under /sys/devices/system/edac/mc/mc?/: - inject_addrmatch: + inject_addrmatch/*: Controls the error injection mask register. It is possible to specify several characteristics of the address to match an error code: dimm = the affected dimm. Numbers are relative to a channel; @@ -781,10 +781,12 @@ exports one For example, to generate an error at rank 1 of dimm 2, for any channel, any bank, any page, any column: - echo "dimm:2 rank:1" >/sys/devices/system/edac/mc/mc0/inject_addrmatch + echo 2 >/sys/devices/system/edac/mc/mc0/inject_addrmatch/dimm + echo 1 >/sys/devices/system/edac/mc/mc0/inject_addrmatch/rank To return to the default behaviour of matching any, you can do: - echo "dimm:any rank:any" >/sys/devices/system/edac/mc/mc0/inject_addrmatch + echo any >/sys/devices/system/edac/mc/mc0/inject_addrmatch/dimm + echo any >/sys/devices/system/edac/mc/mc0/inject_addrmatch/rank inject_eccmask: specifies what bits will have troubles, @@ -813,7 +815,7 @@ exports one For example, the following code will generate an error for any write access at socket 0, on any DIMM/address on channel 2: - echo "channel:2" > /sys/devices/system/edac/mc/mc0/inject_addrmatch + echo 2 >/sys/devices/system/edac/mc/mc0/inject_addrmatch/channel echo 2 >/sys/devices/system/edac/mc/mc0/inject_type echo 64 >/sys/devices/system/edac/mc/mc0/inject_eccmask echo 3 >/sys/devices/system/edac/mc/mc0/inject_section @@ -829,18 +831,23 @@ exports one 3) Nehalem specific Corrected Error memory counters - Nehalem have some registers to count memory errors, reporting it on a - way that it is different from what EDAC API allows. Due to that, a - separate sysfs note were created to handle such counters. + Nehalem have some registers to count memory errors. The driver uses those + registers to report Corrected Errors on devices with Registered Dimms. - They can be read by looking at the contents of "corrected_error_counts" - counter. Due to hardware limits, the output is different on machines - with unregistered memories and machines with registered ones. + However, those counters don't work with Unregistered Dimms. As the chipset + offers some counters that also work with UDIMMS (but with a worse level of + granularity than the default ones), the driver exposes those registers for + UDIMM memories. - With unregistered memories, it outputs: + They can be read by looking at the contents of all_channel_counts/ - $ cat /sys/devices/system/edac/mc/mc0/corrected_error_counts - all channels UDIMM0: 0 UDIMM1: 0 UDIMM2: 0 + $ for i in /sys/devices/system/edac/mc/mc0/all_channel_counts/*; do echo $i; cat $i; done + /sys/devices/system/edac/mc/mc0/all_channel_counts/udimm0 + 0 + /sys/devices/system/edac/mc/mc0/all_channel_counts/udimm1 + 0 + /sys/devices/system/edac/mc/mc0/all_channel_counts/udimm2 + 0 What happens here is that errors on different csrows, but at the same dimm number will increment the same counter. @@ -849,21 +856,16 @@ exports one csrow1: channel 0, dimm1 csrow2: channel 1, dimm0 csrow3: channel 2, dimm0 - The hardware will increment UDIMM0 for an error at either csrow0, csrow2 - or csrow3. - - With registered memories, it outputs: - - $cat /sys/devices/system/edac/mc/mc0/corrected_error_counts - channel 0 RDIMM0: 0 RDIMM1: 0 RDIMM2: 0 - channel 1 RDIMM0: 0 RDIMM1: 0 RDIMM2: 0 - channel 2 RDIMM0: 0 RDIMM1: 0 RDIMM2: 0 - - So, with registered memories, there's a direct map between a csrow and a - counter. + The hardware will increment udimm0 for an error at the first dimm at either + csrow0, csrow2 or csrow3; + The hardware will increment udimm1 for an error at the second dimm at either + csrow0, csrow2 or csrow3; + The hardware will increment udimm2 for an error at the third dimm at either + csrow0, csrow2 or csrow3; 4) Standard error counters The standard error counters are generated when an mcelog error is received - by the driver. Since it is counted by software, it is possible that some - errors could be lost. + by the driver. Since, with udimm, this is counted by software, it is + possible that some errors could be lost. With rdimm's, they displays the + contents of the registers From b968759ee7102f86fec5f3349f7a8ab4556884a3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 25 Sep 2009 13:42:25 -0300 Subject: [PATCH 64/83] edac: Create an unique instance for each kobj Current code only works when there's just one memory controller, since we need one kobj for each instance. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_core.h | 23 +++++++---- drivers/edac/edac_mc_sysfs.c | 75 +++++++++++++++++++++++------------- 2 files changed, 65 insertions(+), 33 deletions(-) diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index 02bbbc9696d9..efca9343d26a 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h @@ -342,23 +342,29 @@ struct csrow_info { }; struct mcidev_sysfs_group { - const char *name; - struct mcidev_sysfs_attribute *mcidev_attr; - struct kobject kobj; - - struct mem_ctl_info *mci; /* the parent */ + const char *name; /* group name */ + struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */ }; +struct mcidev_sysfs_group_kobj { + struct list_head list; /* list for all instances within a mc */ + + struct kobject kobj; /* kobj for the group */ + + struct mcidev_sysfs_group *grp; /* group description table */ + struct mem_ctl_info *mci; /* the parent */ +}; /* mcidev_sysfs_attribute structure * used for driver sysfs attributes and in mem_ctl_info * sysfs top level entries */ struct mcidev_sysfs_attribute { + /* It should use either attr or grp */ struct attribute attr; + struct mcidev_sysfs_group *grp; /* Points to a group of attributes */ - struct mcidev_sysfs_group *grp; - + /* Ops for show/store values at the attribute - not used on group */ ssize_t (*show)(struct mem_ctl_info *,char *); ssize_t (*store)(struct mem_ctl_info *, const char *,size_t); }; @@ -436,6 +442,9 @@ struct mem_ctl_info { /* edac sysfs device control */ struct kobject edac_mci_kobj; + /* list for all grp instances within a mc */ + struct list_head grp_kobj_list; + /* Additional top controller level attributes, but specified * by the low level driver. * diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index f689d7d6ab46..c200c2fd43ea 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -730,7 +730,7 @@ void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) #define EDAC_DEVICE_SYMLINK "device" -#define grp_to_mci(k) (container_of(k, struct mcidev_sysfs_group, kobj)->mci) +#define grp_to_mci(k) (container_of(k, struct mcidev_sysfs_group_kobj, kobj)->mci) /* MCI show/store functions for top most object */ static ssize_t inst_grp_show(struct kobject *kobj, struct attribute *attr, @@ -764,12 +764,12 @@ static ssize_t inst_grp_store(struct kobject *kobj, struct attribute *attr, /* No memory to release for this kobj */ static void edac_inst_grp_release(struct kobject *kobj) { - struct mcidev_sysfs_group *grp; + struct mcidev_sysfs_group_kobj *grp; struct mem_ctl_info *mci; debugf1("%s()\n", __func__); - grp = container_of(kobj, struct mcidev_sysfs_group, kobj); + grp = container_of(kobj, struct mcidev_sysfs_group_kobj, kobj); mci = grp->mci; kobject_put(&mci->edac_mci_kobj); @@ -804,22 +804,30 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, while (sysfs_attrib) { if (sysfs_attrib->grp) { - struct kobject *newkobj = &sysfs_attrib->grp->kobj; + struct mcidev_sysfs_group_kobj *grp_kobj; + + grp_kobj = kzalloc(sizeof(*grp_kobj), GFP_KERNEL); + if (!grp_kobj) + return -ENOMEM; + + list_add_tail(&grp_kobj->list, &mci->grp_kobj_list); + + grp_kobj->grp = sysfs_attrib->grp; + grp_kobj->mci = mci; debugf0("%s() grp %s, mci %p\n", __func__, sysfs_attrib->grp->name, mci); - sysfs_attrib->grp->mci = mci; - - err = kobject_init_and_add(newkobj, &ktype_inst_grp, + err = kobject_init_and_add(&grp_kobj->kobj, + &ktype_inst_grp, &mci->edac_mci_kobj, sysfs_attrib->grp->name); if (err) return err; err = edac_create_mci_instance_attributes(mci, - sysfs_attrib->grp->mcidev_attr, - newkobj); + grp_kobj->grp->mcidev_attr, + &grp_kobj->kobj); if (err) return err; @@ -845,25 +853,27 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, * remove MC driver specific attributes at the topmost level * directory of this mci instance. */ -static void edac_remove_mci_instance_attributes( +static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, struct mcidev_sysfs_attribute *sysfs_attrib, - struct kobject *kobj) + struct kobject *kobj, int count) { + struct mcidev_sysfs_group_kobj *grp_kobj, *tmp; + debugf1("%s()\n", __func__); - /* loop if there are attributes and until we hit a NULL entry */ + /* + * loop if there are attributes and until we hit a NULL entry + * Remove first all the atributes + */ while (sysfs_attrib) { if (sysfs_attrib->grp) { - struct kobject *newkobj = &sysfs_attrib->grp->kobj; - - debugf0("%s() grp %s\n", __func__, - sysfs_attrib->grp->name); - - edac_remove_mci_instance_attributes( - sysfs_attrib->grp->mcidev_attr, newkobj); - - kobject_put(newkobj); - } else if (sysfs_attrib->attr.name) { + list_for_each_entry(grp_kobj, &mci->grp_kobj_list, + list) + if (grp_kobj->grp == sysfs_attrib->grp) + edac_remove_mci_instance_attributes(mci, + grp_kobj->grp->mcidev_attr, + &grp_kobj->kobj, count + 1); + } else if (sysfs_attrib->attr.name) { debugf0("%s() file %s\n", __func__, sysfs_attrib->attr.name); sysfs_remove_file(kobj, &sysfs_attrib->attr); @@ -871,6 +881,16 @@ static void edac_remove_mci_instance_attributes( break; sysfs_attrib++; } + + /* + * Now that all attributes got removed, it is save to remove all groups + */ + if (!count) + list_for_each_entry_safe(grp_kobj, tmp, &mci->grp_kobj_list, + list) { + debugf0("%s() grp %s\n", __func__, grp_kobj->grp->name); + kobject_put(&grp_kobj->kobj); + } } @@ -891,6 +911,8 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) debugf0("%s() idx=%d\n", __func__, mci->mc_idx); + INIT_LIST_HEAD(&mci->grp_kobj_list); + /* create a symlink for the device */ err = sysfs_create_link(kobj_mci, &mci->dev->kobj, EDAC_DEVICE_SYMLINK); @@ -940,8 +962,8 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) } /* remove the mci instance's attributes, if any */ - edac_remove_mci_instance_attributes( - mci->mc_driver_sysfs_attributes, &mci->edac_mci_kobj); + edac_remove_mci_instance_attributes(mci, + mci->mc_driver_sysfs_attributes, &mci->edac_mci_kobj, 0); /* remove the symlink */ sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK); @@ -975,8 +997,9 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) debugf0("%s() remove_mci_instance\n", __func__); /* remove this mci instance's attribtes */ - edac_remove_mci_instance_attributes(mci->mc_driver_sysfs_attributes, - &mci->edac_mci_kobj); + edac_remove_mci_instance_attributes(mci, + mci->mc_driver_sysfs_attributes, + &mci->edac_mci_kobj, 0); debugf0("%s() unregister this mci kobj\n", __func__); /* unregister this instance's kobject */ From ca9c90ba09ca3c9799319f46a56f397afbf617c2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 4 Oct 2009 10:15:40 -0300 Subject: [PATCH 65/83] i7core_edac: Use a lockless ringbuffer Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 83 +++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 97f6d1759c99..94aeca011ac4 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -239,9 +238,16 @@ struct i7core_pvt { /* mcelog glue */ struct edac_mce edac_mce; + + /* Fifo double buffers */ struct mce mce_entry[MCE_LOG_LEN]; - unsigned mce_count; - spinlock_t mce_lock; + struct mce mce_outentry[MCE_LOG_LEN]; + + /* Fifo in/out counters */ + unsigned mce_in, mce_out; + + /* Count indicator to show errors not got */ + unsigned mce_overrun; }; /* Static vars */ @@ -1617,30 +1623,50 @@ static void i7core_check_error(struct mem_ctl_info *mci) struct i7core_pvt *pvt = mci->pvt_info; int i; unsigned count = 0; - struct mce *m = NULL; - unsigned long flags; + struct mce *m; - /* Copy all mce errors into a temporary buffer */ - spin_lock_irqsave(&pvt->mce_lock, flags); - if (pvt->mce_count) { - m = kmalloc(sizeof(*m) * pvt->mce_count, GFP_ATOMIC); + /* + * MCE first step: Copy all mce errors into a temporary buffer + * We use a double buffering here, to reduce the risk of + * loosing an error. + */ + smp_rmb(); + count = (pvt->mce_out + sizeof(mce_entry) - pvt->mce_in) + % sizeof(mce_entry); + if (!count) + return; - if (m) { - count = pvt->mce_count; - memcpy(m, &pvt->mce_entry, sizeof(*m) * count); - } - pvt->mce_count = 0; + m = pvt->mce_outentry; + if (pvt->mce_in + count > sizeof(mce_entry)) { + unsigned l = sizeof(mce_entry) - pvt->mce_in; + + memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l); + smp_wmb(); + pvt->mce_in = 0; + count -= l; + m += l; + } + memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * count); + smp_wmb(); + pvt->mce_in += count; + + smp_rmb(); + if (pvt->mce_overrun) { + i7core_printk(KERN_ERR, "Lost %d memory errors\n", + pvt->mce_overrun); + smp_wmb(); + pvt->mce_overrun = 0; } - spin_unlock_irqrestore(&pvt->mce_lock, flags); - - /* proccess mcelog errors */ + /* + * MCE second step: parse errors and display + */ for (i = 0; i < count; i++) - i7core_mce_output_error(mci, &m[i]); + i7core_mce_output_error(mci, &pvt->mce_outentry[i]); - kfree(m); - - /* check memory count errors */ + /* + * Now, let's increment CE error counts + */ if (!pvt->is_registered) i7core_udimm_check_mc_ecc_err(mci); else @@ -1657,7 +1683,6 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) { struct mem_ctl_info *mci = priv; struct i7core_pvt *pvt = mci->pvt_info; - unsigned long flags; /* * Just let mcelog handle it if the error is @@ -1679,12 +1704,15 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) return 0; } - spin_lock_irqsave(&pvt->mce_lock, flags); - if (pvt->mce_count < MCE_LOG_LEN) { - memcpy(&pvt->mce_entry[pvt->mce_count], mce, sizeof(*mce)); - pvt->mce_count++; + smp_rmb(); + if ((pvt->mce_out + 1) % sizeof(mce_entry) == pvt->mce_in) { + smp_wmb(); + pvt->mce_overrun++; + return 0; } - spin_unlock_irqrestore(&pvt->mce_lock, flags); + smp_wmb(); + pvt->mce_out = (pvt->mce_out + 1) % sizeof(mce_entry); + memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce)); /* Handle fatal errors immediately */ if (mce->mcgstatus & 1) @@ -1777,7 +1805,6 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev, /* Registers on edac_mce in order to receive memory errors */ pvt->edac_mce.priv = mci; pvt->edac_mce.check_error = i7core_mce_check_error; - spin_lock_init(&pvt->mce_lock); rc = edac_mce_register(&pvt->edac_mce); if (unlikely(rc < 0)) { From 4f87fad1d32fcdda448f9eb430c9c234a1939ece Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 4 Oct 2009 11:54:56 -0300 Subject: [PATCH 66/83] i7core_edac: Better parse "any" addrmask Instead of accepting just "any", accept also "any\n" Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 94aeca011ac4..477b62a74dbf 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -800,7 +800,7 @@ static ssize_t i7core_inject_store_##param( \ if (pvt->inject.enable) \ disable_inject(mci); \ \ - if (!strcasecmp(data, "any")) \ + if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\ value = -1; \ else { \ rc = strict_strtoul(data, 10, &value); \ From 6e103be1c7c4adb50f25aaf1f1e8f828833c1719 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 5 Oct 2009 09:40:09 -0300 Subject: [PATCH 67/83] i7core_edac: First store, then increment Fix ringbuffer store logic. While here, add a few comments to the code and remove the undesired printk that could otherwise be called during NMI time. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 477b62a74dbf..59ec44175560 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1678,6 +1678,8 @@ static void i7core_check_error(struct mem_ctl_info *mci) * This routine simply queues mcelog errors, and * return. The error itself should be handled later * by i7core_check_error. + * WARNING: As this routine should be called at NMI time, extra care should + * be taken to avoid deadlocks, and to be as fast as possible. */ static int i7core_mce_check_error(void *priv, struct mce *mce) { @@ -1696,13 +1698,8 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) return 0; /* Only handle if it is the right mc controller */ - if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket) { - debugf0("mc%d: ignoring mce log for socket %d. " - "Another mc should get it.\n", - pvt->i7core_dev->socket, - cpu_data(mce->cpu).phys_proc_id); + if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket) return 0; - } smp_rmb(); if ((pvt->mce_out + 1) % sizeof(mce_entry) == pvt->mce_in) { @@ -1710,9 +1707,11 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) pvt->mce_overrun++; return 0; } + + /* Copy memory error at the ringbuffer */ + memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce)); smp_wmb(); pvt->mce_out = (pvt->mce_out + 1) % sizeof(mce_entry); - memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce)); /* Handle fatal errors immediately */ if (mce->mcgstatus & 1) From 321ece4dda32f52d4a28d6eb11f2ca2a5c93c191 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 8 Oct 2009 13:11:08 -0300 Subject: [PATCH 68/83] i7core_edac: Fix ringbuffer maxsize Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 59ec44175560..c2857f60ae6a 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1631,14 +1631,14 @@ static void i7core_check_error(struct mem_ctl_info *mci) * loosing an error. */ smp_rmb(); - count = (pvt->mce_out + sizeof(mce_entry) - pvt->mce_in) - % sizeof(mce_entry); + count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in) + % MCE_LOG_LEN; if (!count) return; m = pvt->mce_outentry; - if (pvt->mce_in + count > sizeof(mce_entry)) { - unsigned l = sizeof(mce_entry) - pvt->mce_in; + if (pvt->mce_in + count > MCE_LOG_LEN) { + unsigned l = MCE_LOG_LEN - pvt->mce_in; memcpy(m, &pvt->mce_entry[pvt->mce_in], sizeof(*m) * l); smp_wmb(); @@ -1702,7 +1702,7 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) return 0; smp_rmb(); - if ((pvt->mce_out + 1) % sizeof(mce_entry) == pvt->mce_in) { + if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) { smp_wmb(); pvt->mce_overrun++; return 0; @@ -1711,7 +1711,7 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) /* Copy memory error at the ringbuffer */ memcpy(&pvt->mce_entry[pvt->mce_out], mce, sizeof(*mce)); smp_wmb(); - pvt->mce_out = (pvt->mce_out + 1) % sizeof(mce_entry); + pvt->mce_out = (pvt->mce_out + 1) % MCE_LOG_LEN; /* Handle fatal errors immediately */ if (mce->mcgstatus & 1) From fd3826549db7f73d22b9c9abb80e01effb95c2ba Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 14 Oct 2009 06:07:07 -0300 Subject: [PATCH 69/83] i7core_edac: PCI device is called NONCORE, instead of NOCORE Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 6 +++--- include/linux/pci_ids.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index c2857f60ae6a..bb538dfbdc6c 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -291,7 +291,7 @@ struct pci_id_descr pci_dev_descr[] = { * the probing code needs to test for the other address in case of * failure of this one */ - { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NOCORE) }, + { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) }, }; #define N_DEVS ARRAY_SIZE(pci_dev_descr) @@ -1177,9 +1177,9 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno) * is at addr 8086:2c40, instead of 8086:2c41. So, we need * to probe for the alternate address in case of failure */ - if (pci_dev_descr[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NOCORE && !pdev) + if (pci_dev_descr[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev) pdev = pci_get_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT, *prev); + PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev); if (!pdev) { if (*prev) { diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index bf6db4814c27..382476a8a339 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2548,8 +2548,8 @@ #define PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR 0x2c31 #define PCI_DEVICE_ID_INTEL_I7_MC_CH2_RANK 0x2c32 #define PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC 0x2c33 -#define PCI_DEVICE_ID_INTEL_I7_NOCORE 0x2c41 -#define PCI_DEVICE_ID_INTEL_I7_NOCORE_ALT 0x2c40 +#define PCI_DEVICE_ID_INTEL_I7_NONCORE 0x2c41 +#define PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT 0x2c40 #define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 #define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 #define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a From de06eeef5809a69ff4daaae2bd63977e5404553d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 14 Oct 2009 08:02:40 -0300 Subject: [PATCH 70/83] i7core_edac: Use a more generic approach for probing PCI devices Currently, only one PCI set of tables is allowed. This prevents using the driver for other devices like Lynnfield, with have a different set of PCI ID's. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 79 +++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index bb538dfbdc6c..b6fce2e38e3d 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -202,12 +202,14 @@ struct pci_id_descr { int dev; int func; int dev_id; + int optional; }; struct i7core_dev { struct list_head list; u8 socket; struct pci_dev **pdev; + int n_devs; struct mem_ctl_info *mci; }; @@ -259,11 +261,12 @@ static DEFINE_MUTEX(i7core_edac_lock); .func = (function), \ .dev_id = (device_id) -struct pci_id_descr pci_dev_descr[] = { +struct pci_id_descr pci_dev_descr_i7core[] = { /* Memory controller */ { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) }, { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) }, - { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS) }, /* if RDIMM */ + /* Exists only for RDIMM */ + { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_I7_MC_RAS), .optional = 1 }, { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_I7_MC_TEST) }, /* Channel 0 */ @@ -294,7 +297,6 @@ struct pci_id_descr pci_dev_descr[] = { { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_I7_NONCORE) }, }; -#define N_DEVS ARRAY_SIZE(pci_dev_descr) /* * pci_device_id table for which devices we are looking for @@ -380,7 +382,7 @@ static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot, if (!i7core_dev) return NULL; - for (i = 0; i < N_DEVS; i++) { + for (i = 0; i < i7core_dev->n_devs; i++) { if (!i7core_dev->pdev[i]) continue; @@ -1116,7 +1118,7 @@ static void i7core_put_devices(struct i7core_dev *i7core_dev) int i; debugf0(__FILE__ ": %s()\n", __func__); - for (i = 0; i < N_DEVS; i++) { + for (i = 0; i < i7core_dev->n_devs; i++) { struct pci_dev *pdev = i7core_dev->pdev[i]; if (!pdev) continue; @@ -1138,7 +1140,7 @@ static void i7core_put_all_devices(void) i7core_put_devices(i7core_dev); } -static void i7core_xeon_pci_fixup(void) +static void i7core_xeon_pci_fixup(int dev_id) { struct pci_dev *pdev = NULL; int i; @@ -1147,8 +1149,7 @@ static void i7core_xeon_pci_fixup(void) * aren't announced by acpi. So, we need to use a legacy scan probing * to detect them */ - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, - pci_dev_descr[0].dev_id, NULL); + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, dev_id, NULL); if (unlikely(!pdev)) { for (i = 0; i < MAX_SOCKET_BUSES; i++) pcibios_scan_specific_bus(255-i); @@ -1161,7 +1162,8 @@ static void i7core_xeon_pci_fixup(void) * * Need to 'get' device 16 func 1 and func 2 */ -int i7core_get_onedevice(struct pci_dev **prev, int devno) +int i7core_get_onedevice(struct pci_dev **prev, int devno, + struct pci_id_descr *dev_descr, unsigned n_devs) { struct i7core_dev *i7core_dev; @@ -1170,14 +1172,14 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno) u8 socket = 0; pdev = pci_get_device(PCI_VENDOR_ID_INTEL, - pci_dev_descr[devno].dev_id, *prev); + dev_descr->dev_id, *prev); /* * On Xeon 55xx, the Intel Quckpath Arch Generic Non-core regs * is at addr 8086:2c40, instead of 8086:2c41. So, we need * to probe for the alternate address in case of failure */ - if (pci_dev_descr[devno].dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev) + if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_I7_NONCORE && !pdev) pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev); @@ -1187,19 +1189,13 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno) return 0; } - /* - * Dev 3 function 2 only exists on chips with RDIMMs - * so, it is ok to not found it - */ - if ((pci_dev_descr[devno].dev == 3) && (pci_dev_descr[devno].func == 2)) { - *prev = pdev; + if (dev_descr->optional) return 0; - } i7core_printk(KERN_ERR, "Device not found: dev %02x.%d PCI ID %04x:%04x\n", - pci_dev_descr[devno].dev, pci_dev_descr[devno].func, - PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id); + dev_descr->dev, dev_descr->func, + PCI_VENDOR_ID_INTEL, dev_descr->dev_id); /* End of list, leave */ return -ENODEV; @@ -1216,11 +1212,12 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno) i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL); if (!i7core_dev) return -ENOMEM; - i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * N_DEVS, + i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * n_devs, GFP_KERNEL); if (!i7core_dev->pdev) return -ENOMEM; i7core_dev->socket = socket; + i7core_dev->n_devs = n_devs; list_add_tail(&i7core_dev->list, &i7core_edac_list); } @@ -1228,8 +1225,8 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno) i7core_printk(KERN_ERR, "Duplicated device for " "dev %02x:%02x.%d PCI ID %04x:%04x\n", - bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func, - PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id); + bus, dev_descr->dev, dev_descr->func, + PCI_VENDOR_ID_INTEL, dev_descr->dev_id); pci_dev_put(pdev); return -ENODEV; } @@ -1237,14 +1234,14 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno) i7core_dev->pdev[devno] = pdev; /* Sanity check */ - if (unlikely(PCI_SLOT(pdev->devfn) != pci_dev_descr[devno].dev || - PCI_FUNC(pdev->devfn) != pci_dev_descr[devno].func)) { + if (unlikely(PCI_SLOT(pdev->devfn) != dev_descr->dev || + PCI_FUNC(pdev->devfn) != dev_descr->func)) { i7core_printk(KERN_ERR, "Device PCI ID %04x:%04x " "has dev %02x:%02x.%d instead of dev %02x:%02x.%d\n", - PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id, + PCI_VENDOR_ID_INTEL, dev_descr->dev_id, bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), - bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func); + bus, dev_descr->dev, dev_descr->func); return -ENODEV; } @@ -1253,30 +1250,32 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno) i7core_printk(KERN_ERR, "Couldn't enable " "dev %02x:%02x.%d PCI ID %04x:%04x\n", - bus, pci_dev_descr[devno].dev, pci_dev_descr[devno].func, - PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id); + bus, dev_descr->dev, dev_descr->func, + PCI_VENDOR_ID_INTEL, dev_descr->dev_id); return -ENODEV; } debugf0("Detected socket %d dev %02x:%02x.%d PCI ID %04x:%04x\n", - socket, bus, pci_dev_descr[devno].dev, - pci_dev_descr[devno].func, - PCI_VENDOR_ID_INTEL, pci_dev_descr[devno].dev_id); + socket, bus, dev_descr->dev, + dev_descr->func, + PCI_VENDOR_ID_INTEL, dev_descr->dev_id); *prev = pdev; return 0; } -static int i7core_get_devices(void) +static int i7core_get_devices(struct pci_id_descr dev_descr[], unsigned n_devs) { - int i; + int i, rc; struct pci_dev *pdev = NULL; - for (i = 0; i < N_DEVS; i++) { + for (i = 0; i < n_devs; i++) { pdev = NULL; do { - if (i7core_get_onedevice(&pdev, i) < 0) { + rc = i7core_get_onedevice(&pdev, i, &dev_descr[i], + n_devs); + if (rc < 0) { i7core_put_all_devices(); return -ENODEV; } @@ -1298,7 +1297,7 @@ static int mci_bind_devs(struct mem_ctl_info *mci, i7core_dev->mci = mci; pvt->is_registered = 0; - for (i = 0; i < N_DEVS; i++) { + for (i = 0; i < i7core_dev->n_devs; i++) { pdev = i7core_dev->pdev[i]; if (!pdev) continue; @@ -1838,7 +1837,9 @@ static int __devinit i7core_probe(struct pci_dev *pdev, /* get the pci devices we want to reserve for our use */ mutex_lock(&i7core_edac_lock); - rc = i7core_get_devices(); + + rc = i7core_get_devices(pci_dev_descr_i7core, + ARRAY_SIZE(pci_dev_descr_i7core)); if (unlikely(rc < 0)) goto fail0; @@ -1937,7 +1938,7 @@ static int __init i7core_init(void) /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); - i7core_xeon_pci_fixup(); + i7core_xeon_pci_fixup(pci_dev_descr_i7core[0].dev_id); pci_rc = pci_register_driver(&i7core_driver); From 486dd09f129da01cd02b212ba48dce987488b860 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sun, 8 Nov 2009 01:34:27 -0200 Subject: [PATCH 71/83] edac: i7core_edac produces undefined behaviour on 32bit Fix the shifts up Signed-off-by: Alan Cox Acked-by: Doug Thompson Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index b6fce2e38e3d..bd7c727030a3 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -916,41 +916,41 @@ static ssize_t i7core_inject_enable_store(struct mem_ctl_info *mci, /* Sets pvt->inject.dimm mask */ if (pvt->inject.dimm < 0) - mask |= 1L << 41; + mask |= 1LL << 41; else { if (pvt->channel[pvt->inject.channel].dimms > 2) - mask |= (pvt->inject.dimm & 0x3L) << 35; + mask |= (pvt->inject.dimm & 0x3LL) << 35; else - mask |= (pvt->inject.dimm & 0x1L) << 36; + mask |= (pvt->inject.dimm & 0x1LL) << 36; } /* Sets pvt->inject.rank mask */ if (pvt->inject.rank < 0) - mask |= 1L << 40; + mask |= 1LL << 40; else { if (pvt->channel[pvt->inject.channel].dimms > 2) - mask |= (pvt->inject.rank & 0x1L) << 34; + mask |= (pvt->inject.rank & 0x1LL) << 34; else - mask |= (pvt->inject.rank & 0x3L) << 34; + mask |= (pvt->inject.rank & 0x3LL) << 34; } /* Sets pvt->inject.bank mask */ if (pvt->inject.bank < 0) - mask |= 1L << 39; + mask |= 1LL << 39; else - mask |= (pvt->inject.bank & 0x15L) << 30; + mask |= (pvt->inject.bank & 0x15LL) << 30; /* Sets pvt->inject.page mask */ if (pvt->inject.page < 0) - mask |= 1L << 38; + mask |= 1LL << 38; else - mask |= (pvt->inject.page & 0xffffL) << 14; + mask |= (pvt->inject.page & 0xffff) << 14; /* Sets pvt->inject.column mask */ if (pvt->inject.col < 0) - mask |= 1L << 37; + mask |= 1LL << 37; else - mask |= (pvt->inject.col & 0x3fffL); + mask |= (pvt->inject.col & 0x3fff); /* * bit 0: REPEAT_EN From 3b918c12df4f624140456d6c6f982bada8e1f095 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 8 Nov 2009 01:36:40 -0200 Subject: [PATCH 72/83] edac: fix i7core build Fix build warning (missing header file) and build error when CONFIG_SMP=n. drivers/edac/i7core_edac.c:860: error: implicit declaration of function 'msleep' drivers/edac/i7core_edac.c:1700: error: 'struct cpuinfo_x86' has no member named 'phys_proc_id' Signed-off-by: Randy Dunlap Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index bd7c727030a3..e944b63d9f06 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -1696,9 +1697,11 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) if (mce->bank != 8) return 0; +#ifdef CONFIG_SMP /* Only handle if it is the right mc controller */ if (cpu_data(mce->cpu).phys_proc_id != pvt->i7core_dev->socket) return 0; +#endif smp_rmb(); if ((pvt->mce_out + 1) % MCE_LOG_LEN == pvt->mce_in) { From 4f7b9e7cbe68c97dbe1266709ecfc8b807b0d0ee Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Fri, 4 Dec 2009 16:49:34 -0200 Subject: [PATCH 73/83] i7core_edac: do not export static functions Signed-off-by: Stephen Rothwell Signed-off-by: Mauro Carvalho Chehab --- arch/x86/pci/legacy.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/x86/pci/legacy.c b/arch/x86/pci/legacy.c index d6cc2eddf339..8d460eaf524f 100644 --- a/arch/x86/pci/legacy.c +++ b/arch/x86/pci/legacy.c @@ -35,7 +35,6 @@ int __init pci_legacy_init(void) return 0; } -EXPORT_SYMBOL_GPL(pci_legacy_init); void pcibios_scan_specific_bus(int busn) { From 52a2e4fc3712d12888decd386d78ad526078a1fa Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 14 Oct 2009 11:21:58 -0300 Subject: [PATCH 74/83] i7core_edac: Add initial support for Lynnfield Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 39 ++++++++++++++++++++++++++++++++++++-- include/linux/pci_ids.h | 15 +++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index e944b63d9f06..e525d571cb25 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -299,11 +299,30 @@ struct pci_id_descr pci_dev_descr_i7core[] = { }; +struct pci_id_descr pci_dev_descr_lynnfield[] = { + { PCI_DESCR( 3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR) }, + { PCI_DESCR( 3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD) }, + { PCI_DESCR( 3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST) }, + + { PCI_DESCR( 4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL) }, + { PCI_DESCR( 4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR) }, + { PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK) }, + { PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC) }, + + { PCI_DESCR( 4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL) }, + { PCI_DESCR( 4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) }, + { PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) }, + { PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) }, + + { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) }, +}; + /* * pci_device_id table for which devices we are looking for */ static const struct pci_device_id i7core_pci_tbl[] __devinitdata = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE)}, {0,} /* 0 terminated list. */ }; @@ -522,6 +541,9 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) for (i = 0; i < NUM_CHANS; i++) { u32 data, dimm_dod[3], value[8]; + if (!pvt->pci_ch[i][0]) + continue; + if (!CH_ACTIVE(pvt, i)) { debugf0("Channel %i is not active\n", i); continue; @@ -1001,6 +1023,9 @@ static ssize_t i7core_inject_enable_show(struct mem_ctl_info *mci, struct i7core_pvt *pvt = mci->pvt_info; u32 injectmask; + if (!pvt->pci_ch[pvt->inject.channel][0]) + return 0; + pci_read_config_dword(pvt->pci_ch[pvt->inject.channel][0], MC_CHANNEL_ERROR_INJECT, &injectmask); @@ -1841,8 +1866,18 @@ static int __devinit i7core_probe(struct pci_dev *pdev, /* get the pci devices we want to reserve for our use */ mutex_lock(&i7core_edac_lock); - rc = i7core_get_devices(pci_dev_descr_i7core, - ARRAY_SIZE(pci_dev_descr_i7core)); + if (pdev->device == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) { + printk(KERN_INFO "i7core_edac: detected a " + "Lynnfield processor\n"); + rc = i7core_get_devices(pci_dev_descr_lynnfield, + ARRAY_SIZE(pci_dev_descr_lynnfield)); + } else { + printk(KERN_INFO "i7core_edac: detected a " + "Nehalem/Nehalem-EP processor\n"); + rc = i7core_get_devices(pci_dev_descr_i7core, + ARRAY_SIZE(pci_dev_descr_i7core)); + } + if (unlikely(rc < 0)) goto fail0; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 382476a8a339..ebc0fa4c7a66 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2550,6 +2550,21 @@ #define PCI_DEVICE_ID_INTEL_I7_MC_CH2_TC 0x2c33 #define PCI_DEVICE_ID_INTEL_I7_NONCORE 0x2c41 #define PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT 0x2c40 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE 0x2c50 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_SAD 0x2c81 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0 0x2c90 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_PHY0 0x2c91 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR 0x2c98 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD 0x2c99 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST 0x2c9C +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL 0x2ca0 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR 0x2ca1 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK 0x2ca2 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC 0x2ca3 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL 0x2ca8 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR 0x2ca9 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK 0x2caa +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC 0x2cab #define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 #define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 #define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a From f05da2f7855b3b88a831ca79e037245872549ec0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 14 Oct 2009 13:31:06 -0300 Subject: [PATCH 75/83] i7core: add support for Lynnfield alternate address Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 13 +++++++++++-- include/linux/pci_ids.h | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index e525d571cb25..d3f5c016c5eb 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -314,6 +314,10 @@ struct pci_id_descr pci_dev_descr_lynnfield[] = { { PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) }, { PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) }, + /* + * This is the PCI device has an alternate address on some + * processors like Core i7 860 + */ { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) }, }; @@ -322,7 +326,7 @@ struct pci_id_descr pci_dev_descr_lynnfield[] = { */ static const struct pci_device_id i7core_pci_tbl[] __devinitdata = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0)}, {0,} /* 0 terminated list. */ }; @@ -1209,6 +1213,11 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno, pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev); + if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev) + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT, + *prev); + if (!pdev) { if (*prev) { *prev = pdev; @@ -1866,7 +1875,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, /* get the pci devices we want to reserve for our use */ mutex_lock(&i7core_edac_lock); - if (pdev->device == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) { + if (pdev->device == PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0) { printk(KERN_INFO "i7core_edac: detected a " "Lynnfield processor\n"); rc = i7core_get_devices(pci_dev_descr_lynnfield, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index ebc0fa4c7a66..e67cb20b8401 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2551,6 +2551,7 @@ #define PCI_DEVICE_ID_INTEL_I7_NONCORE 0x2c41 #define PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT 0x2c40 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE 0x2c50 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT 0x2c51 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_SAD 0x2c81 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0 0x2c90 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_PHY0 0x2c91 From 508fa179f8e0da5d7241e12ad1562b96f291e800 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 14 Oct 2009 13:44:37 -0300 Subject: [PATCH 76/83] i7core_edac: Fix wrong device id for channel 1 devices Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index d3f5c016c5eb..4d6ecf1291b8 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -309,10 +309,10 @@ struct pci_id_descr pci_dev_descr_lynnfield[] = { { PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK) }, { PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC) }, - { PCI_DESCR( 4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL) }, - { PCI_DESCR( 4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) }, - { PCI_DESCR( 4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) }, - { PCI_DESCR( 4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) }, + { PCI_DESCR( 5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL) }, + { PCI_DESCR( 5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR) }, + { PCI_DESCR( 5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK) }, + { PCI_DESCR( 5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC) }, /* * This is the PCI device has an alternate address on some From 71753e0141a220ecbf9c71a66e0a8acce9705fb5 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 9 Dec 2009 16:55:15 -0300 Subject: [PATCH 77/83] EDAC: add __init to i7core_xeon_pci_fixup It's called only from an __init function and is the only user of pcibios_scan_specific_bus which will be marked as __devinit in the next patch. Signed-off-by: Jiri Slaby Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 4d6ecf1291b8..37ade251e9e2 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1170,7 +1170,7 @@ static void i7core_put_all_devices(void) i7core_put_devices(i7core_dev); } -static void i7core_xeon_pci_fixup(int dev_id) +static void __init i7core_xeon_pci_fixup(int dev_id) { struct pci_dev *pdev = NULL; int i; From 2a6fae326713ec84f307c045f6b497d4afaeb1d4 Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Thu, 7 Jan 2010 23:27:30 -0300 Subject: [PATCH 78/83] i7core_edac: fix memory leak of i7core_dev Free already allocated i7core_dev. Signed-off-by: Alexander Beregalov Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 37ade251e9e2..8e93df637bca 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1249,8 +1249,10 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno, return -ENOMEM; i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * n_devs, GFP_KERNEL); - if (!i7core_dev->pdev) + if (!i7core_dev->pdev) { + kfree(i7core_dev); return -ENOMEM; + } i7core_dev->socket = socket; i7core_dev->n_devs = n_devs; list_add_tail(&i7core_dev->list, &i7core_edac_list); From 8a311e179e52d122ac203d8e88014284c18ca8ab Mon Sep 17 00:00:00 2001 From: Vernon Mauery Date: Fri, 16 Apr 2010 19:40:19 -0300 Subject: [PATCH 79/83] Always call i7core_[ur]dimm_check_mc_ecc_err This fixes an error in function i7core_check_error In commit ca9c90ba09ca3c9799319f46a56f397afbf617c2 which converts the driver to use double buffering, there is a change in the logic. Before, if mce_count was zero, it skipped over a couple of statements and finished out with a call to the *check_mc_ecc_err function. The current code checks to see if mce_count is 0 and then exits. This change reverts the behavior back to the original where if there are no errors to report, we skip to the end and call the *check_mc_ecc_err function. This fix allows the driver to work again on my Nehalem based blades again. Signed-off-by: Vernon Mauery Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 8e93df637bca..cd51709c4d89 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1670,7 +1670,7 @@ static void i7core_check_error(struct mem_ctl_info *mci) count = (pvt->mce_out + MCE_LOG_LEN - pvt->mce_in) % MCE_LOG_LEN; if (!count) - return; + goto check_ce_error; m = pvt->mce_outentry; if (pvt->mce_in + count > MCE_LOG_LEN) { @@ -1703,6 +1703,7 @@ static void i7core_check_error(struct mem_ctl_info *mci) /* * Now, let's increment CE error counts */ +check_ce_error: if (!pvt->is_registered) i7core_udimm_check_mc_ecc_err(mci); else From ac1ececea995fd77c8da6a1299674f22991cecaa Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 18 May 2010 13:00:31 -0300 Subject: [PATCH 80/83] i7core_edac: Add support for X5670 As reported by Vernon Mauery , X5670 (Westmere-EP) uses a different register for one of the uncore PCI devices. Add support for it. Those are the PCI ID's on this new chipset: fe:00.0 0600: 8086:2c70 (rev 02) fe:00.1 0600: 8086:2d81 (rev 02) fe:02.0 0600: 8086:2d90 (rev 02) fe:02.1 0600: 8086:2d91 (rev 02) fe:02.2 0600: 8086:2d92 (rev 02) fe:02.3 0600: 8086:2d93 (rev 02) fe:02.4 0600: 8086:2d94 (rev 02) fe:02.5 0600: 8086:2d95 (rev 02) fe:03.0 0600: 8086:2d98 (rev 02) fe:03.1 0600: 8086:2d99 (rev 02) fe:03.2 0600: 8086:2d9a (rev 02) fe:03.4 0600: 8086:2d9c (rev 02) fe:04.0 0600: 8086:2da0 (rev 02) fe:04.1 0600: 8086:2da1 (rev 02) fe:04.2 0600: 8086:2da2 (rev 02) fe:04.3 0600: 8086:2da3 (rev 02) fe:05.0 0600: 8086:2da8 (rev 02) fe:05.1 0600: 8086:2da9 (rev 02) fe:05.2 0600: 8086:2daa (rev 02) fe:05.3 0600: 8086:2dab (rev 02) fe:06.0 0600: 8086:2db0 (rev 02) fe:06.1 0600: 8086:2db1 (rev 02) fe:06.2 0600: 8086:2db2 (rev 02) fe:06.3 0600: 8086:2db3 (rev 02) (as usual, the same PCI devices repeat at ff: bus) The PCI device 8086:2c70 is shown as: fe:00.0 Host bridge: Intel Corporation QuickPath Architecture Generic Non-core Registers (rev 02) So, for this device to be recognized, it is only a matter of adding this new PCI ID to the driver. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 7 ++++++- include/linux/pci_ids.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index cd51709c4d89..82acfbd01779 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1213,10 +1213,15 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno, pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev); - if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev) + if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev) { pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT, *prev); + if (!pdev) + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2, + *prev); + } if (!pdev) { if (*prev) { diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index e67cb20b8401..46d76e985bac 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2552,6 +2552,7 @@ #define PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT 0x2c40 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE 0x2c50 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT 0x2c51 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2 0x2c70 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_SAD 0x2c81 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0 0x2c90 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_PHY0 0x2c91 From d4d1ef4515cca074d5bbe1c63420822d6b20fe63 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Tue, 18 May 2010 10:53:25 -0300 Subject: [PATCH 81/83] i7core_edac: don't free on success Signed-off-by: Tony Luck Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 82acfbd01779..3e2b5379bc05 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1856,7 +1856,8 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev, } fail: - edac_mc_free(mci); + if (rc < 0) + edac_mc_free(mci); return rc; } From bd9e19ca46b54fa85141c4d20afd668379d94c81 Mon Sep 17 00:00:00 2001 From: Vernon Mauery Date: Tue, 18 May 2010 19:02:50 -0300 Subject: [PATCH 82/83] Add support for Westmere to i7core_edac driver This adds new PCI IDs for the Westmere's memory controller devices and modifies the i7core_edac driver to be able to probe both Nehalem and Westmere processors. Signed-off-by: Vernon Mauery Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 115 +++++++++++++++++++++++++------------ include/linux/pci_ids.h | 16 ++++++ 2 files changed, 95 insertions(+), 36 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 3e2b5379bc05..8d63b0046480 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -206,6 +206,11 @@ struct pci_id_descr { int optional; }; +struct pci_id_table { + struct pci_id_descr *descr; + int n_devs; +}; + struct i7core_dev { struct list_head list; u8 socket; @@ -262,7 +267,7 @@ static DEFINE_MUTEX(i7core_edac_lock); .func = (function), \ .dev_id = (device_id) -struct pci_id_descr pci_dev_descr_i7core[] = { +struct pci_id_descr pci_dev_descr_i7core_nehalem[] = { /* Memory controller */ { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_I7_MCR) }, { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_I7_MC_TAD) }, @@ -321,6 +326,44 @@ struct pci_id_descr pci_dev_descr_lynnfield[] = { { PCI_DESCR( 0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE) }, }; +struct pci_id_descr pci_dev_descr_i7core_westmere[] = { + /* Memory controller */ + { PCI_DESCR(3, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2) }, + { PCI_DESCR(3, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2) }, + /* Exists only for RDIMM */ + { PCI_DESCR(3, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_RAS_REV2), .optional = 1 }, + { PCI_DESCR(3, 4, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST_REV2) }, + + /* Channel 0 */ + { PCI_DESCR(4, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL_REV2) }, + { PCI_DESCR(4, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR_REV2) }, + { PCI_DESCR(4, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK_REV2) }, + { PCI_DESCR(4, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC_REV2) }, + + /* Channel 1 */ + { PCI_DESCR(5, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL_REV2) }, + { PCI_DESCR(5, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR_REV2) }, + { PCI_DESCR(5, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK_REV2) }, + { PCI_DESCR(5, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC_REV2) }, + + /* Channel 2 */ + { PCI_DESCR(6, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_CTRL_REV2) }, + { PCI_DESCR(6, 1, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2) }, + { PCI_DESCR(6, 2, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2) }, + { PCI_DESCR(6, 3, PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2) }, + + /* Generic Non-core registers */ + { PCI_DESCR(0, 0, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2) }, + +}; + +#define PCI_ID_TABLE_ENTRY(A) { A, ARRAY_SIZE(A) } +struct pci_id_table pci_dev_table[] = { + PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_nehalem), + PCI_ID_TABLE_ENTRY(pci_dev_descr_lynnfield), + PCI_ID_TABLE_ENTRY(pci_dev_descr_i7core_westmere), +}; + /* * pci_device_id table for which devices we are looking for */ @@ -1170,7 +1213,7 @@ static void i7core_put_all_devices(void) i7core_put_devices(i7core_dev); } -static void __init i7core_xeon_pci_fixup(int dev_id) +static void __init i7core_xeon_pci_fixup(struct pci_id_table *table) { struct pci_dev *pdev = NULL; int i; @@ -1179,10 +1222,13 @@ static void __init i7core_xeon_pci_fixup(int dev_id) * aren't announced by acpi. So, we need to use a legacy scan probing * to detect them */ - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, dev_id, NULL); - if (unlikely(!pdev)) { - for (i = 0; i < MAX_SOCKET_BUSES; i++) - pcibios_scan_specific_bus(255-i); + while (table && table->descr) { + pdev = pci_get_device(PCI_VENDOR_ID_INTEL, table->descr[0].dev_id, NULL); + if (unlikely(!pdev)) { + for (i = 0; i < MAX_SOCKET_BUSES; i++) + pcibios_scan_specific_bus(255-i); + } + table++; } } @@ -1213,15 +1259,10 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno, pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_NONCORE_ALT, *prev); - if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev) { + if (dev_descr->dev_id == PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE && !pdev) pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_ALT, *prev); - if (!pdev) - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_LYNNFIELD_NONCORE_REV2, - *prev); - } if (!pdev) { if (*prev) { @@ -1232,6 +1273,9 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno, if (dev_descr->optional) return 0; + if (devno == 0) + return -ENODEV; + i7core_printk(KERN_ERR, "Device not found: dev %02x.%d PCI ID %04x:%04x\n", dev_descr->dev, dev_descr->func, @@ -1307,24 +1351,34 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno, return 0; } -static int i7core_get_devices(struct pci_id_descr dev_descr[], unsigned n_devs) +static int i7core_get_devices(struct pci_id_table *table) { int i, rc; struct pci_dev *pdev = NULL; + struct pci_id_descr *dev_descr; - for (i = 0; i < n_devs; i++) { - pdev = NULL; - do { - rc = i7core_get_onedevice(&pdev, i, &dev_descr[i], - n_devs); - if (rc < 0) { - i7core_put_all_devices(); - return -ENODEV; - } - } while (pdev); + while (table && table->descr) { + dev_descr = table->descr; + for (i = 0; i < table->n_devs; i++) { + pdev = NULL; + do { + rc = i7core_get_onedevice(&pdev, i, &dev_descr[i], + table->n_devs); + if (rc < 0) { + if (i == 0) { + i = table->n_devs; + break; + } + i7core_put_all_devices(); + return -ENODEV; + } + } while (pdev); + } + table++; } return 0; + return 0; } static int mci_bind_devs(struct mem_ctl_info *mci, @@ -1884,18 +1938,7 @@ static int __devinit i7core_probe(struct pci_dev *pdev, /* get the pci devices we want to reserve for our use */ mutex_lock(&i7core_edac_lock); - if (pdev->device == PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0) { - printk(KERN_INFO "i7core_edac: detected a " - "Lynnfield processor\n"); - rc = i7core_get_devices(pci_dev_descr_lynnfield, - ARRAY_SIZE(pci_dev_descr_lynnfield)); - } else { - printk(KERN_INFO "i7core_edac: detected a " - "Nehalem/Nehalem-EP processor\n"); - rc = i7core_get_devices(pci_dev_descr_i7core, - ARRAY_SIZE(pci_dev_descr_i7core)); - } - + rc = i7core_get_devices(pci_dev_table); if (unlikely(rc < 0)) goto fail0; @@ -1994,7 +2037,7 @@ static int __init i7core_init(void) /* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init(); - i7core_xeon_pci_fixup(pci_dev_descr_i7core[0].dev_id); + i7core_xeon_pci_fixup(pci_dev_table); pci_rc = pci_register_driver(&i7core_driver); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 46d76e985bac..413fab765a5f 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2567,6 +2567,22 @@ #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR 0x2ca9 #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK 0x2caa #define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC 0x2cab +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR_REV2 0x2d98 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TAD_REV2 0x2d99 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_RAS_REV2 0x2d9a +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST_REV2 0x2d9c +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL_REV2 0x2da0 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR_REV2 0x2da1 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_RANK_REV2 0x2da2 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_TC_REV2 0x2da3 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL_REV2 0x2da8 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR_REV2 0x2da9 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_RANK_REV2 0x2daa +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_TC_REV2 0x2dab +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_CTRL_REV2 0x2db0 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_ADDR_REV2 0x2db1 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_RANK_REV2 0x2db2 +#define PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH2_TC_REV2 0x2db3 #define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 #define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 #define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a From 52707f918cca231f8461d45e78a60014795f20d9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 18 May 2010 20:43:52 -0300 Subject: [PATCH 83/83] i7core_edac: Better describe the supported devices Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 8d63b0046480..6b8b7b41ec5f 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1,9 +1,14 @@ -/* Intel 7 core Memory Controller kernel module (Nehalem) +/* Intel i7 core/Nehalem Memory Controller kernel module + * + * This driver supports yhe memory controllers found on the Intel + * processor families i7core, i7core 7xx/8xx, i5core, Xeon 35xx, + * Xeon 55xx and Xeon 56xx also known as Nehalem, Nehalem-EP, Lynnfield + * and Westmere-EP. * * This file may be distributed under the terms of the * GNU General Public License version 2 only. * - * Copyright (c) 2009 by: + * Copyright (c) 2009-2010 by: * Mauro Carvalho Chehab * * Red Hat Inc. http://www.redhat.com