diff options
Diffstat (limited to 'drivers/edac/edac_mc.c')
-rw-r--r-- | drivers/edac/edac_mc.c | 175 |
1 files changed, 170 insertions, 5 deletions
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 1b4fc9221803..7b622300d0e5 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -927,6 +927,57 @@ static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, return count; } +/* memory scrubbing */ +static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, + const char *data, size_t count) +{ + u32 bandwidth = -1; + + if (mci->set_sdram_scrub_rate) { + + memctrl_int_store(&bandwidth, data, count); + + if (!(*mci->set_sdram_scrub_rate)(mci, &bandwidth)) { + edac_printk(KERN_DEBUG, EDAC_MC, + "Scrub rate set successfully, applied: %d\n", + bandwidth); + } else { + /* FIXME: error codes maybe? */ + edac_printk(KERN_DEBUG, EDAC_MC, + "Scrub rate set FAILED, could not apply: %d\n", + bandwidth); + } + } else { + /* FIXME: produce "not implemented" ERROR for user-side. */ + edac_printk(KERN_WARNING, EDAC_MC, + "Memory scrubbing 'set'control is not implemented!\n"); + } + return count; +} + +static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) +{ + u32 bandwidth = -1; + + if (mci->get_sdram_scrub_rate) { + if (!(*mci->get_sdram_scrub_rate)(mci, &bandwidth)) { + edac_printk(KERN_DEBUG, EDAC_MC, + "Scrub rate successfully, fetched: %d\n", + bandwidth); + } else { + /* FIXME: error codes maybe? */ + edac_printk(KERN_DEBUG, EDAC_MC, + "Scrub rate fetch FAILED, got: %d\n", + bandwidth); + } + } else { + /* FIXME: produce "not implemented" ERROR for user-side. */ + edac_printk(KERN_WARNING, EDAC_MC, + "Memory scrubbing 'get' control is not implemented!\n"); + } + return sprintf(data, "%d\n", bandwidth); +} + /* default attribute files for the MCI object */ static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data) { @@ -1033,6 +1084,9 @@ MCIDEV_ATTR(ce_noinfo_count,S_IRUGO,mci_ce_noinfo_show,NULL); MCIDEV_ATTR(ue_count,S_IRUGO,mci_ue_count_show,NULL); MCIDEV_ATTR(ce_count,S_IRUGO,mci_ce_count_show,NULL); +/* memory scrubber attribute file */ +MCIDEV_ATTR(sdram_scrub_rate,S_IRUGO|S_IWUSR,mci_sdram_scrub_rate_show,mci_sdram_scrub_rate_store); + static struct mcidev_attribute *mci_attr[] = { &mci_attr_reset_counters, &mci_attr_mc_name, @@ -1042,6 +1096,7 @@ static struct mcidev_attribute *mci_attr[] = { &mci_attr_ce_noinfo_count, &mci_attr_ue_count, &mci_attr_ce_count, + &mci_attr_sdram_scrub_rate, NULL }; @@ -1442,11 +1497,11 @@ int edac_mc_add_mc(struct mem_ctl_info *mci, int mc_idx) /* set load time so that error rate can be tracked */ mci->start_time = jiffies; - if (edac_create_sysfs_mci_device(mci)) { - edac_mc_printk(mci, KERN_WARNING, + if (edac_create_sysfs_mci_device(mci)) { + edac_mc_printk(mci, KERN_WARNING, "failed to create sysfs device\n"); - goto fail1; - } + goto fail1; + } /* Report action taken */ edac_mc_printk(mci, KERN_INFO, "Giving out device to %s %s: DEV %s\n", @@ -1703,6 +1758,116 @@ void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg) EXPORT_SYMBOL_GPL(edac_mc_handle_ue_no_info); +/************************************************************* + * On Fully Buffered DIMM modules, this help function is + * called to process UE events + */ +void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci, + unsigned int csrow, + unsigned int channela, + unsigned int channelb, + char *msg) +{ + int len = EDAC_MC_LABEL_LEN * 4; + char labels[len + 1]; + char *pos = labels; + int chars; + + if (csrow >= mci->nr_csrows) { + /* something is wrong */ + edac_mc_printk(mci, KERN_ERR, + "INTERNAL ERROR: row out of range (%d >= %d)\n", + csrow, mci->nr_csrows); + edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR"); + return; + } + + if (channela >= mci->csrows[csrow].nr_channels) { + /* something is wrong */ + edac_mc_printk(mci, KERN_ERR, + "INTERNAL ERROR: channel-a out of range " + "(%d >= %d)\n", + channela, mci->csrows[csrow].nr_channels); + edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR"); + return; + } + + if (channelb >= mci->csrows[csrow].nr_channels) { + /* something is wrong */ + edac_mc_printk(mci, KERN_ERR, + "INTERNAL ERROR: channel-b out of range " + "(%d >= %d)\n", + channelb, mci->csrows[csrow].nr_channels); + edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR"); + return; + } + + mci->ue_count++; + mci->csrows[csrow].ue_count++; + + /* Generate the DIMM labels from the specified channels */ + chars = snprintf(pos, len + 1, "%s", + mci->csrows[csrow].channels[channela].label); + len -= chars; pos += chars; + chars = snprintf(pos, len + 1, "-%s", + mci->csrows[csrow].channels[channelb].label); + + if (log_ue) + edac_mc_printk(mci, KERN_EMERG, + "UE row %d, channel-a= %d channel-b= %d " + "labels \"%s\": %s\n", csrow, channela, channelb, + labels, msg); + + if (panic_on_ue) + panic("UE row %d, channel-a= %d channel-b= %d " + "labels \"%s\": %s\n", csrow, channela, + channelb, labels, msg); +} +EXPORT_SYMBOL(edac_mc_handle_fbd_ue); + +/************************************************************* + * On Fully Buffered DIMM modules, this help function is + * called to process CE events + */ +void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci, + unsigned int csrow, + unsigned int channel, + char *msg) +{ + + /* Ensure boundary values */ + if (csrow >= mci->nr_csrows) { + /* something is wrong */ + edac_mc_printk(mci, KERN_ERR, + "INTERNAL ERROR: row out of range (%d >= %d)\n", + csrow, mci->nr_csrows); + edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR"); + return; + } + if (channel >= mci->csrows[csrow].nr_channels) { + /* something is wrong */ + edac_mc_printk(mci, KERN_ERR, + "INTERNAL ERROR: channel out of range (%d >= %d)\n", + channel, mci->csrows[csrow].nr_channels); + edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR"); + return; + } + + if (log_ce) + /* FIXME - put in DIMM location */ + edac_mc_printk(mci, KERN_WARNING, + "CE row %d, channel %d, label \"%s\": %s\n", + csrow, channel, + mci->csrows[csrow].channels[channel].label, + msg); + + mci->ce_count++; + mci->csrows[csrow].ce_count++; + mci->csrows[csrow].channels[channel].ce_count++; +} +EXPORT_SYMBOL(edac_mc_handle_fbd_ce); + + /* * Iterate over all MC instances and check for ECC, et al, errors */ @@ -1806,7 +1971,7 @@ static void __exit edac_mc_exit(void) debugf0("%s()\n", __func__); kthread_stop(edac_thread); - /* tear down the sysfs device */ + /* tear down the sysfs device */ edac_sysfs_memctrl_teardown(); edac_sysfs_pci_teardown(); } |