-/* 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 <mchehab@redhat.com>
*
* Red Hat Inc. http://www.redhat.com
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/slab.h>
+#include <linux/delay.h>
#include <linux/edac.h>
#include <linux/mmzone.h>
#include <linux/edac_mce.h>
-#include <linux/spinlock.h>
#include <linux/smp.h>
#include <asm/processor.h>
int dev;
int func;
int dev_id;
+ int optional;
+};
+
+struct pci_id_table {
+ struct pci_id_descr *descr;
+ int n_devs;
};
struct i7core_dev {
struct list_head list;
u8 socket;
struct pci_dev **pdev;
+ int n_devs;
struct mem_ctl_info *mci;
};
/* 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 */
.func = (function), \
.dev_id = (device_id)
-struct pci_id_descr pci_dev_descr[] = {
+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) },
- { 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 */
* 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)
+
+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( 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
+ * processors like Core i7 860
+ */
+ { 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
*/
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_QPI_LINK0)},
{0,} /* 0 terminated list. */
};
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;
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;
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); \
/* 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
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);
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;
+#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]); \
+}
- 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;
- }
+#define ATTR_COUNTER(param) \
+ { \
+ .attr = { \
+ .name = __stringify(udimm##param), \
+ .mode = (S_IRUGO | S_IWUSR) \
+ }, \
+ .show = i7core_show_counter_##param \
}
- return total;
-}
+DECLARE_COUNTER(0);
+DECLARE_COUNTER(1);
+DECLARE_COUNTER(2);
/*
* Sysfs struct
{ .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 = {
},
.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 } }
};
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;
i7core_put_devices(i7core_dev);
}
-static void i7core_xeon_pci_fixup(void)
+static void __init i7core_xeon_pci_fixup(struct pci_id_table *table)
{
struct pci_dev *pdev = NULL;
int i;
* 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);
- 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++;
}
}
*
* 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;
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_NOCORE && !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_NOCORE_ALT, *prev);
+ 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) {
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;
- }
+
+ if (devno == 0)
+ return -ENODEV;
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;
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)
+ 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);
}
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;
}
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;
}
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_table *table)
{
- int i;
+ int i, rc;
struct pci_dev *pdev = NULL;
-
- for (i = 0; i < N_DEVS; i++) {
- pdev = NULL;
- do {
- if (i7core_get_onedevice(&pdev, i) < 0) {
- i7core_put_all_devices();
- return -ENODEV;
- }
- } while (pdev);
+ struct pci_id_descr *dev_descr;
+
+ 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,
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;
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:
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);
-
- if (m) {
- count = pvt->mce_count;
- memcpy(m, &pvt->mce_entry, sizeof(*m) * count);
- }
- pvt->mce_count = 0;
+ /*
+ * 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 + MCE_LOG_LEN - pvt->mce_in)
+ % MCE_LOG_LEN;
+ if (!count)
+ goto check_ce_error;
+
+ m = pvt->mce_outentry;
+ 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();
+ 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]);
-
- kfree(m);
+ i7core_mce_output_error(mci, &pvt->mce_outentry[i]);
- /* check memory count errors */
+ /*
+ * Now, let's increment CE error counts
+ */
+check_ce_error:
if (!pvt->is_registered)
i7core_udimm_check_mc_ecc_err(mci);
else
* 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)
{
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
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) {
- 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;
- }
+#endif
- 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) % MCE_LOG_LEN == pvt->mce_in) {
+ smp_wmb();
+ pvt->mce_overrun++;
+ return 0;
}
- spin_unlock_irqrestore(&pvt->mce_lock, flags);
+
+ /* 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) % MCE_LOG_LEN;
/* Handle fatal errors immediately */
if (mce->mcgstatus & 1)
/* 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)) {
}
fail:
- edac_mc_free(mci);
+ if (rc < 0)
+ edac_mc_free(mci);
return rc;
}
/* 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_table);
if (unlikely(rc < 0))
goto fail0;
/* Ensure that the OPSTATE is set correctly for POLL or NMI */
opstate_init();
- i7core_xeon_pci_fixup();
+ i7core_xeon_pci_fixup(pci_dev_table);
pci_rc = pci_register_driver(&i7core_driver);