diff options
Diffstat (limited to 'drivers/edac')
-rw-r--r-- | drivers/edac/edac_core.h | 11 | ||||
-rw-r--r-- | drivers/edac/edac_mc.c | 12 | ||||
-rw-r--r-- | drivers/edac/edac_mc_sysfs.c | 82 | ||||
-rw-r--r-- | drivers/edac/i7core_edac.c | 432 |
4 files changed, 313 insertions, 224 deletions
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index ce7146677e9b..d7ca43a828bd 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h @@ -42,8 +42,10 @@ #if PAGE_SHIFT < 20 #define PAGES_TO_MiB( pages ) ( ( pages ) >> ( 20 - PAGE_SHIFT ) ) +#define MiB_TO_PAGES(mb) ((mb) >> (20 - PAGE_SHIFT)) #else /* PAGE_SHIFT > 20 */ #define PAGES_TO_MiB( pages ) ( ( pages ) << ( PAGE_SHIFT - 20 ) ) +#define MiB_TO_PAGES(mb) ((mb) >> (PAGE_SHIFT - 20)) #endif #define edac_printk(level, prefix, fmt, arg...) \ @@ -328,7 +330,7 @@ struct csrow_info { struct mcidev_sysfs_group { const char *name; /* group name */ - struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */ + const struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */ }; struct mcidev_sysfs_group_kobj { @@ -336,7 +338,7 @@ struct mcidev_sysfs_group_kobj { struct kobject kobj; /* kobj for the group */ - struct mcidev_sysfs_group *grp; /* group description table */ + const struct mcidev_sysfs_group *grp; /* group description table */ struct mem_ctl_info *mci; /* the parent */ }; @@ -347,7 +349,7 @@ struct mcidev_sysfs_group_kobj { 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 */ + const 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 *); @@ -440,7 +442,7 @@ struct mem_ctl_info { * If attributes are desired, then set to array of attributes * If no attributes are desired, leave NULL */ - struct mcidev_sysfs_attribute *mc_driver_sysfs_attributes; + const struct mcidev_sysfs_attribute *mc_driver_sysfs_attributes; /* work struct for this MC */ struct delayed_work work; @@ -810,6 +812,7 @@ extern struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows, extern int edac_mc_add_mc(struct mem_ctl_info *mci); extern void edac_mc_free(struct mem_ctl_info *mci); extern struct mem_ctl_info *edac_mc_find(int idx); +extern struct mem_ctl_info *find_mci_by_dev(struct device *dev); extern struct mem_ctl_info *edac_mc_del_mc(struct device *dev); extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page); diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 6b21e25f7a84..ba6586a69ccc 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -207,6 +207,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows, } mci->op_state = OP_ALLOC; + INIT_LIST_HEAD(&mci->grp_kobj_list); /* * Initialize the 'root' kobj for the edac_mc controller @@ -234,18 +235,24 @@ EXPORT_SYMBOL_GPL(edac_mc_alloc); */ void edac_mc_free(struct mem_ctl_info *mci) { + debugf1("%s()\n", __func__); + edac_mc_unregister_sysfs_main_kobj(mci); + + /* free the mci instance memory here */ + kfree(mci); } EXPORT_SYMBOL_GPL(edac_mc_free); -/* +/** * find_mci_by_dev * * scan list of controllers looking for the one that manages * the 'dev' device + * @dev: pointer to a struct device related with the MCI */ -static struct mem_ctl_info *find_mci_by_dev(struct device *dev) +struct mem_ctl_info *find_mci_by_dev(struct device *dev) { struct mem_ctl_info *mci; struct list_head *item; @@ -261,6 +268,7 @@ static struct mem_ctl_info *find_mci_by_dev(struct device *dev) return NULL; } +EXPORT_SYMBOL_GPL(find_mci_by_dev); /* * handler for EDAC to check if NMI type handler has asserted interrupt diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index a4135860149b..dce61f7ba38b 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -631,9 +631,6 @@ static void edac_mci_control_release(struct kobject *kobj) /* decrement the module ref count */ module_put(mci->owner); - - /* free the mci instance memory here */ - kfree(mci); } static struct kobj_type ktype_mci = { @@ -713,6 +710,8 @@ fail_out: */ void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) { + debugf1("%s()\n", __func__); + /* delete the kobj from the mc_kset */ kobject_put(&mci->edac_mci_kobj); } @@ -760,8 +759,6 @@ static void edac_inst_grp_release(struct kobject *kobj) grp = container_of(kobj, struct mcidev_sysfs_group_kobj, kobj); mci = grp->mci; - - kobject_put(&mci->edac_mci_kobj); } /* Intermediate show/store table */ @@ -784,7 +781,7 @@ static struct kobj_type ktype_inst_grp = { * object tree. */ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, - struct mcidev_sysfs_attribute *sysfs_attrib, + const struct mcidev_sysfs_attribute *sysfs_attrib, struct kobject *kobj) { int err; @@ -792,6 +789,7 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, debugf1("%s()\n", __func__); while (sysfs_attrib) { + debugf1("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib); if (sysfs_attrib->grp) { struct mcidev_sysfs_group_kobj *grp_kobj; @@ -799,10 +797,9 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, 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; + list_add_tail(&grp_kobj->list, &mci->grp_kobj_list); debugf0("%s() grp %s, mci %p\n", __func__, sysfs_attrib->grp->name, mci); @@ -811,26 +808,28 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, &ktype_inst_grp, &mci->edac_mci_kobj, sysfs_attrib->grp->name); - if (err) + if (err < 0) { + printk(KERN_ERR "kobject_init_and_add failed: %d\n", err); return err; - + } err = edac_create_mci_instance_attributes(mci, grp_kobj->grp->mcidev_attr, &grp_kobj->kobj); - if (err) + if (err < 0) 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); + if (err < 0) { + printk(KERN_ERR "sysfs_create_file failed: %d\n", err); + return err; + } } else break; - if (err) { - return err; - } sysfs_attrib++; } @@ -843,7 +842,7 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, * directory of this mci instance. */ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, - struct mcidev_sysfs_attribute *sysfs_attrib, + const struct mcidev_sysfs_attribute *sysfs_attrib, struct kobject *kobj, int count) { struct mcidev_sysfs_group_kobj *grp_kobj, *tmp; @@ -855,13 +854,24 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, * Remove first all the atributes */ while (sysfs_attrib) { + debugf1("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib); if (sysfs_attrib->grp) { - list_for_each_entry(grp_kobj, &mci->grp_kobj_list, - list) - if (grp_kobj->grp == sysfs_attrib->grp) + debugf1("%s() seeking for group %s\n", + __func__, sysfs_attrib->grp->name); + list_for_each_entry(grp_kobj, + &mci->grp_kobj_list, list) { + debugf1("%s() grp_kobj->grp = %p\n",__func__, grp_kobj->grp); + if (grp_kobj->grp == sysfs_attrib->grp) { edac_remove_mci_instance_attributes(mci, grp_kobj->grp->mcidev_attr, &grp_kobj->kobj, count + 1); + debugf0("%s() group %s\n", __func__, + sysfs_attrib->grp->name); + kobject_put(&grp_kobj->kobj); + } + } + debugf1("%s() end of seeking for group %s\n", + __func__, sysfs_attrib->grp->name); } else if (sysfs_attrib->attr.name) { debugf0("%s() file %s\n", __func__, sysfs_attrib->attr.name); @@ -871,15 +881,14 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, 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); - } + /* Remove the group objects */ + if (count) + return; + list_for_each_entry_safe(grp_kobj, tmp, + &mci->grp_kobj_list, list) { + list_del(&grp_kobj->list); + kfree(grp_kobj); + } } @@ -971,6 +980,7 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) debugf0("%s()\n", __func__); /* remove all csrow kobjects */ + debugf0("%s() unregister this mci kobj\n", __func__); for (i = 0; i < mci->nr_csrows; i++) { if (mci->csrows[i].nr_pages > 0) { debugf0("%s() unreg csrow-%d\n", __func__, i); @@ -978,20 +988,20 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) } } - debugf0("%s() remove_link\n", __func__); + /* remove this mci instance's attribtes */ + if (mci->mc_driver_sysfs_attributes) { + debugf0("%s() unregister mci private attributes\n", __func__); + edac_remove_mci_instance_attributes(mci, + mci->mc_driver_sysfs_attributes, + &mci->edac_mci_kobj, 0); + } /* remove the symlink */ + debugf0("%s() remove_link\n", __func__); sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK); - debugf0("%s() remove_mci_instance\n", __func__); - - /* remove this mci instance's attribtes */ - 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 */ + debugf0("%s() remove_mci_instance\n", __func__); kobject_put(&mci->edac_mci_kobj); } diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 0fd5b85a0f75..362861c15779 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -39,6 +39,14 @@ #include "edac_core.h" +/* Static vars */ +static LIST_HEAD(i7core_edac_list); +static DEFINE_MUTEX(i7core_edac_lock); +static int probed; + +static int use_pci_fixup; +module_param(use_pci_fixup, int, 0444); +MODULE_PARM_DESC(use_pci_fixup, "Enable PCI fixup to seek for hidden devices"); /* * 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. @@ -212,8 +220,8 @@ struct pci_id_descr { }; struct pci_id_table { - struct pci_id_descr *descr; - int n_devs; + const struct pci_id_descr *descr; + int n_devs; }; struct i7core_dev { @@ -235,8 +243,6 @@ struct i7core_pvt { struct i7core_inject inject; struct i7core_channel channel[NUM_CHANS]; - int channels; /* Number of active channels */ - int ce_count_available; int csrow_map[NUM_CHANS][MAX_DIMMS]; @@ -261,22 +267,22 @@ struct i7core_pvt { /* Count indicator to show errors not got */ unsigned mce_overrun; -}; -/* Static vars */ -static LIST_HEAD(i7core_edac_list); -static DEFINE_MUTEX(i7core_edac_lock); + /* Struct to control EDAC polling */ + struct edac_pci_ctl_info *i7core_pci; +}; #define PCI_DESCR(device, function, device_id) \ .dev = (device), \ .func = (function), \ .dev_id = (device_id) -struct pci_id_descr pci_dev_descr_i7core_nehalem[] = { +static const 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) }, - /* Exists only for 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) }, @@ -297,19 +303,9 @@ struct pci_id_descr pci_dev_descr_i7core_nehalem[] = { { 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_NONCORE) }, - }; -struct pci_id_descr pci_dev_descr_lynnfield[] = { +static const 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) }, @@ -323,15 +319,9 @@ struct pci_id_descr pci_dev_descr_lynnfield[] = { { 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[] = { +static const 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) }, @@ -356,17 +346,14 @@ struct pci_id_descr pci_dev_descr_i7core_westmere[] = { { 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[] = { +#define PCI_ID_TABLE_ENTRY(A) { .descr=A, .n_devs = ARRAY_SIZE(A) } +static const 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), + {0,} /* 0 terminated list. */ }; /* @@ -378,8 +365,6 @@ static const struct pci_device_id i7core_pci_tbl[] __devinitdata = { {0,} /* 0 terminated list. */ }; -static struct edac_pci_ctl_info *i7core_pci; - /**************************************************************************** Anciliary status routines ****************************************************************************/ @@ -442,6 +427,36 @@ static struct i7core_dev *get_i7core_dev(u8 socket) return NULL; } +static struct i7core_dev *alloc_i7core_dev(u8 socket, + const struct pci_id_table *table) +{ + struct i7core_dev *i7core_dev; + + i7core_dev = kzalloc(sizeof(*i7core_dev), GFP_KERNEL); + if (!i7core_dev) + return NULL; + + i7core_dev->pdev = kzalloc(sizeof(*i7core_dev->pdev) * table->n_devs, + GFP_KERNEL); + if (!i7core_dev->pdev) { + kfree(i7core_dev); + return NULL; + } + + i7core_dev->socket = socket; + i7core_dev->n_devs = table->n_devs; + list_add_tail(&i7core_dev->list, &i7core_edac_list); + + return i7core_dev; +} + +static void free_i7core_dev(struct i7core_dev *i7core_dev) +{ + list_del(&i7core_dev->list); + kfree(i7core_dev->pdev); + kfree(i7core_dev); +} + /**************************************************************************** Memory check routines ****************************************************************************/ @@ -484,7 +499,7 @@ static struct pci_dev *get_pdev_slot_func(u8 socket, unsigned slot, * 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, +static int i7core_get_active_channels(const u8 socket, unsigned *channels, unsigned *csrows) { struct pci_dev *pdev = NULL; @@ -545,12 +560,13 @@ static int i7core_get_active_channels(u8 socket, unsigned *channels, return 0; } -static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) +static int get_dimm_config(const struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; struct csrow_info *csr; struct pci_dev *pdev; int i, j; + int csrow = 0; unsigned long last_page = 0; enum edac_type mode; enum mem_type mtype; @@ -664,13 +680,9 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) RANKOFFSET(dimm_dod[j]), banks, ranks, rows, cols); -#if PAGE_SHIFT > 20 - npages = size >> (PAGE_SHIFT - 20); -#else - npages = size << (20 - PAGE_SHIFT); -#endif + npages = MiB_TO_PAGES(size); - csr = &mci->csrows[*csrow]; + csr = &mci->csrows[csrow]; csr->first_page = last_page + 1; last_page += npages; csr->last_page = last_page; @@ -678,13 +690,13 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) 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; csr->channels[0].ce_count = 0; - pvt->csrow_map[i][j] = *csrow; + pvt->csrow_map[i][j] = csrow; switch (banks) { case 4: @@ -703,7 +715,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) csr->edac_mode = mode; csr->mtype = mtype; - (*csrow)++; + csrow++; } pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]); @@ -736,7 +748,7 @@ static int get_dimm_config(struct mem_ctl_info *mci, int *csrow) we're disabling error injection on all write calls to the sysfs nodes that controls the error code injection. */ -static int disable_inject(struct mem_ctl_info *mci) +static int disable_inject(const struct mem_ctl_info *mci) { struct i7core_pvt *pvt = mci->pvt_info; @@ -921,7 +933,7 @@ 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) +static int write_and_test(struct pci_dev *dev, const int where, const u32 val) { u32 read; int count; @@ -1120,35 +1132,34 @@ DECLARE_COUNTER(2); * Sysfs struct */ - -static struct mcidev_sysfs_attribute i7core_addrmatch_attrs[] = { +static const 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 } } + { } /* End of list */ }; -static struct mcidev_sysfs_group i7core_inject_addrmatch = { +static const struct mcidev_sysfs_group i7core_inject_addrmatch = { .name = "inject_addrmatch", .mcidev_attr = i7core_addrmatch_attrs, }; -static struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = { +static const struct mcidev_sysfs_attribute i7core_udimm_counters_attrs[] = { ATTR_COUNTER(0), ATTR_COUNTER(1), ATTR_COUNTER(2), { .attr = { .name = NULL } } }; -static struct mcidev_sysfs_group i7core_udimm_counters = { +static const 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[] = { +static const struct mcidev_sysfs_attribute i7core_sysfs_rdimm_attrs[] = { { .attr = { .name = "inject_section", @@ -1180,8 +1191,44 @@ static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = { .show = i7core_inject_enable_show, .store = i7core_inject_enable_store, }, - { .attr = { .name = NULL } }, /* Reserved for udimm counters */ - { .attr = { .name = NULL } } + { } /* End of list */ +}; + +static const struct mcidev_sysfs_attribute i7core_sysfs_udimm_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, + }, { + .grp = &i7core_inject_addrmatch, + }, { + .attr = { + .name = "inject_enable", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = i7core_inject_enable_show, + .store = i7core_inject_enable_store, + }, { + .grp = &i7core_udimm_counters, + }, + { } /* End of list */ }; /**************************************************************************** @@ -1189,7 +1236,7 @@ static struct mcidev_sysfs_attribute i7core_sysfs_attrs[] = { ****************************************************************************/ /* - * i7core_put_devices 'put' all the devices that we have + * i7core_put_all_devices 'put' all the devices that we have * reserved via 'get' */ static void i7core_put_devices(struct i7core_dev *i7core_dev) @@ -1206,23 +1253,23 @@ static void i7core_put_devices(struct i7core_dev *i7core_dev) PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); pci_dev_put(pdev); } - kfree(i7core_dev->pdev); - list_del(&i7core_dev->list); - kfree(i7core_dev); } static void i7core_put_all_devices(void) { struct i7core_dev *i7core_dev, *tmp; - list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) + list_for_each_entry_safe(i7core_dev, tmp, &i7core_edac_list, list) { i7core_put_devices(i7core_dev); + free_i7core_dev(i7core_dev); + } } -static void __init i7core_xeon_pci_fixup(struct pci_id_table *table) +static void __init i7core_xeon_pci_fixup(const struct pci_id_table *table) { 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 @@ -1257,16 +1304,18 @@ static unsigned i7core_pci_lastbus(void) } /* - * i7core_get_devices Find and perform 'get' operation on the MCH's + * i7core_get_all_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 */ -int i7core_get_onedevice(struct pci_dev **prev, int devno, - struct pci_id_descr *dev_descr, unsigned n_devs, - unsigned last_bus) +static int i7core_get_onedevice(struct pci_dev **prev, + const struct pci_id_table *table, + const unsigned devno, + const unsigned last_bus) { struct i7core_dev *i7core_dev; + const struct pci_id_descr *dev_descr = &table->descr[devno]; struct pci_dev *pdev = NULL; u8 bus = 0; @@ -1275,20 +1324,6 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno, pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 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 (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); - - 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; @@ -1315,18 +1350,11 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno, 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) { - kfree(i7core_dev); + i7core_dev = alloc_i7core_dev(socket, table); + if (!i7core_dev) { + pci_dev_put(pdev); return -ENOMEM; } - i7core_dev->socket = socket; - i7core_dev->n_devs = n_devs; - list_add_tail(&i7core_dev->list, &i7core_edac_list); } if (i7core_dev->pdev[devno]) { @@ -1368,27 +1396,31 @@ int i7core_get_onedevice(struct pci_dev **prev, int devno, dev_descr->func, PCI_VENDOR_ID_INTEL, dev_descr->dev_id); + /* + * As stated on drivers/pci/search.c, the reference count for + * @from is always decremented if it is not %NULL. So, as we need + * to get all devices up to null, we need to do a get for the device + */ + pci_dev_get(pdev); + *prev = pdev; return 0; } -static int i7core_get_devices(struct pci_id_table *table) +static int i7core_get_all_devices(void) { int i, rc, last_bus; struct pci_dev *pdev = NULL; - struct pci_id_descr *dev_descr; + const struct pci_id_table *table = pci_dev_table; last_bus = i7core_pci_lastbus(); 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, + rc = i7core_get_onedevice(&pdev, table, i, last_bus); if (rc < 0) { if (i == 0) { @@ -1404,7 +1436,6 @@ static int i7core_get_devices(struct pci_id_table *table) } return 0; - return 0; } static int mci_bind_devs(struct mem_ctl_info *mci, @@ -1414,10 +1445,6 @@ static int mci_bind_devs(struct mem_ctl_info *mci, struct pci_dev *pdev; int i, func, slot; - /* Associates i7core_dev and mci for future usage */ - pvt->i7core_dev = i7core_dev; - i7core_dev->mci = mci; - pvt->is_registered = 0; for (i = 0; i < i7core_dev->n_devs; i++) { pdev = i7core_dev->pdev[i]; @@ -1448,15 +1475,6 @@ 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: @@ -1470,7 +1488,9 @@ error: Error check routines ****************************************************************************/ static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci, - int chan, int dimm, int add) + const int chan, + const int dimm, + const int add) { char *msg; struct i7core_pvt *pvt = mci->pvt_info; @@ -1487,7 +1507,10 @@ static void i7core_rdimm_update_csrow(struct mem_ctl_info *mci, } static void i7core_rdimm_update_ce_count(struct mem_ctl_info *mci, - int chan, int new0, int new1, int new2) + const int chan, + const int new0, + const int new1, + const int new2) { struct i7core_pvt *pvt = mci->pvt_info; int add0 = 0, add1 = 0, add2 = 0; @@ -1641,7 +1664,7 @@ static void i7core_udimm_check_mc_ecc_err(struct mem_ctl_info *mci) * fields */ static void i7core_mce_output_error(struct mem_ctl_info *mci, - struct mce *m) + const struct mce *m) { struct i7core_pvt *pvt = mci->pvt_info; char *type, *optype, *err, *msg; @@ -1845,28 +1868,85 @@ static int i7core_mce_check_error(void *priv, struct mce *mce) return 1; } -static int i7core_register_mci(struct i7core_dev *i7core_dev, - int num_channels, int num_csrows) +static void i7core_pci_ctl_create(struct i7core_pvt *pvt) +{ + pvt->i7core_pci = edac_pci_create_generic_ctl( + &pvt->i7core_dev->pdev[0]->dev, + EDAC_MOD_STR); + if (unlikely(!pvt->i7core_pci)) + pr_warn("Unable to setup PCI error report via EDAC\n"); +} + +static void i7core_pci_ctl_release(struct i7core_pvt *pvt) +{ + if (likely(pvt->i7core_pci)) + edac_pci_release_generic_ctl(pvt->i7core_pci); + else + i7core_printk(KERN_ERR, + "Couldn't find mem_ctl_info for socket %d\n", + pvt->i7core_dev->socket); + pvt->i7core_pci = NULL; +} + +static void i7core_unregister_mci(struct i7core_dev *i7core_dev) +{ + struct mem_ctl_info *mci = i7core_dev->mci; + struct i7core_pvt *pvt; + + if (unlikely(!mci || !mci->pvt_info)) { + debugf0("MC: " __FILE__ ": %s(): dev = %p\n", + __func__, &i7core_dev->pdev[0]->dev); + + i7core_printk(KERN_ERR, "Couldn't find mci handler\n"); + return; + } + + pvt = mci->pvt_info; + + debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n", + __func__, mci, &i7core_dev->pdev[0]->dev); + + /* Disable MCE NMI handler */ + edac_mce_unregister(&pvt->edac_mce); + + /* Disable EDAC polling */ + i7core_pci_ctl_release(pvt); + + /* Remove MC sysfs nodes */ + edac_mc_del_mc(mci->dev); + + debugf1("%s: free mci struct\n", mci->ctl_name); + kfree(mci->ctl_name); + edac_mc_free(mci); + i7core_dev->mci = NULL; +} + +static int i7core_register_mci(struct i7core_dev *i7core_dev) { struct mem_ctl_info *mci; struct i7core_pvt *pvt; - int csrow = 0; - int rc; + int rc, channels, csrows; + + /* Check the number of active and not disabled channels */ + rc = i7core_get_active_channels(i7core_dev->socket, &channels, &csrows); + if (unlikely(rc < 0)) + return rc; /* allocate a new MC control structure */ - mci = edac_mc_alloc(sizeof(*pvt), num_csrows, num_channels, - i7core_dev->socket); + mci = edac_mc_alloc(sizeof(*pvt), csrows, channels, i7core_dev->socket); if (unlikely(!mci)) return -ENOMEM; - debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); - - /* record ptr to the generic device */ - mci->dev = &i7core_dev->pdev[0]->dev; + debugf0("MC: " __FILE__ ": %s(): mci = %p, dev = %p\n", + __func__, mci, &i7core_dev->pdev[0]->dev); pvt = mci->pvt_info; memset(pvt, 0, sizeof(*pvt)); + /* Associates i7core_dev and mci for future usage */ + pvt->i7core_dev = i7core_dev; + i7core_dev->mci = mci; + /* * FIXME: how to handle RDDR3 at MCI level? It is possible to have * Mixed RDDR3/UDDR3 with Nehalem, provided that they are on different @@ -1881,17 +1961,23 @@ 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_sysfs_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, i7core_dev); if (unlikely(rc < 0)) - goto fail; + goto fail0; + + if (pvt->is_registered) + mci->mc_driver_sysfs_attributes = i7core_sysfs_rdimm_attrs; + else + mci->mc_driver_sysfs_attributes = i7core_sysfs_udimm_attrs; /* Get dimm basic config */ - get_dimm_config(mci, &csrow); + get_dimm_config(mci); + /* record ptr to the generic device */ + mci->dev = &i7core_dev->pdev[0]->dev; + /* 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 (unlikely(edac_mc_add_mc(mci))) { @@ -1902,19 +1988,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev, */ rc = -EINVAL; - goto fail; - } - - /* allocating generic PCI control info */ - 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", - __func__); - printk(KERN_WARNING - "%s(): PCI error report via EDAC not setup\n", - __func__); + goto fail0; } /* Default error mask is any memory */ @@ -1925,19 +1999,28 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev, pvt->inject.page = -1; pvt->inject.col = -1; + /* allocating generic PCI control info */ + i7core_pci_ctl_create(pvt); + /* Registers on edac_mce in order to receive memory errors */ pvt->edac_mce.priv = mci; pvt->edac_mce.check_error = i7core_mce_check_error; - rc = edac_mce_register(&pvt->edac_mce); if (unlikely(rc < 0)) { debugf0("MC: " __FILE__ ": %s(): failed edac_mce_register()\n", __func__); + goto fail1; } -fail: - if (rc < 0) - edac_mc_free(mci); + return 0; + +fail1: + i7core_pci_ctl_release(pvt); + edac_mc_del_mc(mci->dev); +fail0: + kfree(mci->ctl_name); + edac_mc_free(mci); + i7core_dev->mci = NULL; return rc; } @@ -1949,8 +2032,6 @@ fail: * < 0 for error code */ -static int probed = 0; - static int __devinit i7core_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -1965,25 +2046,16 @@ static int __devinit i7core_probe(struct pci_dev *pdev, */ if (unlikely(probed >= 1)) { mutex_unlock(&i7core_edac_lock); - return -EINVAL; + return -ENODEV; } probed++; - rc = i7core_get_devices(pci_dev_table); + rc = i7core_get_all_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; - - rc = i7core_register_mci(i7core_dev, channels, csrows); + rc = i7core_register_mci(i7core_dev); if (unlikely(rc < 0)) goto fail1; } @@ -1994,6 +2066,9 @@ static int __devinit i7core_probe(struct pci_dev *pdev, return 0; fail1: + list_for_each_entry(i7core_dev, &i7core_edac_list, list) + i7core_unregister_mci(i7core_dev); + i7core_put_all_devices(); fail0: mutex_unlock(&i7core_edac_lock); @@ -2006,14 +2081,10 @@ fail0: */ static void __devexit i7core_remove(struct pci_dev *pdev) { - struct mem_ctl_info *mci; - struct i7core_dev *i7core_dev, *tmp; + struct i7core_dev *i7core_dev; 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 @@ -2023,22 +2094,18 @@ static void __devexit i7core_remove(struct pci_dev *pdev) */ mutex_lock(&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; - - 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); - } + + if (unlikely(!probed)) { + mutex_unlock(&i7core_edac_lock); + return; } + + list_for_each_entry(i7core_dev, &i7core_edac_list, list) + i7core_unregister_mci(i7core_dev); + + /* Release PCI resources */ + i7core_put_all_devices(); + probed--; mutex_unlock(&i7core_edac_lock); @@ -2070,7 +2137,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_dev_table); + if (use_pci_fixup) + i7core_xeon_pci_fixup(pci_dev_table); pci_rc = pci_register_driver(&i7core_driver); |