edac_mc.c
来自「linux 内核源代码」· C语言 代码 · 共 910 行 · 第 1/2 页
C
910 行
return NULL;}EXPORT_SYMBOL(edac_mc_find);/** * edac_mc_add_mc: Insert the 'mci' structure into the mci global list and * create sysfs entries associated with mci structure * @mci: pointer to the mci structure to be added to the list * @mc_idx: A unique numeric identifier to be assigned to the 'mci' structure. * * Return: * 0 Success * !0 Failure *//* FIXME - should a warning be printed if no error detection? correction? */int edac_mc_add_mc(struct mem_ctl_info *mci){ debugf0("%s()\n", __func__);#ifdef CONFIG_EDAC_DEBUG if (edac_debug_level >= 3) edac_mc_dump_mci(mci); if (edac_debug_level >= 4) { int i; for (i = 0; i < mci->nr_csrows; i++) { int j; edac_mc_dump_csrow(&mci->csrows[i]); for (j = 0; j < mci->csrows[i].nr_channels; j++) edac_mc_dump_channel(&mci->csrows[i]. channels[j]); } }#endif mutex_lock(&mem_ctls_mutex); if (add_mc_to_global_list(mci)) goto fail0; /* 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, "failed to create sysfs device\n"); goto fail1; } /* If there IS a check routine, then we are running POLLED */ if (mci->edac_check != NULL) { /* This instance is NOW RUNNING */ mci->op_state = OP_RUNNING_POLL; edac_mc_workq_setup(mci, edac_mc_get_poll_msec()); } else { mci->op_state = OP_RUNNING_INTERRUPT; } /* Report action taken */ edac_mc_printk(mci, KERN_INFO, "Giving out device to '%s' '%s':" " DEV %s\n", mci->mod_name, mci->ctl_name, dev_name(mci)); mutex_unlock(&mem_ctls_mutex); return 0;fail1: del_mc_from_global_list(mci);fail0: mutex_unlock(&mem_ctls_mutex); return 1;}EXPORT_SYMBOL_GPL(edac_mc_add_mc);/** * edac_mc_del_mc: Remove sysfs entries for specified mci structure and * remove mci structure from global list * @pdev: Pointer to 'struct device' representing mci structure to remove. * * Return pointer to removed mci structure, or NULL if device not found. */struct mem_ctl_info *edac_mc_del_mc(struct device *dev){ struct mem_ctl_info *mci; debugf0("%s()\n", __func__); mutex_lock(&mem_ctls_mutex); /* find the requested mci struct in the global list */ mci = find_mci_by_dev(dev); if (mci == NULL) { mutex_unlock(&mem_ctls_mutex); return NULL; } /* marking MCI offline */ mci->op_state = OP_OFFLINE; del_mc_from_global_list(mci); mutex_unlock(&mem_ctls_mutex); /* flush workq processes and remove sysfs */ edac_mc_workq_teardown(mci); edac_remove_sysfs_mci_device(mci); edac_printk(KERN_INFO, EDAC_MC, "Removed device %d for %s %s: DEV %s\n", mci->mc_idx, mci->mod_name, mci->ctl_name, dev_name(mci)); return mci;}EXPORT_SYMBOL_GPL(edac_mc_del_mc);static void edac_mc_scrub_block(unsigned long page, unsigned long offset, u32 size){ struct page *pg; void *virt_addr; unsigned long flags = 0; debugf3("%s()\n", __func__); /* ECC error page was not in our memory. Ignore it. */ if (!pfn_valid(page)) return; /* Find the actual page structure then map it and fix */ pg = pfn_to_page(page); if (PageHighMem(pg)) local_irq_save(flags); virt_addr = kmap_atomic(pg, KM_BOUNCE_READ); /* Perform architecture specific atomic scrub operation */ atomic_scrub(virt_addr + offset, size); /* Unmap and complete */ kunmap_atomic(virt_addr, KM_BOUNCE_READ); if (PageHighMem(pg)) local_irq_restore(flags);}/* FIXME - should return -1 */int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page){ struct csrow_info *csrows = mci->csrows; int row, i; debugf1("MC%d: %s(): 0x%lx\n", mci->mc_idx, __func__, page); row = -1; for (i = 0; i < mci->nr_csrows; i++) { struct csrow_info *csrow = &csrows[i]; if (csrow->nr_pages == 0) continue; debugf3("MC%d: %s(): first(0x%lx) page(0x%lx) last(0x%lx) " "mask(0x%lx)\n", mci->mc_idx, __func__, csrow->first_page, page, csrow->last_page, csrow->page_mask); if ((page >= csrow->first_page) && (page <= csrow->last_page) && ((page & csrow->page_mask) == (csrow->first_page & csrow->page_mask))) { row = i; break; } } if (row == -1) edac_mc_printk(mci, KERN_ERR, "could not look up page error address %lx\n", (unsigned long)page); return row;}EXPORT_SYMBOL_GPL(edac_mc_find_csrow_by_page);/* FIXME - setable log (warning/emerg) levels *//* FIXME - integrate with evlog: http://evlog.sourceforge.net/ */void edac_mc_handle_ce(struct mem_ctl_info *mci, unsigned long page_frame_number, unsigned long offset_in_page, unsigned long syndrome, int row, int channel, const char *msg){ unsigned long remapped_page; debugf3("MC%d: %s()\n", mci->mc_idx, __func__); /* FIXME - maybe make panic on INTERNAL ERROR an option */ if (row >= mci->nr_csrows || row < 0) { /* something is wrong */ edac_mc_printk(mci, KERN_ERR, "INTERNAL ERROR: row out of range " "(%d >= %d)\n", row, mci->nr_csrows); edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR"); return; } if (channel >= mci->csrows[row].nr_channels || channel < 0) { /* something is wrong */ edac_mc_printk(mci, KERN_ERR, "INTERNAL ERROR: channel out of range " "(%d >= %d)\n", channel, mci->csrows[row].nr_channels); edac_mc_handle_ce_no_info(mci, "INTERNAL ERROR"); return; } if (edac_mc_get_log_ce()) /* FIXME - put in DIMM location */ edac_mc_printk(mci, KERN_WARNING, "CE page 0x%lx, offset 0x%lx, grain %d, syndrome " "0x%lx, row %d, channel %d, label \"%s\": %s\n", page_frame_number, offset_in_page, mci->csrows[row].grain, syndrome, row, channel, mci->csrows[row].channels[channel].label, msg); mci->ce_count++; mci->csrows[row].ce_count++; mci->csrows[row].channels[channel].ce_count++; if (mci->scrub_mode & SCRUB_SW_SRC) { /* * Some MC's can remap memory so that it is still available * at a different address when PCI devices map into memory. * MC's that can't do this lose the memory where PCI devices * are mapped. This mapping is MC dependant and so we call * back into the MC driver for it to map the MC page to * a physical (CPU) page which can then be mapped to a virtual * page - which can then be scrubbed. */ remapped_page = mci->ctl_page_to_phys ? mci->ctl_page_to_phys(mci, page_frame_number) : page_frame_number; edac_mc_scrub_block(remapped_page, offset_in_page, mci->csrows[row].grain); }}EXPORT_SYMBOL_GPL(edac_mc_handle_ce);void edac_mc_handle_ce_no_info(struct mem_ctl_info *mci, const char *msg){ if (edac_mc_get_log_ce()) edac_mc_printk(mci, KERN_WARNING, "CE - no information available: %s\n", msg); mci->ce_noinfo_count++; mci->ce_count++;}EXPORT_SYMBOL_GPL(edac_mc_handle_ce_no_info);void edac_mc_handle_ue(struct mem_ctl_info *mci, unsigned long page_frame_number, unsigned long offset_in_page, int row, const char *msg){ int len = EDAC_MC_LABEL_LEN * 4; char labels[len + 1]; char *pos = labels; int chan; int chars; debugf3("MC%d: %s()\n", mci->mc_idx, __func__); /* FIXME - maybe make panic on INTERNAL ERROR an option */ if (row >= mci->nr_csrows || row < 0) { /* something is wrong */ edac_mc_printk(mci, KERN_ERR, "INTERNAL ERROR: row out of range " "(%d >= %d)\n", row, mci->nr_csrows); edac_mc_handle_ue_no_info(mci, "INTERNAL ERROR"); return; } chars = snprintf(pos, len + 1, "%s", mci->csrows[row].channels[0].label); len -= chars; pos += chars; for (chan = 1; (chan < mci->csrows[row].nr_channels) && (len > 0); chan++) { chars = snprintf(pos, len + 1, ":%s", mci->csrows[row].channels[chan].label); len -= chars; pos += chars; } if (edac_mc_get_log_ue()) edac_mc_printk(mci, KERN_EMERG, "UE page 0x%lx, offset 0x%lx, grain %d, row %d, " "labels \"%s\": %s\n", page_frame_number, offset_in_page, mci->csrows[row].grain, row, labels, msg); if (edac_mc_get_panic_on_ue()) panic("EDAC MC%d: UE page 0x%lx, offset 0x%lx, grain %d, " "row %d, labels \"%s\": %s\n", mci->mc_idx, page_frame_number, offset_in_page, mci->csrows[row].grain, row, labels, msg); mci->ue_count++; mci->csrows[row].ue_count++;}EXPORT_SYMBOL_GPL(edac_mc_handle_ue);void edac_mc_handle_ue_no_info(struct mem_ctl_info *mci, const char *msg){ if (edac_mc_get_panic_on_ue()) panic("EDAC MC%d: Uncorrected Error", mci->mc_idx); if (edac_mc_get_log_ue()) edac_mc_printk(mci, KERN_WARNING, "UE - no information available: %s\n", msg); mci->ue_noinfo_count++; mci->ue_count++;}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 (edac_mc_get_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 (edac_mc_get_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 (edac_mc_get_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 */void edac_check_mc_devices(void){ struct list_head *item; struct mem_ctl_info *mci; debugf3("%s()\n", __func__); mutex_lock(&mem_ctls_mutex); list_for_each(item, &mc_devices) { mci = list_entry(item, struct mem_ctl_info, link); if (mci->edac_check != NULL) mci->edac_check(mci); } mutex_unlock(&mem_ctls_mutex);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?