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
  ...
This commit is contained in:
Linus Torvalds 2010-06-04 15:39:54 -07:00
commit 9a9620db07
13 changed files with 2598 additions and 44 deletions

View file

@ -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
bool
config EDAC_AMD64
tristate "AMD64 (Opteron, Athlon64) K8, F10h, F11h"
depends on EDAC_MM_EDAC && K8_NB && X86_64 && PCI && EDAC_DECODE_MCE
@ -166,6 +169,16 @@ 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
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
tristate "Intel 82860"
depends on EDAC_MM_EDAC && PCI && X86_32

View file

@ -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
@ -23,6 +24,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

View file

@ -341,12 +341,30 @@ struct csrow_info {
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
* used for driver sysfs attributes and in mem_ctl_info
* sysfs top level entries
*/
struct mcidev_sysfs_attribute {
struct attribute attr;
/* It should use either attr or grp */
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 (*store)(struct mem_ctl_info *, const char *,size_t);
};
@ -424,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.
*

View file

@ -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,28 +730,118 @@ 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, 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
* 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 mem_ctl_info *mci,
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;
debugf1("%s()\n", __func__);
while (sysfs_attrib) {
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) {
return err;
}
sysfs_attrib++;
}
@ -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
* 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
* adding each attribute listed to this mci instance's kobject
debugf1("%s()\n", __func__);
/*
* loop if there are attributes and until we hit a NULL entry
* Remove first all the atributes
*/
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) {
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++;
}
/*
* 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)
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);
@ -806,7 +925,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,
mci->mc_driver_sysfs_attributes,
&mci->edac_mci_kobj);
if (err) {
debugf1("%s() failure to create mci attributes\n",
__func__);
@ -841,7 +962,8 @@ fail1:
}
/* 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 */
sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK);
@ -875,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);
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 */

61
drivers/edac/edac_mce.c Normal file
View file

@ -0,0 +1,61 @@
/* 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 <[email protected]>");
MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
MODULE_DESCRIPTION("EDAC Driver for mcelog captured errors");

2078
drivers/edac/i7core_edac.c Normal file

File diff suppressed because it is too large Load diff