Commit 9a9620db authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'linux_next' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/i7core

* 'linux_next' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/i7core: (83 commits)
  i7core_edac: Better describe the supported devices
  Add support for Westmere to i7core_edac driver
  i7core_edac: don't free on success
  i7core_edac: Add support for X5670
  Always call i7core_[ur]dimm_check_mc_ecc_err
  i7core_edac: fix memory leak of i7core_dev
  EDAC: add __init to i7core_xeon_pci_fixup
  i7core_edac: Fix wrong device id for channel 1 devices
  i7core: add support for Lynnfield alternate address
  i7core_edac: Add initial support for Lynnfield
  i7core_edac: do not export static functions
  edac: fix i7core build
  edac: i7core_edac produces undefined behaviour on 32bit
  i7core_edac: Use a more generic approach for probing PCI devices
  i7core_edac: PCI device is called NONCORE, instead of NOCORE
  i7core_edac: Fix ringbuffer maxsize
  i7core_edac: First store, then increment
  i7core_edac: Better parse "any" addrmask
  i7core_edac: Use a lockless ringbuffer
  edac: Create an unique instance for each kobj
  ...
parents e620d1e3 52707f91
...@@ -6,6 +6,8 @@ Written by Doug Thompson <dougthompson@xmission.com> ...@@ -6,6 +6,8 @@ Written by Doug Thompson <dougthompson@xmission.com>
7 Dec 2005 7 Dec 2005
17 Jul 2007 Updated 17 Jul 2007 Updated
(c) Mauro Carvalho Chehab <mchehab@redhat.com>
05 Aug 2009 Nehalem interface
EDAC is maintained and written by: EDAC is maintained and written by:
...@@ -717,3 +719,153 @@ unique drivers for their hardware systems. ...@@ -717,3 +719,153 @@ unique drivers for their hardware systems.
The 'test_device_edac' sample driver is located at the The 'test_device_edac' sample driver is located at the
bluesmoke.sourceforge.net project site for EDAC. 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. 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 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.
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/mc?/:
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 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 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,
inject_section:
specifies what ECC cache section will get the error:
3 for both
2 for the highest
1 for the lowest
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 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
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))
3) Nehalem specific Corrected Error memory counters
Nehalem have some registers to count memory errors. The driver uses those
registers to report Corrected Errors on devices with Registered Dimms.
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.
They can be read by looking at the contents of all_channel_counts/
$ 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.
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 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, 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
...@@ -53,6 +53,8 @@ extern int pcibios_last_bus; ...@@ -53,6 +53,8 @@ extern int pcibios_last_bus;
extern struct pci_bus *pci_root_bus; extern struct pci_bus *pci_root_bus;
extern struct pci_ops pci_root_ops; extern struct pci_ops pci_root_ops;
void pcibios_scan_specific_bus(int busn);
/* pci-irq.c */ /* pci-irq.c */
struct irq_info { struct irq_info {
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/edac_mce.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/hw_irq.h> #include <asm/hw_irq.h>
...@@ -168,6 +169,15 @@ void mce_log(struct mce *mce) ...@@ -168,6 +169,15 @@ void mce_log(struct mce *mce)
for (;;) { for (;;) {
entry = rcu_dereference_check_mce(mcelog.next); entry = rcu_dereference_check_mce(mcelog.next);
for (;;) { 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. * When the buffer fills up discard new entries.
* Assume that the earlier errors are the more * Assume that the earlier errors are the more
......
...@@ -11,28 +11,14 @@ ...@@ -11,28 +11,14 @@
*/ */
static void __devinit pcibios_fixup_peer_bridges(void) static void __devinit pcibios_fixup_peer_bridges(void)
{ {
int n, devfn; int n;
long node;
if (pcibios_last_bus <= 0 || pcibios_last_bus > 0xff) if (pcibios_last_bus <= 0 || pcibios_last_bus > 0xff)
return; return;
DBG("PCI: Peer bridge fixup\n"); DBG("PCI: Peer bridge fixup\n");
for (n=0; n <= pcibios_last_bus; n++) { for (n=0; n <= pcibios_last_bus; n++)
u32 l; pcibios_scan_specific_bus(n);
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;
}
}
}
} }
int __init pci_legacy_init(void) int __init pci_legacy_init(void)
...@@ -50,6 +36,28 @@ int __init pci_legacy_init(void) ...@@ -50,6 +36,28 @@ int __init pci_legacy_init(void)
return 0; return 0;
} }
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;
}
}
}
EXPORT_SYMBOL_GPL(pcibios_scan_specific_bus);
int __init pci_subsys_init(void) int __init pci_subsys_init(void)
{ {
/* /*
......
...@@ -69,6 +69,9 @@ config EDAC_MM_EDAC ...@@ -69,6 +69,9 @@ config EDAC_MM_EDAC
occurred so that a particular failing memory module can be occurred so that a particular failing memory module can be
replaced. If unsure, select 'Y'. replaced. If unsure, select 'Y'.
config EDAC_MCE
bool
config EDAC_AMD64 config EDAC_AMD64
tristate "AMD64 (Opteron, Athlon64) K8, F10h, F11h" tristate "AMD64 (Opteron, Athlon64) K8, F10h, F11h"
depends on EDAC_MM_EDAC && K8_NB && X86_64 && PCI && EDAC_DECODE_MCE depends on EDAC_MM_EDAC && K8_NB && X86_64 && PCI && EDAC_DECODE_MCE
...@@ -166,6 +169,16 @@ config EDAC_I5400 ...@@ -166,6 +169,16 @@ config EDAC_I5400
Support for error detection and correction the Intel Support for error detection and correction the Intel
i5400 MCH chipset (Seaburg). i5400 MCH chipset (Seaburg).
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 that exists on
newer processors like i7 Core, i7 Core Extreme, Xeon 35xx
and Xeon 55xx processors.
config EDAC_I82860 config EDAC_I82860
tristate "Intel 82860" tristate "Intel 82860"
depends on EDAC_MM_EDAC && PCI && X86_32 depends on EDAC_MM_EDAC && PCI && X86_32
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
obj-$(CONFIG_EDAC) := edac_stub.o obj-$(CONFIG_EDAC) := edac_stub.o
obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.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_mc.o edac_device.o edac_mc_sysfs.o edac_pci_sysfs.o
edac_core-objs += edac_module.o edac_device_sysfs.o edac_core-objs += edac_module.o edac_device_sysfs.o
...@@ -23,6 +24,7 @@ obj-$(CONFIG_EDAC_CPC925) += cpc925_edac.o ...@@ -23,6 +24,7 @@ obj-$(CONFIG_EDAC_CPC925) += cpc925_edac.o
obj-$(CONFIG_EDAC_I5000) += i5000_edac.o obj-$(CONFIG_EDAC_I5000) += i5000_edac.o
obj-$(CONFIG_EDAC_I5100) += i5100_edac.o obj-$(CONFIG_EDAC_I5100) += i5100_edac.o
obj-$(CONFIG_EDAC_I5400) += i5400_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_E7XXX) += e7xxx_edac.o
obj-$(CONFIG_EDAC_E752X) += e752x_edac.o obj-$(CONFIG_EDAC_E752X) += e752x_edac.o
obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o
......
...@@ -341,12 +341,30 @@ struct csrow_info { ...@@ -341,12 +341,30 @@ struct csrow_info {
struct channel_info *channels; struct channel_info *channels;
}; };
struct mcidev_sysfs_group {
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 /* mcidev_sysfs_attribute structure
* used for driver sysfs attributes and in mem_ctl_info * used for driver sysfs attributes and in mem_ctl_info
* sysfs top level entries * sysfs top level entries
*/ */
struct mcidev_sysfs_attribute { struct mcidev_sysfs_attribute {
/* It should use either attr or grp */
struct attribute attr; struct attribute attr;
struct mcidev_sysfs_group *grp; /* Points to a group of attributes */
/* Ops for show/store values at the attribute - not used on group */
ssize_t (*show)(struct mem_ctl_info *,char *); ssize_t (*show)(struct mem_ctl_info *,char *);
ssize_t (*store)(struct mem_ctl_info *, const char *,size_t); ssize_t (*store)(struct mem_ctl_info *, const char *,size_t);
}; };
...@@ -424,6 +442,9 @@ struct mem_ctl_info { ...@@ -424,6 +442,9 @@ struct mem_ctl_info {
/* edac sysfs device control */ /* edac sysfs device control */
struct kobject edac_mci_kobj; 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 /* Additional top controller level attributes, but specified
* by the low level driver. * by the low level driver.
* *
......
...@@ -557,6 +557,8 @@ static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr, ...@@ -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 mem_ctl_info *mem_ctl_info = to_mci(kobj);
struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); 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) if (mcidev_attr->show)
return mcidev_attr->show(mem_ctl_info, buffer); return mcidev_attr->show(mem_ctl_info, buffer);
...@@ -569,6 +571,8 @@ static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, ...@@ -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 mem_ctl_info *mem_ctl_info = to_mci(kobj);
struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); 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) if (mcidev_attr->store)
return mcidev_attr->store(mem_ctl_info, buffer, count); return mcidev_attr->store(mem_ctl_info, buffer, count);
...@@ -726,28 +730,118 @@ void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) ...@@ -726,28 +730,118 @@ void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci)
#define EDAC_DEVICE_SYMLINK "device" #define EDAC_DEVICE_SYMLINK "device"
#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,
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_kobj *grp;
struct mem_ctl_info *mci;
debugf1("%s()\n", __func__);
grp = container_of(kobj, struct mcidev_sysfs_group_kobj, 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,
};
/* /*
* edac_create_mci_instance_attributes * edac_create_mci_instance_attributes
* create MC driver specific attributes at the topmost level * create MC driver specific attributes bellow an specified kobj
* directory of this mci instance. * 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 mem_ctl_info *mci,
struct mcidev_sysfs_attribute *sysfs_attrib,
struct kobject *kobj)
{ {
int err; int err;
struct mcidev_sysfs_attribute *sysfs_attrib;
/* point to the start of the array and iterate over it debugf1("%s()\n", __func__);
* adding each attribute listed to this mci instance's kobject
*/ while (sysfs_attrib) {
sysfs_attrib = mci->mc_driver_sysfs_attributes; if (sysfs_attrib->grp) {
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);
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,
grp_kobj->grp->mcidev_attr,
&grp_kobj->kobj);
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) { if (err) {
return err; return err;
} }
sysfs_attrib++; sysfs_attrib++;
} }
...@@ -759,21 +853,44 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci) ...@@ -759,21 +853,44 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci)
* remove MC driver specific attributes at the topmost level * remove MC driver specific attributes at the topmost level
* directory of this mci instance. * 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 mem_ctl_info *mci,
struct mcidev_sysfs_attribute *sysfs_attrib,
struct kobject *kobj, int count)
{ {
struct mcidev_sysfs_attribute *sysfs_attrib; struct mcidev_sysfs_group_kobj *grp_kobj, *tmp;
/* point to the start of the array and iterate over it debugf1("%s()\n", __func__);
* 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) { * loop if there are attributes and until we hit a NULL entry
sysfs_remove_file(&mci->edac_mci_kobj, * Remove first all the atributes
(struct attribute *) sysfs_attrib); */
while (sysfs_attrib) {
if (sysfs_attrib->grp) {
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);
} else
break;
sysfs_attrib++; 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);
}
} }
...@@ -794,6 +911,8 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) ...@@ -794,6 +911,8 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
debugf0("%s() idx=%d\n", __func__, mci->mc_idx); debugf0("%s() idx=%d\n", __func__, mci->mc_idx);
INIT_LIST_HEAD(&mci->grp_kobj_list);
/* create a symlink for the device */ /* create a symlink for the device */
err = sysfs_create_link(kobj_mci, &mci->dev->kobj, err = sysfs_create_link(kobj_mci, &mci->dev->kobj,
EDAC_DEVICE_SYMLINK); EDAC_DEVICE_SYMLINK);
...@@ -806,7 +925,9 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) ...@@ -806,7 +925,9 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
* then create them now for the driver. * then create them now for the driver.
*/ */
if (mci->mc_driver_sysfs_attributes) { if (mci->mc_driver_sysfs_attributes) {
err = edac_create_mci_instance_attributes(mci); err = edac_create_mci_instance_attributes(mci,
mci->mc_driver_sysfs_attributes,
&mci->edac_mci_kobj);
if (err) { if (err) {
debugf1("%s() failure to create mci attributes\n", debugf1("%s() failure to create mci attributes\n",
__func__); __func__);
...@@ -841,7 +962,8 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) ...@@ -841,7 +962,8 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
} }
/* remove the mci instance's attributes, if any */ /* remove the mci instance's attributes, if any */
edac_remove_mci_instance_attributes(mci); edac_remove_mci_instance_attributes(mci,
mci->mc_driver_sysfs_attributes, &mci->edac_mci_kobj, 0);
/* remove the symlink */ /* remove the symlink */
sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK); sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK);
...@@ -875,8 +997,9 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) ...@@ -875,8 +997,9 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
debugf0("%s() remove_mci_instance\n", __func__); debugf0("%s() remove_mci_instance\n", __func__);
/* remove this mci instance's attribtes */ /* remove this mci instance's attribtes */
edac_remove_mci_instance_attributes(mci); edac_remove_mci_instance_attributes(mci,
mci->mc_driver_sysfs_attributes,
&mci->edac_mci_kobj, 0);
debugf0("%s() unregister this mci kobj\n", __func__); debugf0("%s() unregister this mci kobj\n", __func__);
/* unregister this instance's kobject */ /* unregister this instance's kobject */
......
/* 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 <mchehab@redhat.com>
*
* Red Hat Inc. http://www.redhat.com
*/
#include <linux/module.h>
#include <linux/edac_mce.h>
#include <asm/mce.h>
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_parse(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_parse);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
MODULE_DESCRIPTION("EDAC Driver for mcelog captured errors");
This diff is collapsed.
/* 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 <mchehab@redhat.com>
*
* Red Hat Inc. http://www.redhat.com
*/
#if defined(CONFIG_EDAC_MCE) || \
(defined(CONFIG_EDAC_MCE_MODULE) && defined(MODULE))
#include <asm/mce.h>
#include <linux/list.h>
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
...@@ -632,6 +632,7 @@ void pci_fixup_cardbus(struct pci_bus *); ...@@ -632,6 +632,7 @@ void pci_fixup_cardbus(struct pci_bus *);
/* Generic PCI functions used internally */ /* Generic PCI functions used internally */
void pcibios_scan_specific_bus(int busn);
extern struct pci_bus *pci_find_bus(int domain, int busnr); extern struct pci_bus *pci_find_bus(int domain, int busnr);
void pci_bus_add_devices(const struct pci_bus *bus); void pci_bus_add_devices(const struct pci_bus *bus);
struct pci_bus *pci_scan_bus_parented(struct device *parent, int bus, struct pci_bus *pci_scan_bus_parented(struct device *parent, int bus,
......
...@@ -2532,11 +2532,63 @@ ...@@ -2532,11 +2532,63 @@
#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 #define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930
#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 #define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916
#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 #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_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_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
#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_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_82855PM_HB 0x3340
#define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 #define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429
#define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a #define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a
#define PCI_DEVICE_ID_INTEL_IOAT_TBG6 0x342b #define PCI_DEVICE_ID_INTEL_IOAT_TBG6 0x342b
#define PCI_DEVICE_ID_INTEL_IOAT_TBG7 0x342c #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_TBG0 0x3430
#define PCI_DEVICE_ID_INTEL_IOAT_TBG1 0x3431 #define PCI_DEVICE_ID_INTEL_IOAT_TBG1 0x3431
#define PCI_DEVICE_ID_INTEL_IOAT_TBG2 0x3432 #define PCI_DEVICE_ID_INTEL_IOAT_TBG2 0x3432
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment