diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-29 18:32:37 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-29 18:32:37 -0700 |
commit | 87a5af24e54857e7b15c1f1b0468512ee65c94e3 (patch) | |
tree | ee346852a0fc27f172a5eb57b6e3c7bf111f2fad /drivers/edac/edac_mc_sysfs.c | |
parent | 7e5b2db77b05746613516599c916a8cc2e321077 (diff) | |
parent | 0bf09e829dd4b07227ed5a8bc4ac85752a044458 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac
Pull EDAC internal API changes from Mauro Carvalho Chehab:
"This changeset is the first part of a series of patches that fixes the
EDAC sybsystem. On this set, it changes the Kernel EDAC API in order
to properly represent the Intel i3/i5/i7, Xeon 3xxx/5xxx/7xxx, and
Intel E5-xxxx memory controllers.
The EDAC core used to assume that:
- the DRAM chip select pin is directly accessed by the memory
controller
- when multiple channels are used, they're all filled with the
same type of memory.
None of the above premises is true on Intel memory controllers since
2002, when RAMBUS and FB-DIMMs were introduced, and Advanced Memory
Buffer or by some similar technologies hides the direct access to the
DRAM pins.
So, the existing drivers for those chipsets had to lie to the EDAC
core, in general telling that just one channel is filled. That
produces some hard to understand error messages like:
EDAC MC0: CE row 3, channel 0, label "DIMM1": 1 Unknown error(s): memory read error on FATAL area : cpu=0 Err=0008:00c2 (ch=2), addr = 0xad1f73480 => socket=0, Channel=0(mask=2), rank=1
The location information there (row3 channel 0) is completely bogus:
it has no physical meaning, and are just some random values that the
driver uses to talk with the EDAC core. The error actually happened
at CPU socket 0, channel 0, slot 1, but this is not reported anywhere,
as the EDAC core doesn't know anything about the memory layout. So,
only advanced users that know how the EDAC driver works and that tests
their systems to see how DIMMs are mapped can actually benefit for
such error logs.
This patch series fixes the error report logic, in order to allow the
EDAC to expose the memory architecture used by them to the EDAC core.
So, as the EDAC core now understands how the memory is organized, it
can provide an useful report:
EDAC MC0: CE memory read error on DIMM1 (channel:0 slot:1 page:0x364b1b offset:0x600 grain:32 syndrome:0x0 - count:1 area:DRAM err_code:0001:0090 socket:0 channel_mask:1 rank:4)
The location of the DIMM where the error happened is reported by "MC0"
(cpu socket #0), at "channel:0 slot:1" location, and matches the
physical location of the DIMM.
There are two remaining issues not covered by this patch series:
- The EDAC sysfs API will still report bogus values. So,
userspace tools like edac-utils will still use the bogus data;
- Add a new tracepoint-based way to get the binary information
about the errors.
Those are on a second series of patches (also at -next), but will
probably miss the train for 3.5, due to the slow review process."
Fix up trivial conflict (due to spelling correction of removed code) in
drivers/edac/edac_device.c
* git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac: (42 commits)
i7core: fix ranks information at the per-channel struct
i5000: Fix the fatal error handling
i5100_edac: Fix a warning when compiled with 32 bits
i82975x_edac: Test nr_pages earlier to save a few CPU cycles
e752x_edac: provide more info about how DIMMS/ranks are mapped
i5000_edac: Fix the logic that retrieves memory information
i5400_edac: improve debug messages to better represent the filled memory
edac: Cleanup the logs for i7core and sb edac drivers
edac: Initialize the dimm label with the known information
edac: Remove the legacy EDAC ABI
x38_edac: convert driver to use the new edac ABI
tile_edac: convert driver to use the new edac ABI
sb_edac: convert driver to use the new edac ABI
r82600_edac: convert driver to use the new edac ABI
ppc4xx_edac: convert driver to use the new edac ABI
pasemi_edac: convert driver to use the new edac ABI
mv64x60_edac: convert driver to use the new edac ABI
mpc85xx_edac: convert driver to use the new edac ABI
i82975x_edac: convert driver to use the new edac ABI
i82875p_edac: convert driver to use the new edac ABI
...
Diffstat (limited to 'drivers/edac/edac_mc_sysfs.c')
-rw-r--r-- | drivers/edac/edac_mc_sysfs.c | 70 |
1 files changed, 44 insertions, 26 deletions
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index e9a28f576d1..f6a29b0eedc 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -144,25 +144,31 @@ static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data, static ssize_t csrow_size_show(struct csrow_info *csrow, char *data, int private) { - return sprintf(data, "%u\n", PAGES_TO_MiB(csrow->nr_pages)); + int i; + u32 nr_pages = 0; + + for (i = 0; i < csrow->nr_channels; i++) + nr_pages += csrow->channels[i].dimm->nr_pages; + + return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages)); } static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data, int private) { - return sprintf(data, "%s\n", mem_types[csrow->mtype]); + return sprintf(data, "%s\n", mem_types[csrow->channels[0].dimm->mtype]); } static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data, int private) { - return sprintf(data, "%s\n", dev_types[csrow->dtype]); + return sprintf(data, "%s\n", dev_types[csrow->channels[0].dimm->dtype]); } static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data, int private) { - return sprintf(data, "%s\n", edac_caps[csrow->edac_mode]); + return sprintf(data, "%s\n", edac_caps[csrow->channels[0].dimm->edac_mode]); } /* show/store functions for DIMM Label attributes */ @@ -170,11 +176,11 @@ static ssize_t channel_dimm_label_show(struct csrow_info *csrow, char *data, int channel) { /* if field has not been initialized, there is nothing to send */ - if (!csrow->channels[channel].label[0]) + if (!csrow->channels[channel].dimm->label[0]) return 0; return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", - csrow->channels[channel].label); + csrow->channels[channel].dimm->label); } static ssize_t channel_dimm_label_store(struct csrow_info *csrow, @@ -184,8 +190,8 @@ static ssize_t channel_dimm_label_store(struct csrow_info *csrow, ssize_t max_size = 0; max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1); - strncpy(csrow->channels[channel].label, data, max_size); - csrow->channels[channel].label[max_size] = '\0'; + strncpy(csrow->channels[channel].dimm->label, data, max_size); + csrow->channels[channel].dimm->label[max_size] = '\0'; return max_size; } @@ -419,8 +425,8 @@ static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, mci->ue_noinfo_count = 0; mci->ce_noinfo_count = 0; - mci->ue_count = 0; - mci->ce_count = 0; + mci->ue_mc = 0; + mci->ce_mc = 0; for (row = 0; row < mci->nr_csrows; row++) { struct csrow_info *ri = &mci->csrows[row]; @@ -489,12 +495,12 @@ static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) /* default attribute files for the MCI object */ static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data) { - return sprintf(data, "%d\n", mci->ue_count); + return sprintf(data, "%d\n", mci->ue_mc); } static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data) { - return sprintf(data, "%d\n", mci->ce_count); + return sprintf(data, "%d\n", mci->ce_mc); } static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data) @@ -519,16 +525,16 @@ static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data) static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) { - int total_pages, csrow_idx; + int total_pages = 0, csrow_idx, j; - for (total_pages = csrow_idx = 0; csrow_idx < mci->nr_csrows; - csrow_idx++) { + for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) { struct csrow_info *csrow = &mci->csrows[csrow_idx]; - if (!csrow->nr_pages) - continue; + for (j = 0; j < csrow->nr_channels; j++) { + struct dimm_info *dimm = csrow->channels[j].dimm; - total_pages += csrow->nr_pages; + total_pages += dimm->nr_pages; + } } return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages)); @@ -900,7 +906,7 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, */ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) { - int i; + int i, j; int err; struct csrow_info *csrow; struct kobject *kobj_mci = &mci->edac_mci_kobj; @@ -934,10 +940,13 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) /* Make directories for each CSROW object under the mc<id> kobject */ for (i = 0; i < mci->nr_csrows; i++) { + int nr_pages = 0; + csrow = &mci->csrows[i]; + for (j = 0; j < csrow->nr_channels; j++) + nr_pages += csrow->channels[j].dimm->nr_pages; - /* Only expose populated CSROWs */ - if (csrow->nr_pages > 0) { + if (nr_pages > 0) { err = edac_create_csrow_object(mci, csrow, i); if (err) { debugf1("%s() failure: create csrow %d obj\n", @@ -949,12 +958,15 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) return 0; - /* CSROW error: backout what has already been registered, */ fail1: for (i--; i >= 0; i--) { - if (csrow->nr_pages > 0) { + int nr_pages = 0; + + csrow = &mci->csrows[i]; + for (j = 0; j < csrow->nr_channels; j++) + nr_pages += csrow->channels[j].dimm->nr_pages; + if (nr_pages > 0) kobject_put(&mci->csrows[i].kobj); - } } /* remove the mci instance's attributes, if any */ @@ -973,14 +985,20 @@ fail0: */ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) { - int i; + struct csrow_info *csrow; + int i, j; debugf0("%s()\n", __func__); /* remove all csrow kobjects */ debugf4("%s() unregister this mci kobj\n", __func__); for (i = 0; i < mci->nr_csrows; i++) { - if (mci->csrows[i].nr_pages > 0) { + int nr_pages = 0; + + csrow = &mci->csrows[i]; + for (j = 0; j < csrow->nr_channels; j++) + nr_pages += csrow->channels[j].dimm->nr_pages; + if (nr_pages > 0) { debugf0("%s() unreg csrow-%d\n", __func__, i); kobject_put(&mci->csrows[i].kobj); } |