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 + -
显示快捷键?