Release 4.12 drivers/edac/edac_mc.c
  
  
  
/*
 * edac_mc kernel module
 * (C) 2005, 2006 Linux Networx (http://lnxi.com)
 * This file may be distributed under the terms of the
 * GNU General Public License.
 *
 * Written by Thayne Harbaugh
 * Based on work by Dan Hollis <goemon at anime dot net> and others.
 *      http://www.anime.net/~goemon/linux-ecc/
 *
 * Modified by Dave Peterson and Doug Thompson
 *
 */
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/smp.h>
#include <linux/init.h>
#include <linux/sysctl.h>
#include <linux/highmem.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/ctype.h>
#include <linux/edac.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
#include <asm/page.h>
#include "edac_mc.h"
#include "edac_module.h"
#include <ras/ras_event.h>
#ifdef CONFIG_EDAC_ATOMIC_SCRUB
#include <asm/edac.h>
#else
#define edac_atomic_scrub(va, size) do { } while (0)
#endif
int edac_op_state = EDAC_OPSTATE_INVAL;
EXPORT_SYMBOL_GPL(edac_op_state);
static int edac_report = EDAC_REPORTING_ENABLED;
/* lock to memory controller's control array */
static DEFINE_MUTEX(mem_ctls_mutex);
static LIST_HEAD(mc_devices);
/*
 * Used to lock EDAC MC to just one module, avoiding two drivers e. g.
 *      apei/ghes and i7core_edac to be used at the same time.
 */
static void const *edac_mc_owner;
static struct bus_type mc_bus[EDAC_MAX_MCS];
int edac_get_report_status(void)
{
	return edac_report;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Borislav Petkov | 10 | 100.00% | 2 | 100.00% | 
| Total | 10 | 100.00% | 2 | 100.00% | 
EXPORT_SYMBOL_GPL(edac_get_report_status);
void edac_set_report_status(int new)
{
	if (new == EDAC_REPORTING_ENABLED ||
	    new == EDAC_REPORTING_DISABLED ||
	    new == EDAC_REPORTING_FORCE)
		edac_report = new;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Borislav Petkov | 26 | 100.00% | 2 | 100.00% | 
| Total | 26 | 100.00% | 2 | 100.00% | 
EXPORT_SYMBOL_GPL(edac_set_report_status);
static int edac_report_set(const char *str, const struct kernel_param *kp)
{
	if (!str)
		return -EINVAL;
	if (!strncmp(str, "on", 2))
		edac_report = EDAC_REPORTING_ENABLED;
	else if (!strncmp(str, "off", 3))
		edac_report = EDAC_REPORTING_DISABLED;
	else if (!strncmp(str, "force", 5))
		edac_report = EDAC_REPORTING_FORCE;
	return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Borislav Petkov | 79 | 100.00% | 1 | 100.00% | 
| Total | 79 | 100.00% | 1 | 100.00% | 
static int edac_report_get(char *buffer, const struct kernel_param *kp)
{
	int ret = 0;
	switch (edac_report) {
	case EDAC_REPORTING_ENABLED:
		ret = sprintf(buffer, "on");
		break;
	case EDAC_REPORTING_DISABLED:
		ret = sprintf(buffer, "off");
		break;
	case EDAC_REPORTING_FORCE:
		ret = sprintf(buffer, "force");
		break;
	default:
		ret = -EINVAL;
		break;
	}
	return ret;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Borislav Petkov | 76 | 100.00% | 1 | 100.00% | 
| Total | 76 | 100.00% | 1 | 100.00% | 
static const struct kernel_param_ops edac_report_ops = {
	.set = edac_report_set,
	.get = edac_report_get,
};
module_param_cb(edac_report, &edac_report_ops, &edac_report, 0644);
unsigned edac_dimm_info_location(struct dimm_info *dimm, char *buf,
			         unsigned len)
{
	struct mem_ctl_info *mci = dimm->mci;
	int i, n, count = 0;
	char *p = buf;
	for (i = 0; i < mci->n_layers; i++) {
		n = snprintf(p, len, "%s %d ",
			      edac_layer_name[mci->layers[i].type],
			      dimm->location[i]);
		p += n;
		len -= n;
		count += n;
		if (!len)
			break;
	}
	return count;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Mauro Carvalho Chehab | 109 | 100.00% | 1 | 100.00% | 
| Total | 109 | 100.00% | 1 | 100.00% | 
#ifdef CONFIG_EDAC_DEBUG
static void edac_mc_dump_channel(struct rank_info *chan)
{
	edac_dbg(4, "  channel->chan_idx = %d\n", chan->chan_idx);
	edac_dbg(4, "    channel = %p\n", chan);
	edac_dbg(4, "    channel->csrow = %p\n", chan->csrow);
	edac_dbg(4, "    channel->dimm = %p\n", chan->dimm);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Doug Thompson | 21 | 39.62% | 2 | 22.22% | 
| Mauro Carvalho Chehab | 18 | 33.96% | 5 | 55.56% | 
| Joe Perches | 12 | 22.64% | 1 | 11.11% | 
| Alan Cox | 2 | 3.77% | 1 | 11.11% | 
| Total | 53 | 100.00% | 9 | 100.00% | 
static void edac_mc_dump_dimm(struct dimm_info *dimm, int number)
{
	char location[80];
	edac_dimm_info_location(dimm, location, sizeof(location));
	edac_dbg(4, "%s%i: %smapped as virtual row %d, chan %d\n",
		 dimm->mci->csbased ? "rank" : "dimm",
		 number, location, dimm->csrow, dimm->cschannel);
	edac_dbg(4, "  dimm = %p\n", dimm);
	edac_dbg(4, "  dimm->label = '%s'\n", dimm->label);
	edac_dbg(4, "  dimm->nr_pages = 0x%x\n", dimm->nr_pages);
	edac_dbg(4, "  dimm->grain = %d\n", dimm->grain);
	edac_dbg(4, "  dimm->nr_pages = 0x%x\n", dimm->nr_pages);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Mauro Carvalho Chehab | 88 | 77.19% | 5 | 62.50% | 
| Joe Perches | 16 | 14.04% | 1 | 12.50% | 
| Doug Thompson | 9 | 7.89% | 1 | 12.50% | 
| Alan Cox | 1 | 0.88% | 1 | 12.50% | 
| Total | 114 | 100.00% | 8 | 100.00% | 
static void edac_mc_dump_csrow(struct csrow_info *csrow)
{
	edac_dbg(4, "csrow->csrow_idx = %d\n", csrow->csrow_idx);
	edac_dbg(4, "  csrow = %p\n", csrow);
	edac_dbg(4, "  csrow->first_page = 0x%lx\n", csrow->first_page);
	edac_dbg(4, "  csrow->last_page = 0x%lx\n", csrow->last_page);
	edac_dbg(4, "  csrow->page_mask = 0x%lx\n", csrow->page_mask);
	edac_dbg(4, "  csrow->nr_channels = %d\n", csrow->nr_channels);
	edac_dbg(4, "  csrow->channels = %p\n", csrow->channels);
	edac_dbg(4, "  csrow->mci = %p\n", csrow->mci);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Doug Thompson | 46 | 47.42% | 2 | 33.33% | 
| Joe Perches | 24 | 24.74% | 1 | 16.67% | 
| Dave Peterson | 15 | 15.46% | 1 | 16.67% | 
| Mauro Carvalho Chehab | 10 | 10.31% | 1 | 16.67% | 
| Alan Cox | 2 | 2.06% | 1 | 16.67% | 
| Total | 97 | 100.00% | 6 | 100.00% | 
static void edac_mc_dump_mci(struct mem_ctl_info *mci)
{
	edac_dbg(3, "\tmci = %p\n", mci);
	edac_dbg(3, "\tmci->mtype_cap = %lx\n", mci->mtype_cap);
	edac_dbg(3, "\tmci->edac_ctl_cap = %lx\n", mci->edac_ctl_cap);
	edac_dbg(3, "\tmci->edac_cap = %lx\n", mci->edac_cap);
	edac_dbg(4, "\tmci->edac_check = %p\n", mci->edac_check);
	edac_dbg(3, "\tmci->nr_csrows = %d, csrows = %p\n",
		 mci->nr_csrows, mci->csrows);
	edac_dbg(3, "\tmci->nr_dimms = %d, dimms = %p\n",
		 mci->tot_dimms, mci->dimms);
	edac_dbg(3, "\tdev = %p\n", mci->pdev);
	edac_dbg(3, "\tmod_name:ctl_name = %s:%s\n",
		 mci->mod_name, mci->ctl_name);
	edac_dbg(3, "\tpvt_info = %p\n\n", mci->pvt_info);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Doug Thompson | 39 | 29.77% | 1 | 14.29% | 
| Joe Perches | 30 | 22.90% | 1 | 14.29% | 
| Dave Peterson | 27 | 20.61% | 1 | 14.29% | 
| Alan Cox | 21 | 16.03% | 1 | 14.29% | 
| Mauro Carvalho Chehab | 13 | 9.92% | 2 | 28.57% | 
| Adrian Bunk | 1 | 0.76% | 1 | 14.29% | 
| Total | 131 | 100.00% | 7 | 100.00% | 
#endif				/* CONFIG_EDAC_DEBUG */
const char * const edac_mem_types[] = {
	[MEM_EMPTY]	= "Empty csrow",
	[MEM_RESERVED]	= "Reserved csrow type",
	[MEM_UNKNOWN]	= "Unknown csrow type",
	[MEM_FPM]	= "Fast page mode RAM",
	[MEM_EDO]	= "Extended data out RAM",
	[MEM_BEDO]	= "Burst Extended data out RAM",
	[MEM_SDR]	= "Single data rate SDRAM",
	[MEM_RDR]	= "Registered single data rate SDRAM",
	[MEM_DDR]	= "Double data rate SDRAM",
	[MEM_RDDR]	= "Registered Double data rate SDRAM",
	[MEM_RMBS]	= "Rambus DRAM",
	[MEM_DDR2]	= "Unbuffered DDR2 RAM",
	[MEM_FB_DDR2]	= "Fully buffered DDR2",
	[MEM_RDDR2]	= "Registered DDR2 RAM",
	[MEM_XDR]	= "Rambus XDR",
	[MEM_DDR3]	= "Unbuffered DDR3 RAM",
	[MEM_RDDR3]	= "Registered DDR3 RAM",
	[MEM_LRDDR3]	= "Load-Reduced DDR3 RAM",
	[MEM_DDR4]	= "Unbuffered DDR4 RAM",
	[MEM_RDDR4]	= "Registered DDR4 RAM",
};
EXPORT_SYMBOL_GPL(edac_mem_types);
/**
 * edac_align_ptr - Prepares the pointer offsets for a single-shot allocation
 * @p:          pointer to a pointer with the memory offset to be used. At
 *              return, this will be incremented to point to the next offset
 * @size:       Size of the data structure to be reserved
 * @n_elems:    Number of elements that should be reserved
 *
 * If 'size' is a constant, the compiler will optimize this whole function
 * down to either a no-op or the addition of a constant to the value of '*p'.
 *
 * The 'p' pointer is absolutely needed to keep the proper advancing
 * further in memory to the proper offsets when allocating the struct along
 * with its embedded structs, as edac_device_alloc_ctl_info() does it
 * above, for example.
 *
 * At return, the pointer 'p' will be incremented to be used on a next call
 * to this function.
 */
void *edac_align_ptr(void **p, unsigned size, int n_elems)
{
	unsigned align, r;
	void *ptr = *p;
	*p += size * n_elems;
	/*
         * 'p' can possibly be an unaligned item X such that sizeof(X) is
         * 'size'.  Adjust 'p' so that its alignment is at least as
         * stringent as what the compiler would provide for X and return
         * the aligned result.
         * Here we assume that the alignment of a "long long" is the most
         * stringent alignment that the compiler will ever provide by default.
         * As far as I know, this is a reasonable assumption.
         */
	if (size > sizeof(long))
		align = sizeof(long long);
	else if (size > sizeof(int))
		align = sizeof(long);
	else if (size > sizeof(short))
		align = sizeof(int);
	else if (size > sizeof(char))
		align = sizeof(short);
	else
		return (char *)ptr;
	r = (unsigned long)p % align;
	if (r == 0)
		return (char *)ptr;
	*p += align - r;
	return (void *)(((unsigned long)ptr) + align - r);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Doug Thompson | 104 | 64.20% | 3 | 42.86% | 
| Mauro Carvalho Chehab | 27 | 16.67% | 1 | 14.29% | 
| Alan Cox | 21 | 12.96% | 1 | 14.29% | 
| Chris Metcalf | 5 | 3.09% | 1 | 14.29% | 
| Dave Peterson | 5 | 3.09% | 1 | 14.29% | 
| Total | 162 | 100.00% | 7 | 100.00% | 
static void _edac_mc_free(struct mem_ctl_info *mci)
{
	int i, chn, row;
	struct csrow_info *csr;
	const unsigned int tot_dimms = mci->tot_dimms;
	const unsigned int tot_channels = mci->num_cschannel;
	const unsigned int tot_csrows = mci->nr_csrows;
	if (mci->dimms) {
		for (i = 0; i < tot_dimms; i++)
			kfree(mci->dimms[i]);
		kfree(mci->dimms);
	}
	if (mci->csrows) {
		for (row = 0; row < tot_csrows; row++) {
			csr = mci->csrows[row];
			if (csr) {
				if (csr->channels) {
					for (chn = 0; chn < tot_channels; chn++)
						kfree(csr->channels[chn]);
					kfree(csr->channels);
				}
				kfree(csr);
			}
		}
		kfree(mci->csrows);
	}
	kfree(mci);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Shaun Ruffell | 181 | 100.00% | 1 | 100.00% | 
| Total | 181 | 100.00% | 1 | 100.00% | 
struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
				   unsigned n_layers,
				   struct edac_mc_layer *layers,
				   unsigned sz_pvt)
{
	struct mem_ctl_info *mci;
	struct edac_mc_layer *layer;
	struct csrow_info *csr;
	struct rank_info *chan;
	struct dimm_info *dimm;
	u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS];
	unsigned pos[EDAC_MAX_LAYERS];
	unsigned size, tot_dimms = 1, count = 1;
	unsigned tot_csrows = 1, tot_channels = 1, tot_errcount = 0;
	void *pvt, *p, *ptr = NULL;
	int i, j, row, chn, n, len, off;
	bool per_rank = false;
	BUG_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0);
	/*
         * Calculate the total amount of dimms and csrows/cschannels while
         * in the old API emulation mode
         */
	for (i = 0; i < n_layers; i++) {
		tot_dimms *= layers[i].size;
		if (layers[i].is_virt_csrow)
			tot_csrows *= layers[i].size;
		else
			tot_channels *= layers[i].size;
		if (layers[i].type == EDAC_MC_LAYER_CHIP_SELECT)
			per_rank = true;
	}
	/* Figure out the offsets of the various items from the start of an mc
         * structure.  We want the alignment of each item to be at least as
         * stringent as what the compiler would provide if we could simply
         * hardcode everything into a single struct.
         */
	mci = edac_align_ptr(&ptr, sizeof(*mci), 1);
	layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers);
	for (i = 0; i < n_layers; i++) {
		count *= layers[i].size;
		edac_dbg(4, "errcount layer %d size %d\n", i, count);
		ce_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count);
		ue_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count);
		tot_errcount += 2 * count;
	}
	edac_dbg(4, "allocating %d error counters\n", tot_errcount);
	pvt = edac_align_ptr(&ptr, sz_pvt, 1);
	size = ((unsigned long)pvt) + sz_pvt;
	edac_dbg(1, "allocating %u bytes for mci data (%d %s, %d csrows/channels)\n",
		 size,
		 tot_dimms,
		 per_rank ? "ranks" : "dimms",
		 tot_csrows * tot_channels);
	mci = kzalloc(size, GFP_KERNEL);
	if (mci == NULL)
		return NULL;
	/* Adjust pointers so they point within the memory we just allocated
         * rather than an imaginary chunk of memory located at address 0.
         */
	layer = (struct edac_mc_layer *)(((char *)mci) + ((unsigned long)layer));
	for (i = 0; i < n_layers; i++) {
		mci->ce_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ce_per_layer[i]));
		mci->ue_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ue_per_layer[i]));
	}
	pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL;
	/* setup index and various internal pointers */
	mci->mc_idx = mc_num;
	mci->tot_dimms = tot_dimms;
	mci->pvt_info = pvt;
	mci->n_layers = n_layers;
	mci->layers = layer;
	memcpy(mci->layers, layers, sizeof(*layer) * n_layers);
	mci->nr_csrows = tot_csrows;
	mci->num_cschannel = tot_channels;
	mci->csbased = per_rank;
	/*
         * Alocate and fill the csrow/channels structs
         */
	mci->csrows = kcalloc(tot_csrows, sizeof(*mci->csrows), GFP_KERNEL);
	if (!mci->csrows)
		goto error;
	for (row = 0; row < tot_csrows; row++) {
		csr = kzalloc(sizeof(**mci->csrows), GFP_KERNEL);
		if (!csr)
			goto error;
		mci->csrows[row] = csr;
		csr->csrow_idx = row;
		csr->mci = mci;
		csr->nr_channels = tot_channels;
		csr->channels = kcalloc(tot_channels, sizeof(*csr->channels),
					GFP_KERNEL);
		if (!csr->channels)
			goto error;
		for (chn = 0; chn < tot_channels; chn++) {
			chan = kzalloc(sizeof(**csr->channels), GFP_KERNEL);
			if (!chan)
				goto error;
			csr->channels[chn] = chan;
			chan->chan_idx = chn;
			chan->csrow = csr;
		}
	}
	/*
         * Allocate and fill the dimm structs
         */
	mci->dimms  = kcalloc(tot_dimms, sizeof(*mci->dimms), GFP_KERNEL);
	if (!mci->dimms)
		goto error;
	memset(&pos, 0, sizeof(pos));
	row = 0;
	chn = 0;
	for (i = 0; i < tot_dimms; i++) {
		chan = mci->csrows[row]->channels[chn];
		off = EDAC_DIMM_OFF(layer, n_layers, pos[0], pos[1], pos[2]);
		if (off < 0 || off >= tot_dimms) {
			edac_mc_printk(mci, KERN_ERR, "EDAC core bug: EDAC_DIMM_OFF is trying to do an illegal data access\n");
			goto error;
		}
		dimm = kzalloc(sizeof(**mci->dimms), GFP_KERNEL);
		if (!dimm)
			goto error;
		mci->dimms[off] = dimm;
		dimm->mci = mci;
		/*
                 * Copy DIMM location and initialize it.
                 */
		len = sizeof(dimm->label);
		p = dimm->label;
		n = snprintf(p, len, "mc#%u", mc_num);
		p += n;
		len -= n;
		for (j = 0; j < n_layers; j++) {
			n = snprintf(p, len, "%s#%u",
				     edac_layer_name[layers[j].type],
				     pos[j]);
			p += n;
			len -= n;
			dimm->location[j] = pos[j];
			if (len <= 0)
				break;
		}
		/* Link it to the csrows old API data */
		chan->dimm = dimm;
		dimm->csrow = row;
		dimm->cschannel = chn;
		/* Increment csrow location */
		if (layers[0].is_virt_csrow) {
			chn++;
			if (chn == tot_channels) {
				chn = 0;
				row++;
			}
		} else {
			row++;
			if (row == tot_csrows) {
				row = 0;
				chn++;
			}
		}
		/* Increment dimm location */
		for (j = n_layers - 1; j >= 0; j--) {
			pos[j]++;
			if (pos[j] < layers[j].size)
				break;
			pos[j] = 0;
		}
	}
	mci->op_state = OP_ALLOC;
	return mci;
error:
	_edac_mc_free(mci);
	return NULL;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Mauro Carvalho Chehab | 946 | 81.20% | 10 | 47.62% | 
| Doug Thompson | 151 | 12.96% | 4 | 19.05% | 
| Alan Cox | 22 | 1.89% | 1 | 4.76% | 
| Dave Peterson | 19 | 1.63% | 1 | 4.76% | 
| Joe Perches | 12 | 1.03% | 2 | 9.52% | 
| Dan Carpenter | 8 | 0.69% | 1 | 4.76% | 
| Dave Jiang | 6 | 0.52% | 1 | 4.76% | 
| Shaun Ruffell | 1 | 0.09% | 1 | 4.76% | 
| Total | 1165 | 100.00% | 21 | 100.00% | 
EXPORT_SYMBOL_GPL(edac_mc_alloc);
void edac_mc_free(struct mem_ctl_info *mci)
{
	edac_dbg(1, "\n");
	/* If we're not yet registered with sysfs free only what was allocated
         * in edac_mc_alloc().
         */
	if (!device_is_registered(&mci->dev)) {
		_edac_mc_free(mci);
		return;
	}
	/* the mci instance is freed here, when the sysfs object is dropped */
	edac_unregister_sysfs(mci);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Mauro Carvalho Chehab | 21 | 48.84% | 4 | 66.67% | 
| Shaun Ruffell | 20 | 46.51% | 1 | 16.67% | 
| Joe Perches | 2 | 4.65% | 1 | 16.67% | 
| Total | 43 | 100.00% | 6 | 100.00% | 
EXPORT_SYMBOL_GPL(edac_mc_free);
bool edac_has_mcs(void)
{
	bool ret;
	mutex_lock(&mem_ctls_mutex);
	ret = list_empty(&mc_devices);
	mutex_unlock(&mem_ctls_mutex);
	return !ret;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Yazen Ghannam | 34 | 100.00% | 1 | 100.00% | 
| Total | 34 | 100.00% | 1 | 100.00% | 
EXPORT_SYMBOL_GPL(edac_has_mcs);
/* Caller must hold mem_ctls_mutex */
static struct mem_ctl_info *__find_mci_by_dev(struct device *dev)
{
	struct mem_ctl_info *mci;
	struct list_head *item;
	edac_dbg(3, "\n");
	list_for_each(item, &mc_devices) {
		mci = list_entry(item, struct mem_ctl_info, link);
		if (mci->pdev == dev)
			return mci;
	}
	return NULL;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Doug Thompson | 40 | 62.50% | 1 | 16.67% | 
| Alan Cox | 18 | 28.12% | 1 | 16.67% | 
| Borislav Petkov | 2 | 3.12% | 1 | 16.67% | 
| Mauro Carvalho Chehab | 2 | 3.12% | 2 | 33.33% | 
| Joe Perches | 2 | 3.12% | 1 | 16.67% | 
| Total | 64 | 100.00% | 6 | 100.00% | 
/**
 * 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
 */
struct mem_ctl_info *find_mci_by_dev(struct device *dev)
{
	struct mem_ctl_info *ret;
	mutex_lock(&mem_ctls_mutex);
	ret = __find_mci_by_dev(dev);
	mutex_unlock(&mem_ctls_mutex);
	return ret;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Borislav Petkov | 39 | 100.00% | 1 | 100.00% | 
| Total | 39 | 100.00% | 1 | 100.00% | 
EXPORT_SYMBOL_GPL(find_mci_by_dev);
/*
 * edac_mc_workq_function
 *      performs the operation scheduled by a workq request
 */
static void edac_mc_workq_function(struct work_struct *work_req)
{
	struct delayed_work *d_work = to_delayed_work(work_req);
	struct mem_ctl_info *mci = to_edac_mem_ctl_work(d_work);
	mutex_lock(&mem_ctls_mutex);
	if (mci->op_state != OP_RUNNING_POLL) {
		mutex_unlock(&mem_ctls_mutex);
		return;
	}
	if (edac_op_state == EDAC_OPSTATE_POLL)
		mci->edac_check(mci);
	mutex_unlock(&mem_ctls_mutex);
	/* Queue ourselves again. */
	edac_queue_work(&mci->work, msecs_to_jiffies(edac_mc_get_poll_msec()));
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Dave Jiang | 63 | 71.59% | 2 | 28.57% | 
| Doug Thompson | 15 | 17.05% | 1 | 14.29% | 
| Borislav Petkov | 7 | 7.95% | 3 | 42.86% | 
| Jean Delvare | 3 | 3.41% | 1 | 14.29% | 
| Total | 88 | 100.00% | 7 | 100.00% | 
/*
 * edac_mc_reset_delay_period(unsigned long value)
 *
 *      user space has updated our poll period value, need to
 *      reset our workq delays
 */
void edac_mc_reset_delay_period(unsigned long value)
{
	struct mem_ctl_info *mci;
	struct list_head *item;
	mutex_lock(&mem_ctls_mutex);
	list_for_each(item, &mc_devices) {
		mci = list_entry(item, struct mem_ctl_info, link);
		if (mci->op_state == OP_RUNNING_POLL)
			edac_mod_work(&mci->work, value);
	}
	mutex_unlock(&mem_ctls_mutex);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Doug Thompson | 36 | 52.17% | 1 | 20.00% | 
| Dave Jiang | 19 | 27.54% | 1 | 20.00% | 
| Nicholas Krause | 8 | 11.59% | 1 | 20.00% | 
| Borislav Petkov | 6 | 8.70% | 2 | 40.00% | 
| Total | 69 | 100.00% | 5 | 100.00% | 
/* Return 0 on success, 1 on failure.
 * Before calling this function, caller must
 * assign a unique value to mci->mc_idx.
 *
 *      locking model:
 *
 *              called with the mem_ctls_mutex lock held
 */
static int add_mc_to_global_list(struct mem_ctl_info *mci)
{
	struct list_head *item, *insert_before;
	struct mem_ctl_info *p;
	insert_before = &mc_devices;
	p = __find_mci_by_dev(mci->pdev);
	if (unlikely(p != NULL))
		goto fail0;
	list_for_each(item, &mc_devices) {
		p = list_entry(item, struct mem_ctl_info, link);
		if (p->mc_idx >= mci->mc_idx) {
			if (unlikely(p->mc_idx == mci->mc_idx))
				goto fail1;
			insert_before = item;
			break;
		}
	}
	list_add_tail_rcu(&mci->link, insert_before);
	return 0;
fail0:
	edac_printk(KERN_WARNING, EDAC_MC,
		"%s (%s) %s %s already assigned %d\n", dev_name(p->pdev),
		edac_dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx);
	return 1;
fail1:
	edac_printk(KERN_WARNING, EDAC_MC,
		"bug in low-level driver: attempt to assign\n"
		"    duplicate mc_idx %d in %s()\n", p->mc_idx, __func__);
	return 1;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Doug Thompson | 140 | 80.00% | 3 | 30.00% | 
| Alan Cox | 26 | 14.86% | 1 | 10.00% | 
| Kay Sievers | 3 | 1.71% | 1 | 10.00% | 
| Mauro Carvalho Chehab | 2 | 1.14% | 1 | 10.00% | 
| Dave Jiang | 1 | 0.57% | 1 | 10.00% | 
| Stephen Rothwell | 1 | 0.57% | 1 | 10.00% | 
| Borislav Petkov | 1 | 0.57% | 1 | 10.00% | 
| Dave Peterson | 1 | 0.57% | 1 | 10.00% | 
| Total | 175 | 100.00% | 10 | 100.00% | 
static int del_mc_from_global_list(struct mem_ctl_info *mci)
{
	list_del_rcu(&mci->link);
	/* these are for safe removal of devices from global list while
         * NMI handlers may be traversing list
         */
	synchronize_rcu();
	INIT_LIST_HEAD(&mci->link);
	return list_empty(&mc_devices);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Doug Thompson | 17 | 44.74% | 1 | 20.00% | 
| Alan Cox | 7 | 18.42% | 1 | 20.00% | 
| Lai Jiangshan | 6 | 15.79% | 1 | 20.00% | 
| Borislav Petkov | 5 | 13.16% | 1 | 20.00% | 
| Mauro Carvalho Chehab | 3 | 7.89% | 1 | 20.00% | 
| Total | 38 | 100.00% | 5 | 100.00% | 
struct mem_ctl_info *edac_mc_find(int idx)
{
	struct mem_ctl_info *mci = NULL;
	struct list_head *item;
	mutex_lock(&mem_ctls_mutex);
	list_for_each(item, &mc_devices) {
		mci = list_entry(item, struct mem_ctl_info, link);
		if (mci->mc_idx >= idx) {
			if (mci->mc_idx == idx) {
				goto unlock;
			}
			break;
		}
	}
unlock:
	mutex_unlock(&mem_ctls_mutex);
	return mci;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Doug Thompson | 57 | 68.67% | 1 | 50.00% | 
| Borislav Petkov | 26 | 31.33% | 1 | 50.00% | 
| Total | 83 | 100.00% | 2 | 100.00% | 
EXPORT_SYMBOL(edac_mc_find);
/* FIXME - should a warning be printed if no error detection? correction? */
int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci,
			       const struct attribute_group **groups)
{
	int ret = -EINVAL;
	edac_dbg(0, "\n");
	if (mci->mc_idx >= EDAC_MAX_MCS) {
		pr_warn_once("Too many memory controllers: %d\n", mci->mc_idx);
		return -ENODEV;
	}
#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++) {
			struct csrow_info *csrow = mci->csrows[i];
			u32 nr_pages = 0;
			int j;
			for (j = 0; j < csrow->nr_channels; j++)
				nr_pages += csrow->channels[j]->dimm->nr_pages;
			if (!nr_pages)
				continue;
			edac_mc_dump_csrow(csrow);
			for (j = 0; j < csrow->nr_channels; j++)
				if (csrow->channels[j]->dimm->nr_pages)
					edac_mc_dump_channel(csrow->channels[j]);
		}
		for (i = 0; i < mci->tot_dimms; i++)
			if (mci->dimms[i]->nr_pages)
				edac_mc_dump_dimm(mci->dimms[i], i);
	}
#endif
	mutex_lock(&mem_ctls_mutex);
	if (edac_mc_owner && edac_mc_owner != mci->mod_name) {
		ret = -EPERM;
		goto fail0;
	}
	if (add_mc_to_global_list(mci))
		goto fail0;
	/* set load time so that error rate can be tracked */
	mci->start_time = jiffies;
	mci->bus = &mc_bus[mci->mc_idx];
	if (edac_create_sysfs_mci_device(mci, groups)) {
		edac_mc_printk(mci, KERN_WARNING,
			"failed to create sysfs device\n");
		goto fail1;
	}
	if (mci->edac_check) {
		mci->op_state = OP_RUNNING_POLL;
		INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
		edac_queue_work(&mci->work, msecs_to_jiffies(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 module %s controller %s: DEV %s (%s)\n",
		mci->mod_name, mci->ctl_name, mci->dev_name,
		edac_op_state_to_string(mci->op_state));
	edac_mc_owner = mci->mod_name;
	mutex_unlock(&mem_ctls_mutex);
	return 0;
fail1:
	del_mc_from_global_list(mci);
fail0:
	mutex_unlock(&mem_ctls_mutex);
	return ret;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Mauro Carvalho Chehab | 138 | 32.94% | 5 | 33.33% | 
| Doug Thompson | 111 | 26.49% | 1 | 6.67% | 
| Alan Cox | 64 | 15.27% | 1 | 6.67% | 
| Borislav Petkov | 52 | 12.41% | 2 | 13.33% | 
| Dave Jiang | 30 | 7.16% | 1 | 6.67% | 
| Takashi Iwai | 10 | 2.39% | 1 | 6.67% | 
| Robert Richter | 8 | 1.91% | 1 | 6.67% | 
| Matthias Kaehlcke | 3 | 0.72% | 1 | 6.67% | 
| Joe Perches | 2 | 0.48% | 1 | 6.67% | 
| Dave Peterson | 1 | 0.24% | 1 | 6.67% | 
| Total | 419 | 100.00% | 15 | 100.00% | 
EXPORT_SYMBOL_GPL(edac_mc_add_mc_with_groups);
struct mem_ctl_info *edac_mc_del_mc(struct device *dev)
{
	struct mem_ctl_info *mci;
	edac_dbg(0, "\n");
	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;
	}
	/* mark MCI offline: */
	mci->op_state = OP_OFFLINE;
	if (del_mc_from_global_list(mci))
		edac_mc_owner = NULL;
	mutex_unlock(&mem_ctls_mutex);
	if (mci->edac_check)
		edac_stop_work(&mci->work);
	/* remove from sysfs */
	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, edac_dev_name(mci));
	return mci;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Doug Thompson | 71 | 55.47% | 2 | 14.29% | 
| Alan Cox | 20 | 15.62% | 1 | 7.14% | 
| Borislav Petkov | 19 | 14.84% | 4 | 28.57% | 
| Mauro Carvalho Chehab | 7 | 5.47% | 2 | 14.29% | 
| Dave Jiang | 4 | 3.12% | 1 | 7.14% | 
| Dave Peterson | 2 | 1.56% | 1 | 7.14% | 
| Joe Perches | 2 | 1.56% | 1 | 7.14% | 
| Matthias Kaehlcke | 2 | 1.56% | 1 | 7.14% | 
| Stephen Rothwell | 1 | 0.78% | 1 | 7.14% | 
| Total | 128 | 100.00% | 14 | 100.00% | 
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;
	edac_dbg(3, "\n");
	/* 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);
	/* Perform architecture specific atomic scrub operation */
	edac_atomic_scrub(virt_addr + offset, size);
	/* Unmap and complete */
	kunmap_atomic(virt_addr);
	if (PageHighMem(pg))
		local_irq_restore(flags);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Doug Thompson | 78 | 75.00% | 2 | 28.57% | 
| Alan Cox | 21 | 20.19% | 1 | 14.29% | 
| Joe Perches | 2 | 1.92% | 1 | 14.29% | 
| Mauro Carvalho Chehab | 1 | 0.96% | 1 | 14.29% | 
| Borislav Petkov | 1 | 0.96% | 1 | 14.29% | 
| Adrian Bunk | 1 | 0.96% | 1 | 14.29% | 
| Total | 104 | 100.00% | 7 | 100.00% | 
/* 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, j, n;
	edac_dbg(1, "MC%d: 0x%lx\n", mci->mc_idx, page);
	row = -1;
	for (i = 0; i < mci->nr_csrows; i++) {
		struct csrow_info *csrow = csrows[i];
		n = 0;
		for (j = 0; j < csrow->nr_channels; j++) {
			struct dimm_info *dimm = csrow->channels[j]->dimm;
			n += dimm->nr_pages;
		}
		if (n == 0)
			continue;
		edac_dbg(3, "MC%d: first(0x%lx) page(0x%lx) last(0x%lx) mask(0x%lx)\n",
			 mci->mc_idx,
			 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;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Doug Thompson | 138 | 62.44% | 2 | 25.00% | 
| Mauro Carvalho Chehab | 47 | 21.27% | 3 | 37.50% | 
| Alan Cox | 27 | 12.22% | 1 | 12.50% | 
| Joe Perches | 7 | 3.17% | 1 | 12.50% | 
| Dave Peterson | 2 | 0.90% | 1 | 12.50% | 
| Total | 221 | 100.00% | 8 | 100.00% | 
EXPORT_SYMBOL_GPL(edac_mc_find_csrow_by_page);
const char *edac_layer_name[] = {
	[EDAC_MC_LAYER_BRANCH] = "branch",
	[EDAC_MC_LAYER_CHANNEL] = "channel",
	[EDAC_MC_LAYER_SLOT] = "slot",
	[EDAC_MC_LAYER_CHIP_SELECT] = "csrow",
	[EDAC_MC_LAYER_ALL_MEM] = "memory",
};
EXPORT_SYMBOL_GPL(edac_layer_name);
static void edac_inc_ce_error(struct mem_ctl_info *mci,
			      bool enable_per_layer_report,
			      const int pos[EDAC_MAX_LAYERS],
			      const u16 count)
{
	int i, index = 0;
	mci->ce_mc += count;
	if (!enable_per_layer_report) {
		mci->ce_noinfo_count += count;
		return;
	}
	for (i = 0; i < mci->n_layers; i++) {
		if (pos[i] < 0)
			break;
		index += pos[i];
		mci->ce_per_layer[i][index] += count;
		if (i < mci->n_layers - 1)
			index *= mci->layers[i + 1].size;
	}
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Mauro Carvalho Chehab | 88 | 72.73% | 5 | 62.50% | 
| Doug Thompson | 24 | 19.83% | 2 | 25.00% | 
| Alan Cox | 9 | 7.44% | 1 | 12.50% | 
| Total | 121 | 100.00% | 8 | 100.00% | 
static void edac_inc_ue_error(struct mem_ctl_info *mci,
				    bool enable_per_layer_report,
				    const int pos[EDAC_MAX_LAYERS],
				    const u16 count)
{
	int i, index = 0;
	mci->ue_mc += count;
	if (!enable_per_layer_report) {
		mci->ue_noinfo_count += count;
		return;
	}
	for (i = 0; i < mci->n_layers; i++) {
		if (pos[i] < 0)
			break;
		index += pos[i];
		mci->ue_per_layer[i][index] += count;
		if (i < mci->n_layers - 1)
			index *= mci->layers[i + 1].size;
	}
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Mauro Carvalho Chehab | 96 | 79.34% | 4 | 50.00% | 
| Doug Thompson | 20 | 16.53% | 2 | 25.00% | 
| Alan Cox | 4 | 3.31% | 1 | 12.50% | 
| Emmanouil Maroudas | 1 | 0.83% | 1 | 12.50% | 
| Total | 121 | 100.00% | 8 | 100.00% | 
static void edac_ce_error(struct mem_ctl_info *mci,
			  const u16 error_count,
			  const int pos[EDAC_MAX_LAYERS],
			  const char *msg,
			  const char *location,
			  const char *label,
			  const char *detail,
			  const char *other_detail,
			  const bool enable_per_layer_report,
			  const unsigned long page_frame_number,
			  const unsigned long offset_in_page,
			  long grain)
{
	unsigned long remapped_page;
	char *msg_aux = "";
	if (*msg)
		msg_aux = " ";
	if (edac_mc_get_log_ce()) {
		if (other_detail && *other_detail)
			edac_mc_printk(mci, KERN_WARNING,
				       "%d CE %s%son %s (%s %s - %s)\n",
				       error_count, msg, msg_aux, label,
				       location, detail, other_detail);
		else
			edac_mc_printk(mci, KERN_WARNING,
				       "%d CE %s%son %s (%s %s)\n",
				       error_count, msg, msg_aux, label,
				       location, detail);
	}
	edac_inc_ce_error(mci, enable_per_layer_report, pos, error_count);
	if (mci->scrub_mode == SCRUB_SW_SRC) {
		/*
                        * Some memory controllers (called MCs below) 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-dependent
                        * 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, grain);
	}
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Mauro Carvalho Chehab | 135 | 71.05% | 3 | 33.33% | 
| Borislav Petkov | 21 | 11.05% | 1 | 11.11% | 
| Doug Thompson | 20 | 10.53% | 2 | 22.22% | 
| Alan Cox | 12 | 6.32% | 1 | 11.11% | 
| Dave Jiang | 1 | 0.53% | 1 | 11.11% | 
| Loc Ho | 1 | 0.53% | 1 | 11.11% | 
| Total | 190 | 100.00% | 9 | 100.00% | 
static void edac_ue_error(struct mem_ctl_info *mci,
			  const u16 error_count,
			  const int pos[EDAC_MAX_LAYERS],
			  const char *msg,
			  const char *location,
			  const char *label,
			  const char *detail,
			  const char *other_detail,
			  const bool enable_per_layer_report)
{
	char *msg_aux = "";
	if (*msg)
		msg_aux = " ";
	if (edac_mc_get_log_ue()) {
		if (other_detail && *other_detail)
			edac_mc_printk(mci, KERN_WARNING,
				       "%d UE %s%son %s (%s %s - %s)\n",
				       error_count, msg, msg_aux, label,
				       location, detail, other_detail);
		else
			edac_mc_printk(mci, KERN_WARNING,
				       "%d UE %s%son %s (%s %s)\n",
				       error_count, msg, msg_aux, label,
				       location, detail);
	}
	if (edac_mc_get_panic_on_ue()) {
		if (other_detail && *other_detail)
			panic("UE %s%son %s (%s%s - %s)\n",
			      msg, msg_aux, label, location, detail, other_detail);
		else
			panic("UE %s%son %s (%s%s)\n",
			      msg, msg_aux, label, location, detail);
	}
	edac_inc_ue_error(mci, enable_per_layer_report, pos, error_count);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Mauro Carvalho Chehab | 116 | 63.39% | 3 | 50.00% | 
| Eric Wollesen | 38 | 20.77% | 1 | 16.67% | 
| Borislav Petkov | 27 | 14.75% | 1 | 16.67% | 
| Doug Thompson | 2 | 1.09% | 1 | 16.67% | 
| Total | 183 | 100.00% | 6 | 100.00% | 
void edac_raw_mc_handle_error(const enum hw_event_mc_err_type type,
			      struct mem_ctl_info *mci,
			      struct edac_raw_error_desc *e)
{
	char detail[80];
	int pos[EDAC_MAX_LAYERS] = { e->top_layer, e->mid_layer, e->low_layer };
	/* Memory type dependent details about the error */
	if (type == HW_EVENT_ERR_CORRECTED) {
		snprintf(detail, sizeof(detail),
			"page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx",
			e->page_frame_number, e->offset_in_page,
			e->grain, e->syndrome);
		edac_ce_error(mci, e->error_count, pos, e->msg, e->location, e->label,
			      detail, e->other_detail, e->enable_per_layer_report,
			      e->page_frame_number, e->offset_in_page, e->grain);
	} else {
		snprintf(detail, sizeof(detail),
			"page:0x%lx offset:0x%lx grain:%ld",
			e->page_frame_number, e->offset_in_page, e->grain);
		edac_ue_error(mci, e->error_count, pos, e->msg, e->location, e->label,
			      detail, e->other_detail, e->enable_per_layer_report);
	}
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Mauro Carvalho Chehab | 188 | 100.00% | 1 | 100.00% | 
| Total | 188 | 100.00% | 1 | 100.00% | 
EXPORT_SYMBOL_GPL(edac_raw_mc_handle_error);
void edac_mc_handle_error(const enum hw_event_mc_err_type type,
			  struct mem_ctl_info *mci,
			  const u16 error_count,
			  const unsigned long page_frame_number,
			  const unsigned long offset_in_page,
			  const unsigned long syndrome,
			  const int top_layer,
			  const int mid_layer,
			  const int low_layer,
			  const char *msg,
			  const char *other_detail)
{
	char *p;
	int row = -1, chan = -1;
	int pos[EDAC_MAX_LAYERS] = { top_layer, mid_layer, low_layer };
	int i, n_labels = 0;
	u8 grain_bits;
	struct edac_raw_error_desc *e = &mci->error_desc;
	edac_dbg(3, "MC%d\n", mci->mc_idx);
	/* Fills the error report buffer */
	memset(e, 0, sizeof (*e));
	e->error_count = error_count;
	e->top_layer = top_layer;
	e->mid_layer = mid_layer;
	e->low_layer = low_layer;
	e->page_frame_number = page_frame_number;
	e->offset_in_page = offset_in_page;
	e->syndrome = syndrome;
	e->msg = msg;
	e->other_detail = other_detail;
	/*
         * Check if the event report is consistent and if the memory
         * location is known. If it is known, enable_per_layer_report will be
         * true, the DIMM(s) label info will be filled and the per-layer
         * error counters will be incremented.
         */
	for (i = 0; i < mci->n_layers; i++) {
		if (pos[i] >= (int)mci->layers[i].size) {
			edac_mc_printk(mci, KERN_ERR,
				       "INTERNAL ERROR: %s value is out of range (%d >= %d)\n",
				       edac_layer_name[mci->layers[i].type],
				       pos[i], mci->layers[i].size);
			/*
                         * Instead of just returning it, let's use what's
                         * known about the error. The increment routines and
                         * the DIMM filter logic will do the right thing by
                         * pointing the likely damaged DIMMs.
                         */
			pos[i] = -1;
		}
		if (pos[i] >= 0)
			e->enable_per_layer_report = true;
	}
	/*
         * Get the dimm label/grain that applies to the match criteria.
         * As the error algorithm may not be able to point to just one memory
         * stick, the logic here will get all possible labels that could
         * pottentially be affected by the error.
         * On FB-DIMM memory controllers, for uncorrected errors, it is common
         * to have only the MC channel and the MC dimm (also called "branch")
         * but the channel is not known, as the memory is arranged in pairs,
         * where each memory belongs to a separate channel within the same
         * branch.
         */
	p = e->label;
	*p = '\0';
	for (i = 0; i < mci->tot_dimms; i++) {
		struct dimm_info *dimm = mci->dimms[i];
		if (top_layer >= 0 && top_layer != dimm->location[0])
			continue;
		if (mid_layer >= 0 && mid_layer != dimm->location[1])
			continue;
		if (low_layer >= 0 && low_layer != dimm->location[2])
			continue;
		/* get the max grain, over the error match range */
		if (dimm->grain > e->grain)
			e->grain = dimm->grain;
		/*
                 * If the error is memory-controller wide, there's no need to
                 * seek for the affected DIMMs because the whole
                 * channel/memory controller/...  may be affected.
                 * Also, don't show errors for empty DIMM slots.
                 */
		if (e->enable_per_layer_report && dimm->nr_pages) {
			if (n_labels >= EDAC_MAX_LABELS) {
				e->enable_per_layer_report = false;
				break;
			}
			n_labels++;
			if (p != e->label) {
				strcpy(p, OTHER_LABEL);
				p += strlen(OTHER_LABEL);
			}
			strcpy(p, dimm->label);
			p += strlen(p);
			*p = '\0';
			/*
                         * get csrow/channel of the DIMM, in order to allow
                         * incrementing the compat API counters
                         */
			edac_dbg(4, "%s csrows map: (%d,%d)\n",
				 mci->csbased ? "rank" : "dimm",
				 dimm->csrow, dimm->cschannel);
			if (row == -1)
				row = dimm->csrow;
			else if (row >= 0 && row != dimm->csrow)
				row = -2;
			if (chan == -1)
				chan = dimm->cschannel;
			else if (chan >= 0 && chan != dimm->cschannel)
				chan = -2;
		}
	}
	if (!e->enable_per_layer_report) {
		strcpy(e->label, "any memory");
	} else {
		edac_dbg(4, "csrow/channel to increment: (%d,%d)\n", row, chan);
		if (p == e->label)
			strcpy(e->label, "unknown memory");
		if (type == HW_EVENT_ERR_CORRECTED) {
			if (row >= 0) {
				mci->csrows[row]->ce_count += error_count;
				if (chan >= 0)
					mci->csrows[row]->channels[chan]->ce_count += error_count;
			}
		} else
			if (row >= 0)
				mci->csrows[row]->ue_count += error_count;
	}
	/* Fill the RAM location data */
	p = e->location;
	for (i = 0; i < mci->n_layers; i++) {
		if (pos[i] < 0)
			continue;
		p += sprintf(p, "%s:%d ",
			     edac_layer_name[mci->layers[i].type],
			     pos[i]);
	}
	if (p > e->location)
		*(p - 1) = '\0';
	/* Report the error via the trace interface */
	grain_bits = fls_long(e->grain) + 1;
	if (IS_ENABLED(CONFIG_RAS))
		trace_mc_event(type, e->msg, e->label, e->error_count,
			       mci->mc_idx, e->top_layer, e->mid_layer,
			       e->low_layer,
			       (e->page_frame_number << PAGE_SHIFT) | e->offset_in_page,
			       grain_bits, e->syndrome, e->other_detail);
	edac_raw_mc_handle_error(type, mci, e);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Mauro Carvalho Chehab | 670 | 80.92% | 10 | 58.82% | 
| Doug Thompson | 69 | 8.33% | 1 | 5.88% | 
| Eric Wollesen | 40 | 4.83% | 1 | 5.88% | 
| Alan Cox | 30 | 3.62% | 1 | 5.88% | 
| Joe Perches | 7 | 0.85% | 1 | 5.88% | 
| Borislav Petkov | 7 | 0.85% | 1 | 5.88% | 
| Tan Xiaojun | 4 | 0.48% | 1 | 5.88% | 
| Dave Peterson | 1 | 0.12% | 1 | 5.88% | 
| Total | 828 | 100.00% | 17 | 100.00% | 
EXPORT_SYMBOL_GPL(edac_mc_handle_error);
Overall Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Mauro Carvalho Chehab | 2804 | 48.78% | 23 | 26.44% | 
| Doug Thompson | 1237 | 21.52% | 11 | 12.64% | 
| Borislav Petkov | 621 | 10.80% | 20 | 22.99% | 
| Alan Cox | 366 | 6.37% | 1 | 1.15% | 
| Shaun Ruffell | 202 | 3.51% | 1 | 1.15% | 
| Dave Jiang | 128 | 2.23% | 4 | 4.60% | 
| Joe Perches | 118 | 2.05% | 2 | 2.30% | 
| Eric Wollesen | 78 | 1.36% | 1 | 1.15% | 
| Dave Peterson | 78 | 1.36% | 6 | 6.90% | 
| Yazen Ghannam | 39 | 0.68% | 1 | 1.15% | 
| Takashi Iwai | 11 | 0.19% | 1 | 1.15% | 
| Nicholas Krause | 8 | 0.14% | 1 | 1.15% | 
| Dan Carpenter | 8 | 0.14% | 1 | 1.15% | 
| Robert Richter | 8 | 0.14% | 1 | 1.15% | 
| Aravind Gopalakrishnan | 6 | 0.10% | 1 | 1.15% | 
| Lai Jiangshan | 6 | 0.10% | 1 | 1.15% | 
| Matthias Kaehlcke | 6 | 0.10% | 1 | 1.15% | 
| Chris Metcalf | 5 | 0.09% | 1 | 1.15% | 
| Tan Xiaojun | 4 | 0.07% | 1 | 1.15% | 
| Kay Sievers | 3 | 0.05% | 1 | 1.15% | 
| Jean Delvare | 3 | 0.05% | 1 | 1.15% | 
| Stephen Rothwell | 2 | 0.03% | 1 | 1.15% | 
| Robert P. J. Day | 2 | 0.03% | 1 | 1.15% | 
| Adrian Bunk | 2 | 0.03% | 1 | 1.15% | 
| Emmanouil Maroudas | 1 | 0.02% | 1 | 1.15% | 
| Linus Torvalds | 1 | 0.02% | 1 | 1.15% | 
| Loc Ho | 1 | 0.02% | 1 | 1.15% | 
| Total | 5748 | 100.00% | 87 | 100.00% | 
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.