public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] Add a per-dimm structure
@ 2012-03-07 11:40 Mauro Carvalho Chehab
  2012-03-07 11:40 ` [PATCH 1/6] edac: Create a dimm struct and move the labels into it Mauro Carvalho Chehab
                   ` (7 more replies)
  0 siblings, 8 replies; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-07 11:40 UTC (permalink / raw)
  Cc: Mauro Carvalho Chehab, Linux Edac Mailing List,
	Linux Kernel Mailing List

Prepare the internal structures to represent the memory properties per dimm, 
instead of per csrow. 

This is needed for modern controllers with more than 2 channels, as the memories 
at the same slot number but on different channels (or channel pairs) may be 
different.

Mauro Carvalho Chehab (6):
  edac: Create a dimm struct and move the labels into it
  edac: Add per dimm's sysfs nodes
  edac: move dimm properties to struct memset_info
  edac: Don't initialize csrow's first_page & friends when not needed
  edac: move nr_pages to dimm struct
  edac: Add per-dimm sysfs show nodes

 drivers/edac/amd64_edac.c      |   73 +++--------
 drivers/edac/amd76x_edac.c     |   14 ++-
 drivers/edac/cell_edac.c       |   18 ++-
 drivers/edac/cpc925_edac.c     |   70 ++++++-----
 drivers/edac/e752x_edac.c      |   48 ++++---
 drivers/edac/e7xxx_edac.c      |   49 ++++---
 drivers/edac/edac_mc.c         |   97 ++++++++++-----
 drivers/edac/edac_mc_sysfs.c   |  269 +++++++++++++++++++++++++++++++++++++---
 drivers/edac/i3000_edac.c      |   24 ++--
 drivers/edac/i3200_edac.c      |   24 ++--
 drivers/edac/i5000_edac.c      |   31 ++---
 drivers/edac/i5100_edac.c      |   56 ++++++---
 drivers/edac/i5400_edac.c      |   38 ++----
 drivers/edac/i7300_edac.c      |   40 +++----
 drivers/edac/i7core_edac.c     |   41 +++----
 drivers/edac/i82443bxgx_edac.c |   15 ++-
 drivers/edac/i82860_edac.c     |   13 ++-
 drivers/edac/i82875p_edac.c    |   22 +++-
 drivers/edac/i82975x_edac.c    |   28 +++--
 drivers/edac/mpc85xx_edac.c    |   16 ++-
 drivers/edac/mv64x60_edac.c    |   22 ++--
 drivers/edac/pasemi_edac.c     |   24 ++--
 drivers/edac/ppc4xx_edac.c     |   25 ++--
 drivers/edac/r82600_edac.c     |   13 ++-
 drivers/edac/sb_edac.c         |   39 +++---
 drivers/edac/tile_edac.c       |   17 ++--
 drivers/edac/x38_edac.c        |   24 ++--
 include/linux/edac.h           |  139 +++++++++++++++------
 28 files changed, 808 insertions(+), 481 deletions(-)

-- 
1.7.8


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 1/6] edac: Create a dimm struct and move the labels into it
  2012-03-07 11:40 [PATCH 0/6] Add a per-dimm structure Mauro Carvalho Chehab
@ 2012-03-07 11:40 ` Mauro Carvalho Chehab
  2012-03-07 11:40 ` [PATCH 2/6] edac: Add per dimm's sysfs nodes Mauro Carvalho Chehab
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-07 11:40 UTC (permalink / raw)
  Cc: Mauro Carvalho Chehab, Linux Edac Mailing List,
	Linux Kernel Mailing List

The way a DIMM is currently represented implies that they're
linked into a per-csrow struct. However, some drivers don't see
csrows, as they're ridden behind some chip like the AMB's
on FBDIMM's, for example.

This forced drivers to fake a csrow struct, and to create
a mess under csrow/channel original's concept.

Move the DIMM labels into a per-DIMM struct, and add there
the real location of the socket, in terms of csrow/channel.
Latter patches will modify the location to properly represent the
memory architecture.

All other drivers will use a per-csrow type of location.
Some of those drivers will require a latter conversion, as
they also fake the csrows internally.

TODO: While this patch doesn't change the existing behavior, on
csrows-based memory controllers, a csrow/channel pair points to a memory
rank. There's a known bug at the EDAC core that allows having different
labels for the same DIMM, if it has more than one rank. A latter patch
is need to merge the several ranks for a DIMM into the same dimm_info
struct, in order to avoid having different labels for the same DIMM.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
 drivers/edac/edac_mc.c       |   75 ++++++++++++++++++++++++++++++-----------
 drivers/edac/edac_mc_sysfs.c |   16 +++++---
 drivers/edac/i5100_edac.c    |    8 ++--
 drivers/edac/i7core_edac.c   |    4 +-
 drivers/edac/i82975x_edac.c  |    2 +-
 include/linux/edac.h         |   28 +++++++++++++---
 6 files changed, 95 insertions(+), 38 deletions(-)

diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 7188e60..c3b5cff 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -45,7 +45,8 @@ static void edac_mc_dump_channel(struct rank_info *chan)
 	debugf4("\tchannel = %p\n", chan);
 	debugf4("\tchannel->chan_idx = %d\n", chan->chan_idx);
 	debugf4("\tchannel->ce_count = %d\n", chan->ce_count);
-	debugf4("\tchannel->label = '%s'\n", chan->label);
+	if (chan->dimm)
+		debugf4("\tchannel->label = '%s'\n", chan->dimm->label);
 	debugf4("\tchannel->csrow = %p\n\n", chan->csrow);
 }
 
@@ -158,6 +159,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
 	struct mem_ctl_info *mci;
 	struct csrow_info *csi, *csrow;
 	struct rank_info *chi, *chp, *chan;
+	struct dimm_info *dimm;
 	void *pvt;
 	unsigned size;
 	int row, chn;
@@ -171,7 +173,8 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
 	mci = (struct mem_ctl_info *)0;
 	csi = edac_align_ptr(&mci[1], sizeof(*csi));
 	chi = edac_align_ptr(&csi[nr_csrows], sizeof(*chi));
-	pvt = edac_align_ptr(&chi[nr_chans * nr_csrows], sz_pvt);
+	dimm = edac_align_ptr(&chi[nr_chans * nr_csrows], sizeof(*dimm));
+	pvt = edac_align_ptr(&dimm[nr_chans * nr_csrows], sz_pvt);
 	size = ((unsigned long)pvt) + sz_pvt;
 
 	mci = kzalloc(size, GFP_KERNEL);
@@ -181,13 +184,14 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
 	/* Adjust pointers so they point within the memory we just allocated
 	 * rather than an imaginary chunk of memory located at address 0.
 	 */
-	csi = (struct csrow_info *)(((char *)mci) + ((unsigned long)csi));
 	chi = (struct rank_info *)(((char *)mci) + ((unsigned long)chi));
+	dimm = (struct dimm_info *)(((char *)mci) + ((unsigned long)dimm));
 	pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL;
 
 	/* setup index and various internal pointers */
 	mci->mc_idx = edac_index;
 	mci->csrows = csi;
+	mci->dimms  = dimm;
 	mci->pvt_info = pvt;
 	mci->nr_csrows = nr_csrows;
 
@@ -206,6 +210,21 @@ struct mem_ctl_info *edac_mc_alloc(unsigned sz_pvt, unsigned nr_csrows,
 		}
 	}
 
+	/*
+	 * By default, assumes that a per-csrow arrangement will be used,
+	 * as most drivers are based on such assumption.
+	 */
+	dimm = mci->dimms;
+	for (row = 0; row < mci->nr_csrows; row++) {
+		for (chn = 0; chn < mci->csrows[row].nr_channels; chn++) {
+			mci->csrows[row].channels[chn].dimm = dimm;
+			dimm->csrow = row;
+			dimm->csrow_channel = chn;
+			dimm++;
+			mci->nr_dimms++;
+		}
+	}
+
 	mci->op_state = OP_ALLOC;
 	INIT_LIST_HEAD(&mci->grp_kobj_list);
 
@@ -679,6 +698,7 @@ void edac_mc_handle_ce(struct mem_ctl_info *mci,
 		int row, int channel, const char *msg)
 {
 	unsigned long remapped_page;
+	char *label = NULL;
 
 	debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
 
@@ -702,6 +722,9 @@ void edac_mc_handle_ce(struct mem_ctl_info *mci,
 		return;
 	}
 
+	if (mci->csrows[row].channels[channel].dimm)
+		label = mci->csrows[row].channels[channel].dimm->label;
+
 	if (edac_mc_get_log_ce())
 		/* FIXME - put in DIMM location */
 		edac_mc_printk(mci, KERN_WARNING,
@@ -709,7 +732,7 @@ void edac_mc_handle_ce(struct mem_ctl_info *mci,
 			"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);
+			label, msg);
 
 	mci->ce_count++;
 	mci->csrows[row].ce_count++;
@@ -755,6 +778,7 @@ void edac_mc_handle_ue(struct mem_ctl_info *mci,
 	char *pos = labels;
 	int chan;
 	int chars;
+	char *label = NULL;
 
 	debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
 
@@ -768,17 +792,21 @@ void edac_mc_handle_ue(struct mem_ctl_info *mci,
 		return;
 	}
 
-	chars = snprintf(pos, len + 1, "%s",
-			 mci->csrows[row].channels[0].label);
-	len -= chars;
-	pos += chars;
+	if (mci->csrows[row].channels[0].dimm) {
+		label = mci->csrows[row].channels[0].dimm->label;
+		chars = snprintf(pos, len + 1, "%s", 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 (mci->csrows[row].channels[chan].dimm) {
+			label = mci->csrows[row].channels[chan].dimm->label;
+			chars = snprintf(pos, len + 1, ":%s", label);
+			len -= chars;
+			pos += chars;
+		}
 	}
 
 	if (edac_mc_get_log_ue())
@@ -825,6 +853,7 @@ void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci,
 	char labels[len + 1];
 	char *pos = labels;
 	int chars;
+	char *label;
 
 	if (csrow >= mci->nr_csrows) {
 		/* something is wrong */
@@ -859,12 +888,15 @@ void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci,
 	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 (mci->csrows[csrow].channels[channela].dimm) {
+		label = mci->csrows[csrow].channels[channela].dimm->label;
+		chars = snprintf(pos, len + 1, "%s", label);
+		len -= chars;
+		pos += chars;
+	}
+	if (mci->csrows[csrow].channels[channela].dimm)
+		chars = snprintf(pos, len + 1, "-%s",
+				mci->csrows[csrow].channels[channelb].dimm->label);
 
 	if (edac_mc_get_log_ue())
 		edac_mc_printk(mci, KERN_EMERG,
@@ -886,6 +918,7 @@ EXPORT_SYMBOL(edac_mc_handle_fbd_ue);
 void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci,
 			unsigned int csrow, unsigned int channel, char *msg)
 {
+	char *label = NULL;
 
 	/* Ensure boundary values */
 	if (csrow >= mci->nr_csrows) {
@@ -905,12 +938,14 @@ void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci,
 		return;
 	}
 
+	if (mci->csrows[csrow].channels[channel].dimm)
+		label = mci->csrows[csrow].channels[channel].dimm->label;
+
 	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);
+			csrow, channel, label, msg);
 
 	mci->ce_count++;
 	mci->csrows[csrow].ce_count++;
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index 29ffa35..f99a83d 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -170,11 +170,13 @@ 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)
+		return 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,
@@ -183,9 +185,12 @@ static ssize_t channel_dimm_label_store(struct csrow_info *csrow,
 {
 	ssize_t max_size = 0;
 
+	if (!csrow->channels[channel].dimm)
+		return -EINVAL;
+
 	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;
 }
@@ -952,9 +957,8 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
 	/* CSROW error: backout what has already been registered,  */
 fail1:
 	for (i--; i >= 0; i--) {
-		if (csrow->nr_pages > 0) {
+		if (mci->csrows[i].nr_pages > 0)
 			kobject_put(&mci->csrows[i].kobj);
-		}
 	}
 
 	/* remove the mci instance's attributes, if any */
diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c
index bcbdeec..fd931f4 100644
--- a/drivers/edac/i5100_edac.c
+++ b/drivers/edac/i5100_edac.c
@@ -433,7 +433,7 @@ static void i5100_handle_ce(struct mem_ctl_info *mci,
 		"CE chan %d, bank %u, rank %u, syndrome 0x%lx, "
 		"cas %u, ras %u, csrow %u, label \"%s\": %s\n",
 		chan, bank, rank, syndrome, cas, ras,
-		csrow, mci->csrows[csrow].channels[0].label, msg);
+		csrow, mci->csrows[csrow].channels[0].dimm->label, msg);
 
 	mci->ce_count++;
 	mci->csrows[csrow].ce_count++;
@@ -455,7 +455,7 @@ static void i5100_handle_ue(struct mem_ctl_info *mci,
 		"UE chan %d, bank %u, rank %u, syndrome 0x%lx, "
 		"cas %u, ras %u, csrow %u, label \"%s\": %s\n",
 		chan, bank, rank, syndrome, cas, ras,
-		csrow, mci->csrows[csrow].channels[0].label, msg);
+		csrow, mci->csrows[csrow].channels[0].dimm->label, msg);
 
 	mci->ue_count++;
 	mci->csrows[csrow].ue_count++;
@@ -871,8 +871,8 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
 		mci->csrows[i].channels[0].chan_idx = 0;
 		mci->csrows[i].channels[0].ce_count = 0;
 		mci->csrows[i].channels[0].csrow = mci->csrows + i;
-		snprintf(mci->csrows[i].channels[0].label,
-			 sizeof(mci->csrows[i].channels[0].label),
+		snprintf(mci->csrows[i].channels[0].dimm->label,
+			 sizeof(mci->csrows[i].channels[0].dimm->label),
 			 "DIMM%u", i5100_rank_to_slot(mci, chan, rank));
 
 		total_pages += npages;
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index 70ad892..9a262c7 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -746,8 +746,8 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
 
 			csr->edac_mode = mode;
 			csr->mtype = mtype;
-			snprintf(csr->channels[0].label,
-					sizeof(csr->channels[0].label),
+			snprintf(csr->channels[0].dimm->label,
+					sizeof(csr->channels[0].dimm->label),
 					"CPU#%uChannel#%u_DIMM#%u",
 					pvt->i7core_dev->socket, i, j);
 
diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c
index a5da732..d12e571 100644
--- a/drivers/edac/i82975x_edac.c
+++ b/drivers/edac/i82975x_edac.c
@@ -399,7 +399,7 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
 		 *   [0-3] for dual-channel; i.e. csrow->nr_channels = 2
 		 */
 		for (chan = 0; chan < csrow->nr_channels; chan++)
-			strncpy(csrow->channels[chan].label,
+			strncpy(csrow->channels[chan].dimm->label,
 					labels[(index >> 1) + (chan * 2)],
 					EDAC_MC_LABEL_LEN);
 
diff --git a/include/linux/edac.h b/include/linux/edac.h
index b14e33d..9289654 100644
--- a/include/linux/edac.h
+++ b/include/linux/edac.h
@@ -335,23 +335,34 @@ enum scrub_type {
  * PS - I enjoyed writing all that about as much as you enjoyed reading it.
  */
 
+/* FIXME: add a per-dimm ce error count */
+struct dimm_info {
+	char label[EDAC_MC_LABEL_LEN + 1];	/* DIMM label on motherboard */
+	unsigned memory_controller;
+	unsigned csrow;
+	unsigned csrow_channel;
+};
+
 /**
  * struct rank_info - contains the information for one DIMM rank
  *
  * @chan_idx:	channel number where the rank is (typically, 0 or 1)
  * @ce_count:	number of correctable errors for this rank
- * @label:	DIMM label. Different ranks for the same DIMM should be
- *		filled, on userspace, with the same label.
- *		FIXME: The core currently won't enforce it.
  * @csrow:	A pointer to the chip select row structure (the parent
  *		structure). The location of the rank is given by
  *		the (csrow->csrow_idx, chan_idx) vector.
+ * @dimm:	A pointer to the DIMM structure, where the DIMM label
+ *		information is stored.
+ *
+ * FIXME: Currently, the EDAC core model will assume one DIMM per rank.
+ *	  This is a bad assumption, but it makes this patch easier. Later
+ *	  patches in this series will fix this issue.
  */
 struct rank_info {
 	int chan_idx;
 	u32 ce_count;
-	char label[EDAC_MC_LABEL_LEN + 1];
-	struct csrow_info *csrow;	/* the parent */
+	struct csrow_info *csrow;
+	struct dimm_info *dimm;
 };
 
 struct csrow_info {
@@ -451,6 +462,13 @@ struct mem_ctl_info {
 	int mc_idx;
 	int nr_csrows;
 	struct csrow_info *csrows;
+
+	/*
+	 * DIMM info. Will eventually remove the entire csrows_info some day
+	 */
+	unsigned nr_dimms;
+	struct dimm_info *dimms;
+
 	/*
 	 * FIXME - what about controllers on other busses? - IDs must be
 	 * unique.  dev pointer should be sufficiently unique, but
-- 
1.7.8


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 2/6] edac: Add per dimm's sysfs nodes
  2012-03-07 11:40 [PATCH 0/6] Add a per-dimm structure Mauro Carvalho Chehab
  2012-03-07 11:40 ` [PATCH 1/6] edac: Create a dimm struct and move the labels into it Mauro Carvalho Chehab
@ 2012-03-07 11:40 ` Mauro Carvalho Chehab
  2012-03-07 11:40 ` [PATCH 3/6] edac: move dimm properties to struct memset_info Mauro Carvalho Chehab
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-07 11:40 UTC (permalink / raw)
  Cc: Mauro Carvalho Chehab, Linux Edac Mailing List,
	Linux Kernel Mailing List

Instead of just exporting a per-csrow dimm directories, add
a pure per-dimm attributes. This will help to better map
the DIMM properties, when csrow info is not available.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
 drivers/edac/edac_mc_sysfs.c |  167 +++++++++++++++++++++++++++++++++++++++++-
 include/linux/edac.h         |    2 +
 2 files changed, 168 insertions(+), 1 deletions(-)

diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index f99a83d..f7e469b 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -415,6 +415,151 @@ err_out:
 	return err;
 }
 
+/* dimm specific attribute structure */
+struct dimmdev_attribute {
+	struct attribute attr;
+	 ssize_t(*show) (struct dimm_info *, char *);
+	 ssize_t(*store) (struct dimm_info *, const char *, size_t);
+};
+
+#define DIMMDEV_ATTR(_name, _mode, _show, _store)		\
+static struct dimmdev_attribute attr_##_name = {		\
+	.attr = {.name = __stringify(_name), .mode = _mode },	\
+	.show   = _show,					\
+	.store  = _store,					\
+};
+
+#define to_dimm(k) container_of(k, struct dimm_info, kobj)
+#define to_dimmdev_attr(a) container_of(a, struct dimmdev_attribute, attr)
+
+/* Set of show/store higher level functions for default dimm attributes */
+static ssize_t dimmdev_show(struct kobject *kobj,
+			struct attribute *attr, char *buffer)
+{
+	struct dimm_info *dimm = to_dimm(kobj);
+	struct dimmdev_attribute *dimmdev_attr = to_dimmdev_attr(attr);
+
+	if (dimmdev_attr->show)
+		return dimmdev_attr->show(dimm, buffer);
+	return -EIO;
+}
+
+static ssize_t dimmdev_store(struct kobject *kobj, struct attribute *attr,
+			const char *buffer, size_t count)
+{
+	struct dimm_info *dimm = to_dimm(kobj);
+	struct dimmdev_attribute *dimmdev_attr = to_dimmdev_attr(attr);
+
+	if (dimmdev_attr->store)
+		return dimmdev_attr->store(dimm,
+					buffer,
+					count);
+	return -EIO;
+}
+
+static const struct sysfs_ops dimmfs_ops = {
+	.show = dimmdev_show,
+	.store = dimmdev_store
+};
+
+/* show/store functions for DIMM Label attributes */
+static ssize_t dimmdev_location_show(struct dimm_info *dimm, char *data)
+{
+	return sprintf(data, "csrow %d, channel %d\n",
+		       dimm->csrow,
+		       dimm->csrow_channel);
+}
+
+static ssize_t dimmdev_label_show(struct dimm_info *dimm, char *data)
+{
+	/* if field has not been initialized, there is nothing to send */
+	if (!dimm->label[0])
+		return 0;
+
+	return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", dimm->label);
+}
+
+static ssize_t dimmdev_label_store(struct dimm_info *dimm,
+					const char *data,
+					size_t count)
+{
+	ssize_t max_size = 0;
+
+	max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1);
+	strncpy(dimm->label, data, max_size);
+	dimm->label[max_size] = '\0';
+
+	return max_size;
+}
+
+/* default cwrow<id>/attribute files */
+DIMMDEV_ATTR(label, S_IRUGO | S_IWUSR, dimmdev_label_show, dimmdev_label_store);
+DIMMDEV_ATTR(location, S_IRUGO, dimmdev_location_show, NULL);
+
+/* default attributes of the DIMM<id> object */
+static struct dimmdev_attribute *default_dimm_attr[] = {
+	&attr_label,
+	&attr_location,
+	NULL,
+};
+
+/* No memory to release for this kobj */
+static void edac_dimm_instance_release(struct kobject *kobj)
+{
+	struct mem_ctl_info *mci;
+	struct dimm_info *cs;
+
+	debugf1("%s()\n", __func__);
+
+	cs = container_of(kobj, struct dimm_info, kobj);
+	mci = cs->mci;
+
+	kobject_put(&mci->edac_mci_kobj);
+}
+
+/* the kobj_type instance for a DIMM */
+static struct kobj_type ktype_dimm = {
+	.release = edac_dimm_instance_release,
+	.sysfs_ops = &dimmfs_ops,
+	.default_attrs = (struct attribute **)default_dimm_attr,
+};
+/* Create a CSROW object under specifed edac_mc_device */
+static int edac_create_dimm_object(struct mem_ctl_info *mci,
+					struct dimm_info *dimm, int index)
+{
+	struct kobject *kobj_mci = &mci->edac_mci_kobj;
+	struct kobject *kobj;
+	int err;
+
+	/* generate ..../edac/mc/mc<id>/dimm<index>   */
+	memset(&dimm->kobj, 0, sizeof(dimm->kobj));
+	dimm->mci = mci;	/* include container up link */
+
+	/* bump the mci instance's kobject's ref count */
+	kobj = kobject_get(&mci->edac_mci_kobj);
+	if (!kobj) {
+		err = -ENODEV;
+		goto err_out;
+	}
+
+	/* Instanstiate the dimm object */
+	err = kobject_init_and_add(&dimm->kobj, &ktype_dimm, kobj_mci,
+				   "dimm%d", index);
+	if (err)
+		goto err_release_top_kobj;
+
+	kobject_uevent(&dimm->kobj, KOBJ_ADD);
+	return 0;
+
+	/* error unwind stack */
+err_release_top_kobj:
+	kobject_put(&mci->edac_mci_kobj);
+
+err_out:
+	return err;
+}
+
+
 /* default sysfs methods and data structures for the main MCI kobject */
 
 static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci,
@@ -905,7 +1050,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;
@@ -952,8 +1097,24 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
 		}
 	}
 
+	/*
+	 * Make directories for each DIMM object under the mc<id> kobject
+	 */
+	for (j = 0; j < mci->nr_dimms; j++) {
+		err = edac_create_dimm_object(mci, &mci->dimms[j] , j);
+		if (err) {
+			debugf1("%s() failure: create dimm %d obj\n",
+				__func__, j);
+			goto fail2;
+		}
+	}
+
 	return 0;
 
+fail2:
+	for (j--; j >= 0; j--)
+		kobject_put(&mci->dimms[i].kobj);
+
 	/* CSROW error: backout what has already been registered,  */
 fail1:
 	for (i--; i >= 0; i--) {
@@ -983,6 +1144,10 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
 
 	/* remove all csrow kobjects */
 	debugf4("%s()  unregister this mci kobj\n", __func__);
+	for (i = 0; i < mci->nr_dimms; i++) {
+		debugf0("%s()  unreg dimm-%d\n", __func__, i);
+		kobject_put(&mci->dimms[i].kobj);
+	}
 	for (i = 0; i < mci->nr_csrows; i++) {
 		if (mci->csrows[i].nr_pages > 0) {
 			debugf0("%s()  unreg csrow-%d\n", __func__, i);
diff --git a/include/linux/edac.h b/include/linux/edac.h
index 9289654..296fc7b 100644
--- a/include/linux/edac.h
+++ b/include/linux/edac.h
@@ -341,6 +341,8 @@ struct dimm_info {
 	unsigned memory_controller;
 	unsigned csrow;
 	unsigned csrow_channel;
+	struct kobject kobj;		/* sysfs kobject for this csrow */
+	struct mem_ctl_info *mci;	/* the parent */
 };
 
 /**
-- 
1.7.8


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 3/6] edac: move dimm properties to struct memset_info
  2012-03-07 11:40 [PATCH 0/6] Add a per-dimm structure Mauro Carvalho Chehab
  2012-03-07 11:40 ` [PATCH 1/6] edac: Create a dimm struct and move the labels into it Mauro Carvalho Chehab
  2012-03-07 11:40 ` [PATCH 2/6] edac: Add per dimm's sysfs nodes Mauro Carvalho Chehab
@ 2012-03-07 11:40 ` Mauro Carvalho Chehab
  2012-03-07 11:40 ` [PATCH 4/6] edac: Don't initialize csrow's first_page & friends when not needed Mauro Carvalho Chehab
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-07 11:40 UTC (permalink / raw)
  Cc: Mauro Carvalho Chehab, Linux Edac Mailing List,
	Linux Kernel Mailing List

On systems based on chip select rows, all channels need to use memories
with the same properties, otherwise the memories on channels A and B
won't be recognized.

However, such assumption is not true for all types of memory
controllers.

Controllers for FB-DIMM's don't have such requirements.

Also, modern Intel controllers seem to be capable of handling such
differences.

So, we need to get rid of storing the DIMM information into a per-csrow
data, storing it, instead at the right place.

The first step is to move grain, mtype, dtype and edac_mode to the
per-dimm struct.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
 drivers/edac/amd64_edac.c      |   30 +++++++++++-------
 drivers/edac/amd76x_edac.c     |   10 ++++--
 drivers/edac/cell_edac.c       |   10 +++++-
 drivers/edac/cpc925_edac.c     |   62 +++++++++++++++++++++------------------
 drivers/edac/e752x_edac.c      |   44 +++++++++++++++------------
 drivers/edac/e7xxx_edac.c      |   44 ++++++++++++++++------------
 drivers/edac/edac_mc.c         |   63 +++++++++++++++++----------------------
 drivers/edac/edac_mc_sysfs.c   |    6 ++--
 drivers/edac/i3000_edac.c      |   18 ++++++-----
 drivers/edac/i3200_edac.c      |   18 ++++++-----
 drivers/edac/i5000_edac.c      |   24 +++++++--------
 drivers/edac/i5100_edac.c      |   38 +++++++++++++-----------
 drivers/edac/i5400_edac.c      |   24 ++++++---------
 drivers/edac/i7300_edac.c      |   25 +++++++++------
 drivers/edac/i7core_edac.c     |   27 ++++++++---------
 drivers/edac/i82443bxgx_edac.c |   13 +++++---
 drivers/edac/i82860_edac.c     |   11 ++++--
 drivers/edac/i82875p_edac.c    |   17 +++++++---
 drivers/edac/i82975x_edac.c    |   15 ++++++---
 drivers/edac/mpc85xx_edac.c    |   13 +++++---
 drivers/edac/mv64x60_edac.c    |   18 ++++++-----
 drivers/edac/pasemi_edac.c     |   10 ++++--
 drivers/edac/ppc4xx_edac.c     |   13 +++++---
 drivers/edac/r82600_edac.c     |   10 ++++--
 drivers/edac/sb_edac.c         |   33 ++++++++++++---------
 drivers/edac/tile_edac.c       |   13 ++++----
 drivers/edac/x38_edac.c        |   17 ++++++-----
 include/linux/edac.h           |   21 ++++++++-----
 28 files changed, 357 insertions(+), 290 deletions(-)

diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index c9eee6d..3e7bddc 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -2168,7 +2168,9 @@ static int init_csrows(struct mem_ctl_info *mci)
 	struct amd64_pvt *pvt = mci->pvt_info;
 	u64 input_addr_min, input_addr_max, sys_addr, base, mask;
 	u32 val;
-	int i, empty = 1;
+	int i, j, empty = 1;
+	enum mem_type mtype;
+	enum edac_type edac_mode;
 
 	amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
 
@@ -2202,7 +2204,21 @@ static int init_csrows(struct mem_ctl_info *mci)
 		csrow->page_mask = ~mask;
 		/* 8 bytes of resolution */
 
-		csrow->mtype = amd64_determine_memory_type(pvt, i);
+		mtype = amd64_determine_memory_type(pvt, i);
+
+		/*
+		 * determine whether CHIPKILL or JUST ECC or NO ECC is operating
+		 */
+		if (pvt->nbcfg & NBCFG_ECC_ENABLE)
+			edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) ?
+				    EDAC_S4ECD4ED : EDAC_SECDED;
+		else
+			edac_mode = EDAC_NONE;
+
+		for (j = 0; j < pvt->channel_count; j++) {
+			csrow->channels[j].dimm->mtype = mtype;
+			csrow->channels[j].dimm->edac_mode = edac_mode;
+		}
 
 		debugf1("  for MC node %d csrow %d:\n", pvt->mc_node_id, i);
 		debugf1("    input_addr_min: 0x%lx input_addr_max: 0x%lx\n",
@@ -2214,16 +2230,6 @@ static int init_csrows(struct mem_ctl_info *mci)
 			"last_page: 0x%lx\n",
 			(unsigned)csrow->nr_pages,
 			csrow->first_page, csrow->last_page);
-
-		/*
-		 * determine whether CHIPKILL or JUST ECC or NO ECC is operating
-		 */
-		if (pvt->nbcfg & NBCFG_ECC_ENABLE)
-			csrow->edac_mode =
-			    (pvt->nbcfg & NBCFG_CHIPKILL) ?
-			    EDAC_S4ECD4ED : EDAC_SECDED;
-		else
-			csrow->edac_mode = EDAC_NONE;
 	}
 
 	return empty;
diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c
index e47e73b..2a63ed0 100644
--- a/drivers/edac/amd76x_edac.c
+++ b/drivers/edac/amd76x_edac.c
@@ -186,11 +186,13 @@ static void amd76x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 			enum edac_type edac_mode)
 {
 	struct csrow_info *csrow;
+	struct dimm_info *dimm;
 	u32 mba, mba_base, mba_mask, dms;
 	int index;
 
 	for (index = 0; index < mci->nr_csrows; index++) {
 		csrow = &mci->csrows[index];
+		dimm = csrow->channels[0].dimm;
 
 		/* find the DRAM Chip Select Base address and mask */
 		pci_read_config_dword(pdev,
@@ -206,10 +208,10 @@ static void amd76x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 		csrow->nr_pages = (mba_mask + 1) >> PAGE_SHIFT;
 		csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
 		csrow->page_mask = mba_mask >> PAGE_SHIFT;
-		csrow->grain = csrow->nr_pages << PAGE_SHIFT;
-		csrow->mtype = MEM_RDDR;
-		csrow->dtype = ((dms >> index) & 0x1) ? DEV_X4 : DEV_UNKNOWN;
-		csrow->edac_mode = edac_mode;
+		dimm->grain = csrow->nr_pages << PAGE_SHIFT;
+		dimm->mtype = MEM_RDDR;
+		dimm->dtype = ((dms >> index) & 0x1) ? DEV_X4 : DEV_UNKNOWN;
+		dimm->edac_mode = edac_mode;
 	}
 }
 
diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c
index 9a6a274..94fbb12 100644
--- a/drivers/edac/cell_edac.c
+++ b/drivers/edac/cell_edac.c
@@ -124,8 +124,10 @@ static void cell_edac_check(struct mem_ctl_info *mci)
 static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci)
 {
 	struct csrow_info		*csrow = &mci->csrows[0];
+	struct dimm_info		*dimm;
 	struct cell_edac_priv		*priv = mci->pvt_info;
 	struct device_node		*np;
+	int				j;
 
 	for (np = NULL;
 	     (np = of_find_node_by_name(np, "memory")) != NULL;) {
@@ -142,8 +144,12 @@ static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci)
 		csrow->first_page = r.start >> PAGE_SHIFT;
 		csrow->nr_pages = resource_size(&r) >> PAGE_SHIFT;
 		csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
-		csrow->mtype = MEM_XDR;
-		csrow->edac_mode = EDAC_SECDED;
+
+		for (j = 0; j < csrow->nr_channels; j++) {
+			dimm = csrow->channels[j].dimm;
+			dimm->mtype = MEM_XDR;
+			dimm->edac_mode = EDAC_SECDED;
+		}
 		dev_dbg(mci->dev,
 			"Initialized on node %d, chanmask=0x%x,"
 			" first_page=0x%lx, nr_pages=0x%x\n",
diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c
index a774c0d..ee90f3d 100644
--- a/drivers/edac/cpc925_edac.c
+++ b/drivers/edac/cpc925_edac.c
@@ -329,7 +329,8 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci)
 {
 	struct cpc925_mc_pdata *pdata = mci->pvt_info;
 	struct csrow_info *csrow;
-	int index;
+	struct dimm_info *dimm;
+	int index, j;
 	u32 mbmr, mbbar, bba;
 	unsigned long row_size, last_nr_pages = 0;
 
@@ -354,32 +355,35 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci)
 		csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
 		last_nr_pages = csrow->last_page + 1;
 
-		csrow->mtype = MEM_RDDR;
-		csrow->edac_mode = EDAC_SECDED;
-
-		switch (csrow->nr_channels) {
-		case 1: /* Single channel */
-			csrow->grain = 32; /* four-beat burst of 32 bytes */
-			break;
-		case 2: /* Dual channel */
-		default:
-			csrow->grain = 64; /* four-beat burst of 64 bytes */
-			break;
-		}
-
-		switch ((mbmr & MBMR_MODE_MASK) >> MBMR_MODE_SHIFT) {
-		case 6: /* 0110, no way to differentiate X8 VS X16 */
-		case 5:	/* 0101 */
-		case 8: /* 1000 */
-			csrow->dtype = DEV_X16;
-			break;
-		case 7: /* 0111 */
-		case 9: /* 1001 */
-			csrow->dtype = DEV_X8;
-			break;
-		default:
-			csrow->dtype = DEV_UNKNOWN;
-			break;
+		for (j = 0; j < csrow->nr_channels; j++) {
+			dimm = csrow->channels[j].dimm;
+			dimm->mtype = MEM_RDDR;
+			dimm->edac_mode = EDAC_SECDED;
+
+			switch (csrow->nr_channels) {
+			case 1: /* Single channel */
+				dimm->grain = 32; /* four-beat burst of 32 bytes */
+				break;
+			case 2: /* Dual channel */
+			default:
+				dimm->grain = 64; /* four-beat burst of 64 bytes */
+				break;
+			}
+
+			switch ((mbmr & MBMR_MODE_MASK) >> MBMR_MODE_SHIFT) {
+			case 6: /* 0110, no way to differentiate X8 VS X16 */
+			case 5:	/* 0101 */
+			case 8: /* 1000 */
+				dimm->dtype = DEV_X16;
+				break;
+			case 7: /* 0111 */
+			case 9: /* 1001 */
+				dimm->dtype = DEV_X8;
+				break;
+			default:
+				dimm->dtype = DEV_UNKNOWN;
+				break;
+			}
 		}
 	}
 }
@@ -962,9 +966,9 @@ static int __devinit cpc925_probe(struct platform_device *pdev)
 		goto err2;
 	}
 
-	nr_channels = cpc925_mc_get_channels(vbase);
+	nr_channels = cpc925_mc_get_channels(vbase) + 1;
 	mci = edac_mc_alloc(sizeof(struct cpc925_mc_pdata),
-			CPC925_NR_CSROWS, nr_channels + 1, edac_mc_idx);
+			CPC925_NR_CSROWS, nr_channels, edac_mc_idx);
 	if (!mci) {
 		cpc925_printk(KERN_ERR, "No memory for mem_ctl_info\n");
 		res = -ENOMEM;
diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c
index 1af531a..db291ea 100644
--- a/drivers/edac/e752x_edac.c
+++ b/drivers/edac/e752x_edac.c
@@ -1044,7 +1044,7 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 	int drc_drbg;		/* DRB granularity 0=64mb, 1=128mb */
 	int drc_ddim;		/* DRAM Data Integrity Mode 0=none, 2=edac */
 	u8 value;
-	u32 dra, drc, cumul_size;
+	u32 dra, drc, cumul_size, i;
 
 	dra = 0;
 	for (index = 0; index < 4; index++) {
@@ -1053,7 +1053,7 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 		dra |= dra_reg << (index * 8);
 	}
 	pci_read_config_dword(pdev, E752X_DRC, &drc);
-	drc_chan = dual_channel_active(ddrcsr);
+	drc_chan = dual_channel_active(ddrcsr) ? 1 : 0;
 	drc_drbg = drc_chan + 1;	/* 128 in dual mode, 64 in single */
 	drc_ddim = (drc >> 20) & 0x3;
 
@@ -1080,24 +1080,28 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 		csrow->last_page = cumul_size - 1;
 		csrow->nr_pages = cumul_size - last_cumul_size;
 		last_cumul_size = cumul_size;
-		csrow->grain = 1 << 12;	/* 4KiB - resolution of CELOG */
-		csrow->mtype = MEM_RDDR;	/* only one type supported */
-		csrow->dtype = mem_dev ? DEV_X4 : DEV_X8;
-
-		/*
-		 * if single channel or x8 devices then SECDED
-		 * if dual channel and x4 then S4ECD4ED
-		 */
-		if (drc_ddim) {
-			if (drc_chan && mem_dev) {
-				csrow->edac_mode = EDAC_S4ECD4ED;
-				mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
-			} else {
-				csrow->edac_mode = EDAC_SECDED;
-				mci->edac_cap |= EDAC_FLAG_SECDED;
-			}
-		} else
-			csrow->edac_mode = EDAC_NONE;
+
+		for (i = 0; i < drc_chan + 1; i++) {
+			struct dimm_info *dimm = csrow->channels[i].dimm;
+			dimm->grain = 1 << 12;	/* 4KiB - resolution of CELOG */
+			dimm->mtype = MEM_RDDR;	/* only one type supported */
+			dimm->dtype = mem_dev ? DEV_X4 : DEV_X8;
+
+			/*
+			* if single channel or x8 devices then SECDED
+			* if dual channel and x4 then S4ECD4ED
+			*/
+			if (drc_ddim) {
+				if (drc_chan && mem_dev) {
+					dimm->edac_mode = EDAC_S4ECD4ED;
+					mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
+				} else {
+					dimm->edac_mode = EDAC_SECDED;
+					mci->edac_cap |= EDAC_FLAG_SECDED;
+				}
+			} else
+				dimm->edac_mode = EDAC_NONE;
+		}
 	}
 }
 
diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c
index 6ffb6d2..178d2af 100644
--- a/drivers/edac/e7xxx_edac.c
+++ b/drivers/edac/e7xxx_edac.c
@@ -347,11 +347,12 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 			int dev_idx, u32 drc)
 {
 	unsigned long last_cumul_size;
-	int index;
+	int index, j;
 	u8 value;
 	u32 dra, cumul_size;
 	int drc_chan, drc_drbg, drc_ddim, mem_dev;
 	struct csrow_info *csrow;
+	struct dimm_info *dimm;
 
 	pci_read_config_dword(pdev, E7XXX_DRA, &dra);
 	drc_chan = dual_channel_active(drc, dev_idx);
@@ -381,24 +382,29 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 		csrow->last_page = cumul_size - 1;
 		csrow->nr_pages = cumul_size - last_cumul_size;
 		last_cumul_size = cumul_size;
-		csrow->grain = 1 << 12;	/* 4KiB - resolution of CELOG */
-		csrow->mtype = MEM_RDDR;	/* only one type supported */
-		csrow->dtype = mem_dev ? DEV_X4 : DEV_X8;
-
-		/*
-		 * if single channel or x8 devices then SECDED
-		 * if dual channel and x4 then S4ECD4ED
-		 */
-		if (drc_ddim) {
-			if (drc_chan && mem_dev) {
-				csrow->edac_mode = EDAC_S4ECD4ED;
-				mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
-			} else {
-				csrow->edac_mode = EDAC_SECDED;
-				mci->edac_cap |= EDAC_FLAG_SECDED;
-			}
-		} else
-			csrow->edac_mode = EDAC_NONE;
+
+		for (j = 0; j < drc_chan + 1; j++) {
+			dimm = csrow->channels[j].dimm;
+
+			dimm->grain = 1 << 12;	/* 4KiB - resolution of CELOG */
+			dimm->mtype = MEM_RDDR;	/* only one type supported */
+			dimm->dtype = mem_dev ? DEV_X4 : DEV_X8;
+
+			/*
+			* if single channel or x8 devices then SECDED
+			* if dual channel and x4 then S4ECD4ED
+			*/
+			if (drc_ddim) {
+				if (drc_chan && mem_dev) {
+					dimm->edac_mode = EDAC_S4ECD4ED;
+					mci->edac_cap |= EDAC_FLAG_S4ECD4ED;
+				} else {
+					dimm->edac_mode = EDAC_SECDED;
+					mci->edac_cap |= EDAC_FLAG_SECDED;
+				}
+			} else
+				dimm->edac_mode = EDAC_NONE;
+		}
 	}
 }
 
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index c3b5cff..7e857ea 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -44,8 +44,7 @@ static void edac_mc_dump_channel(struct rank_info *chan)
 {
 	debugf4("\tchannel = %p\n", chan);
 	debugf4("\tchannel->chan_idx = %d\n", chan->chan_idx);
-	debugf4("\tchannel->ce_count = %d\n", chan->ce_count);
-	if (chan->dimm)
+	debugf4("\tchannel->ce_count = %d\n", chan->dimm->ce_count);
 		debugf4("\tchannel->label = '%s'\n", chan->dimm->label);
 	debugf4("\tchannel->csrow = %p\n\n", chan->csrow);
 }
@@ -528,10 +527,8 @@ int edac_mc_add_mc(struct mem_ctl_info *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].
@@ -699,6 +696,7 @@ void edac_mc_handle_ce(struct mem_ctl_info *mci,
 {
 	unsigned long remapped_page;
 	char *label = NULL;
+	u32 grain;
 
 	debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
 
@@ -722,8 +720,8 @@ void edac_mc_handle_ce(struct mem_ctl_info *mci,
 		return;
 	}
 
-	if (mci->csrows[row].channels[channel].dimm)
-		label = mci->csrows[row].channels[channel].dimm->label;
+	label = mci->csrows[row].channels[channel].dimm->label;
+	grain = mci->csrows[row].channels[channel].dimm->grain;
 
 	if (edac_mc_get_log_ce())
 		/* FIXME - put in DIMM location */
@@ -731,11 +729,12 @@ void edac_mc_handle_ce(struct mem_ctl_info *mci,
 			"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,
+			grain, syndrome, row, channel,
 			label, msg);
 
 	mci->ce_count++;
 	mci->csrows[row].ce_count++;
+	mci->csrows[row].channels[channel].dimm->ce_count++;
 	mci->csrows[row].channels[channel].ce_count++;
 
 	if (mci->scrub_mode & SCRUB_SW_SRC) {
@@ -752,8 +751,7 @@ void edac_mc_handle_ce(struct mem_ctl_info *mci,
 			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);
+		edac_mc_scrub_block(remapped_page, offset_in_page, grain);
 	}
 }
 EXPORT_SYMBOL_GPL(edac_mc_handle_ce);
@@ -779,6 +777,7 @@ void edac_mc_handle_ue(struct mem_ctl_info *mci,
 	int chan;
 	int chars;
 	char *label = NULL;
+	u32 grain;
 
 	debugf3("MC%d: %s()\n", mci->mc_idx, __func__);
 
@@ -792,35 +791,31 @@ void edac_mc_handle_ue(struct mem_ctl_info *mci,
 		return;
 	}
 
-	if (mci->csrows[row].channels[0].dimm) {
-		label = mci->csrows[row].channels[0].dimm->label;
-		chars = snprintf(pos, len + 1, "%s", label);
-		len -= chars;
-		pos += chars;
-	}
+	grain = mci->csrows[row].channels[0].dimm->grain;
+	label = mci->csrows[row].channels[0].dimm->label;
+	chars = snprintf(pos, len + 1, "%s", label);
+	len -= chars;
+	pos += chars;
 
 	for (chan = 1; (chan < mci->csrows[row].nr_channels) && (len > 0);
 		chan++) {
-		if (mci->csrows[row].channels[chan].dimm) {
-			label = mci->csrows[row].channels[chan].dimm->label;
-			chars = snprintf(pos, len + 1, ":%s", label);
-			len -= chars;
-			pos += chars;
-		}
+		label = mci->csrows[row].channels[chan].dimm->label;
+		chars = snprintf(pos, len + 1, ":%s", 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);
+			offset_in_page, 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);
+			grain, row, labels, msg);
 
 	mci->ue_count++;
 	mci->csrows[row].ue_count++;
@@ -888,15 +883,13 @@ void edac_mc_handle_fbd_ue(struct mem_ctl_info *mci,
 	mci->csrows[csrow].ue_count++;
 
 	/* Generate the DIMM labels from the specified channels */
-	if (mci->csrows[csrow].channels[channela].dimm) {
-		label = mci->csrows[csrow].channels[channela].dimm->label;
-		chars = snprintf(pos, len + 1, "%s", label);
-		len -= chars;
-		pos += chars;
-	}
-	if (mci->csrows[csrow].channels[channela].dimm)
-		chars = snprintf(pos, len + 1, "-%s",
-				mci->csrows[csrow].channels[channelb].dimm->label);
+	label = mci->csrows[csrow].channels[channela].dimm->label;
+	chars = snprintf(pos, len + 1, "%s", label);
+	len -= chars;
+	pos += chars;
+
+	chars = snprintf(pos, len + 1, "-%s",
+			mci->csrows[csrow].channels[channelb].dimm->label);
 
 	if (edac_mc_get_log_ue())
 		edac_mc_printk(mci, KERN_EMERG,
@@ -938,8 +931,7 @@ void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci,
 		return;
 	}
 
-	if (mci->csrows[csrow].channels[channel].dimm)
-		label = mci->csrows[csrow].channels[channel].dimm->label;
+	label = mci->csrows[csrow].channels[channel].dimm->label;
 
 	if (edac_mc_get_log_ce())
 		/* FIXME - put in DIMM location */
@@ -949,6 +941,7 @@ void edac_mc_handle_fbd_ce(struct mem_ctl_info *mci,
 
 	mci->ce_count++;
 	mci->csrows[csrow].ce_count++;
+	mci->csrows[csrow].channels[channel].dimm->ce_count++;
 	mci->csrows[csrow].channels[channel].ce_count++;
 }
 EXPORT_SYMBOL(edac_mc_handle_fbd_ce);
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index f7e469b..80fdbf5 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -150,19 +150,19 @@ static ssize_t csrow_size_show(struct csrow_info *csrow, char *data,
 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 */
diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c
index c0510b3..1498c5f 100644
--- a/drivers/edac/i3000_edac.c
+++ b/drivers/edac/i3000_edac.c
@@ -304,7 +304,7 @@ static int i3000_is_interleaved(const unsigned char *c0dra,
 static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
 {
 	int rc;
-	int i;
+	int i, j;
 	struct mem_ctl_info *mci = NULL;
 	unsigned long last_cumul_size;
 	int interleaved, nr_channels;
@@ -386,19 +386,21 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
 			cumul_size <<= 1;
 		debugf3("MC: %s(): (%d) cumul_size 0x%x\n",
 			__func__, i, cumul_size);
-		if (cumul_size == last_cumul_size) {
-			csrow->mtype = MEM_EMPTY;
+		if (cumul_size == last_cumul_size)
 			continue;
-		}
 
 		csrow->first_page = last_cumul_size;
 		csrow->last_page = cumul_size - 1;
 		csrow->nr_pages = cumul_size - last_cumul_size;
 		last_cumul_size = cumul_size;
-		csrow->grain = I3000_DEAP_GRAIN;
-		csrow->mtype = MEM_DDR2;
-		csrow->dtype = DEV_UNKNOWN;
-		csrow->edac_mode = EDAC_UNKNOWN;
+
+		for (j = 0; j < nr_channels; j++) {
+			struct dimm_info *dimm = csrow->channels[j].dimm;
+			dimm->grain = I3000_DEAP_GRAIN;
+			dimm->mtype = MEM_DDR2;
+			dimm->dtype = DEV_UNKNOWN;
+			dimm->edac_mode = EDAC_UNKNOWN;
+		}
 	}
 
 	/*
diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c
index aa08497..38d1e87 100644
--- a/drivers/edac/i3200_edac.c
+++ b/drivers/edac/i3200_edac.c
@@ -330,7 +330,7 @@ static unsigned long drb_to_nr_pages(
 static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
 {
 	int rc;
-	int i;
+	int i, j;
 	struct mem_ctl_info *mci = NULL;
 	unsigned long last_page;
 	u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL];
@@ -386,20 +386,22 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
 			i / I3200_RANKS_PER_CHANNEL,
 			i % I3200_RANKS_PER_CHANNEL);
 
-		if (nr_pages == 0) {
-			csrow->mtype = MEM_EMPTY;
+		if (nr_pages == 0)
 			continue;
-		}
 
 		csrow->first_page = last_page + 1;
 		last_page += nr_pages;
 		csrow->last_page = last_page;
 		csrow->nr_pages = nr_pages;
 
-		csrow->grain = nr_pages << PAGE_SHIFT;
-		csrow->mtype = MEM_DDR2;
-		csrow->dtype = DEV_UNKNOWN;
-		csrow->edac_mode = EDAC_UNKNOWN;
+		for (j = 0; j < nr_channels; j++) {
+			struct dimm_info *dimm = csrow->channels[j].dimm;
+
+			dimm->grain = nr_pages << PAGE_SHIFT;
+			dimm->mtype = MEM_DDR2;
+			dimm->dtype = DEV_UNKNOWN;
+			dimm->edac_mode = EDAC_UNKNOWN;
+		}
 	}
 
 	i3200_clear_error_info(mci);
diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c
index 4dc3ac2..e612f1e 100644
--- a/drivers/edac/i5000_edac.c
+++ b/drivers/edac/i5000_edac.c
@@ -1268,25 +1268,23 @@ static int i5000_init_csrows(struct mem_ctl_info *mci)
 		p_csrow->last_page = 9 + csrow * 20;
 		p_csrow->page_mask = 0xFFF;
 
-		p_csrow->grain = 8;
-
 		csrow_megs = 0;
 		for (channel = 0; channel < pvt->maxch; channel++) {
 			csrow_megs += pvt->dimm_info[csrow][channel].megabytes;
-		}
+			p_csrow->channels[channel].dimm->grain = 8;
 
-		p_csrow->nr_pages = csrow_megs << 8;
+			/* Assume DDR2 for now */
+			p_csrow->channels[channel].dimm->mtype = MEM_FB_DDR2;
 
-		/* Assume DDR2 for now */
-		p_csrow->mtype = MEM_FB_DDR2;
+			/* ask what device type on this row */
+			if (MTR_DRAM_WIDTH(mtr))
+				p_csrow->channels[channel].dimm->dtype = DEV_X8;
+			else
+				p_csrow->channels[channel].dimm->dtype = DEV_X4;
 
-		/* ask what device type on this row */
-		if (MTR_DRAM_WIDTH(mtr))
-			p_csrow->dtype = DEV_X8;
-		else
-			p_csrow->dtype = DEV_X4;
-
-		p_csrow->edac_mode = EDAC_S8ECD8ED;
+			p_csrow->channels[channel].dimm->edac_mode = EDAC_S8ECD8ED;
+		}
+		p_csrow->nr_pages = csrow_megs << 8;
 
 		empty = 0;
 	}
diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c
index fd931f4..50e6d6a 100644
--- a/drivers/edac/i5100_edac.c
+++ b/drivers/edac/i5100_edac.c
@@ -428,12 +428,16 @@ static void i5100_handle_ce(struct mem_ctl_info *mci,
 			    const char *msg)
 {
 	const int csrow = i5100_rank_to_csrow(mci, chan, rank);
+	char *label = NULL;
+
+	if (mci->csrows[csrow].channels[0].dimm)
+		label = mci->csrows[csrow].channels[0].dimm->label;
 
 	printk(KERN_ERR
 		"CE chan %d, bank %u, rank %u, syndrome 0x%lx, "
 		"cas %u, ras %u, csrow %u, label \"%s\": %s\n",
 		chan, bank, rank, syndrome, cas, ras,
-		csrow, mci->csrows[csrow].channels[0].dimm->label, msg);
+		csrow, label, msg);
 
 	mci->ce_count++;
 	mci->csrows[csrow].ce_count++;
@@ -450,12 +454,16 @@ static void i5100_handle_ue(struct mem_ctl_info *mci,
 			    const char *msg)
 {
 	const int csrow = i5100_rank_to_csrow(mci, chan, rank);
+	char *label = NULL;
+
+	if (mci->csrows[csrow].channels[0].dimm)
+		label = mci->csrows[csrow].channels[0].dimm->label;
 
 	printk(KERN_ERR
 		"UE chan %d, bank %u, rank %u, syndrome 0x%lx, "
 		"cas %u, ras %u, csrow %u, label \"%s\": %s\n",
 		chan, bank, rank, syndrome, cas, ras,
-		csrow, mci->csrows[csrow].channels[0].dimm->label, msg);
+		csrow, label, msg);
 
 	mci->ue_count++;
 	mci->csrows[csrow].ue_count++;
@@ -840,6 +848,7 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
 	int i;
 	unsigned long total_pages = 0UL;
 	struct i5100_priv *priv = mci->pvt_info;
+	struct dimm_info *dimm;
 
 	for (i = 0; i < mci->nr_csrows; i++) {
 		const unsigned long npages = i5100_npages(mci, i);
@@ -855,27 +864,22 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
 		 */
 		mci->csrows[i].first_page = total_pages;
 		mci->csrows[i].last_page = total_pages + npages - 1;
-		mci->csrows[i].page_mask = 0UL;
-
 		mci->csrows[i].nr_pages = npages;
-		mci->csrows[i].grain = 32;
 		mci->csrows[i].csrow_idx = i;
-		mci->csrows[i].dtype =
-			(priv->mtr[chan][rank].width == 4) ? DEV_X4 : DEV_X8;
-		mci->csrows[i].ue_count = 0;
-		mci->csrows[i].ce_count = 0;
-		mci->csrows[i].mtype = MEM_RDDR2;
-		mci->csrows[i].edac_mode = EDAC_SECDED;
 		mci->csrows[i].mci = mci;
 		mci->csrows[i].nr_channels = 1;
-		mci->csrows[i].channels[0].chan_idx = 0;
-		mci->csrows[i].channels[0].ce_count = 0;
 		mci->csrows[i].channels[0].csrow = mci->csrows + i;
-		snprintf(mci->csrows[i].channels[0].dimm->label,
-			 sizeof(mci->csrows[i].channels[0].dimm->label),
-			 "DIMM%u", i5100_rank_to_slot(mci, chan, rank));
-
 		total_pages += npages;
+
+		dimm = mci->csrows[i].channels[0].dimm;
+		dimm->grain = 32;
+		dimm->dtype = (priv->mtr[chan][rank].width == 4) ?
+			      DEV_X4 : DEV_X8;
+		dimm->mtype = MEM_RDDR2;
+		dimm->edac_mode = EDAC_SECDED;
+		snprintf(dimm->label, sizeof(dimm->label),
+			 "DIMM%u",
+			 i5100_rank_to_slot(mci, chan, rank));
 	}
 }
 
diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c
index b44a5de..229aff5 100644
--- a/drivers/edac/i5400_edac.c
+++ b/drivers/edac/i5400_edac.c
@@ -1159,6 +1159,7 @@ static int i5400_init_csrows(struct mem_ctl_info *mci)
 	int csrow_megs;
 	int channel;
 	int csrow;
+	struct dimm_info *dimm;
 
 	pvt = mci->pvt_info;
 
@@ -1184,24 +1185,17 @@ static int i5400_init_csrows(struct mem_ctl_info *mci)
 		p_csrow->last_page = 9 + csrow * 20;
 		p_csrow->page_mask = 0xFFF;
 
-		p_csrow->grain = 8;
-
 		csrow_megs = 0;
-		for (channel = 0; channel < pvt->maxch; channel++)
+		for (channel = 0; channel < pvt->maxch; channel++) {
 			csrow_megs += pvt->dimm_info[csrow][channel].megabytes;
 
-		p_csrow->nr_pages = csrow_megs << 8;
-
-		/* Assume DDR2 for now */
-		p_csrow->mtype = MEM_FB_DDR2;
-
-		/* ask what device type on this row */
-		if (MTR_DRAM_WIDTH(mtr))
-			p_csrow->dtype = DEV_X8;
-		else
-			p_csrow->dtype = DEV_X4;
-
-		p_csrow->edac_mode = EDAC_S8ECD8ED;
+			p_csrow->nr_pages = csrow_megs << 8;
+			dimm = p_csrow->channels[channel].dimm;
+			dimm->grain = 8;
+			dimm->dtype = MTR_DRAM_WIDTH(mtr) ? DEV_X8 : DEV_X4;
+			dimm->mtype = MEM_RDDR2;
+			dimm->edac_mode = EDAC_SECDED;
+		}
 
 		empty = 0;
 	}
diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c
index 6104dba..07a5927 100644
--- a/drivers/edac/i7300_edac.c
+++ b/drivers/edac/i7300_edac.c
@@ -618,6 +618,7 @@ static int decode_mtr(struct i7300_pvt *pvt,
 		      int slot, int ch, int branch,
 		      struct i7300_dimm_info *dinfo,
 		      struct csrow_info *p_csrow,
+		      struct dimm_info *dimm,
 		      u32 *nr_pages)
 {
 	int mtr, ans, addrBits, channel;
@@ -663,10 +664,7 @@ static int decode_mtr(struct i7300_pvt *pvt,
 	debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]);
 	debugf2("\t\tSIZE: %d MB\n", dinfo->megabytes);
 
-	p_csrow->grain = 8;
-	p_csrow->mtype = MEM_FB_DDR2;
 	p_csrow->csrow_idx = slot;
-	p_csrow->page_mask = 0;
 
 	/*
 	 * The type of error detection actually depends of the
@@ -677,15 +675,17 @@ static int decode_mtr(struct i7300_pvt *pvt,
 	 * See datasheet Sections 7.3.6 to 7.3.8
 	 */
 
+	dimm->grain = 8;
+	dimm->mtype = MEM_FB_DDR2;
 	if (IS_SINGLE_MODE(pvt->mc_settings_a)) {
-		p_csrow->edac_mode = EDAC_SECDED;
+		dimm->edac_mode = EDAC_SECDED;
 		debugf2("\t\tECC code is 8-byte-over-32-byte SECDED+ code\n");
 	} else {
 		debugf2("\t\tECC code is on Lockstep mode\n");
 		if (MTR_DRAM_WIDTH(mtr) == 8)
-			p_csrow->edac_mode = EDAC_S8ECD8ED;
+			dimm->edac_mode = EDAC_S8ECD8ED;
 		else
-			p_csrow->edac_mode = EDAC_S4ECD4ED;
+			dimm->edac_mode = EDAC_S4ECD4ED;
 	}
 
 	/* ask what device type on this row */
@@ -694,9 +694,9 @@ static int decode_mtr(struct i7300_pvt *pvt,
 			IS_SCRBALGO_ENHANCED(pvt->mc_settings) ?
 					    "enhanced" : "normal");
 
-		p_csrow->dtype = DEV_X8;
+		dimm->dtype = DEV_X8;
 	} else
-		p_csrow->dtype = DEV_X4;
+		dimm->dtype = DEV_X4;
 
 	return mtr;
 }
@@ -779,6 +779,7 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
 	int mtr;
 	int ch, branch, slot, channel;
 	u32 last_page = 0, nr_pages;
+	struct dimm_info *dimm;
 
 	pvt = mci->pvt_info;
 
@@ -803,20 +804,24 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
 	}
 
 	/* Get the set of MTR[0-7] regs by each branch */
+	nr_pages = 0;
 	for (slot = 0; slot < MAX_SLOTS; slot++) {
 		int where = mtr_regs[slot];
 		for (branch = 0; branch < MAX_BRANCHES; branch++) {
 			pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch],
 					where,
 					&pvt->mtr[slot][branch]);
-			for (ch = 0; ch < MAX_BRANCHES; ch++) {
+			for (ch = 0; ch < MAX_CH_PER_BRANCH; ch++) {
 				int channel = to_channel(ch, branch);
 
 				dinfo = &pvt->dimm_info[slot][channel];
 				p_csrow = &mci->csrows[slot];
 
+				dimm = p_csrow->channels[branch * MAX_CH_PER_BRANCH + ch].dimm;
+
 				mtr = decode_mtr(pvt, slot, ch, branch,
-						 dinfo, p_csrow, &nr_pages);
+						 dinfo, p_csrow, dimm,
+						 &nr_pages);
 				/* if no DIMMS on this row, continue */
 				if (!MTR_DIMMS_PRESENT(mtr))
 					continue;
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index 9a262c7..e325314 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -592,7 +592,7 @@ static int i7core_get_active_channels(const u8 socket, unsigned *channels,
 	return 0;
 }
 
-static int get_dimm_config(const struct mem_ctl_info *mci)
+static int get_dimm_config(struct mem_ctl_info *mci)
 {
 	struct i7core_pvt *pvt = mci->pvt_info;
 	struct csrow_info *csr;
@@ -602,6 +602,7 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
 	unsigned long last_page = 0;
 	enum edac_type mode;
 	enum mem_type mtype;
+	struct dimm_info *dimm;
 
 	/* Get data from the MC register, function 0 */
 	pdev = pvt->pci_mcr[0];
@@ -721,7 +722,6 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
 			csr->nr_pages = npages;
 
 			csr->page_mask = 0;
-			csr->grain = 8;
 			csr->csrow_idx = csrow;
 			csr->nr_channels = 1;
 
@@ -730,28 +730,27 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
 
 			pvt->csrow_map[i][j] = csrow;
 
+			dimm = csr->channels[0].dimm;
 			switch (banks) {
 			case 4:
-				csr->dtype = DEV_X4;
+				dimm->dtype = DEV_X4;
 				break;
 			case 8:
-				csr->dtype = DEV_X8;
+				dimm->dtype = DEV_X8;
 				break;
 			case 16:
-				csr->dtype = DEV_X16;
+				dimm->dtype = DEV_X16;
 				break;
 			default:
-				csr->dtype = DEV_UNKNOWN;
+				dimm->dtype = DEV_UNKNOWN;
 			}
 
-			csr->edac_mode = mode;
-			csr->mtype = mtype;
-			snprintf(csr->channels[0].dimm->label,
-					sizeof(csr->channels[0].dimm->label),
-					"CPU#%uChannel#%u_DIMM#%u",
-					pvt->i7core_dev->socket, i, j);
-
-			csrow++;
+			snprintf(dimm->label, sizeof(dimm->label),
+				 "CPU#%uChannel#%u_DIMM#%u",
+				 pvt->i7core_dev->socket, i, j);
+			dimm->grain = 8;
+			dimm->edac_mode = mode;
+			dimm->mtype = mtype;
 		}
 
 		pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c
index 4329d39..1e19492 100644
--- a/drivers/edac/i82443bxgx_edac.c
+++ b/drivers/edac/i82443bxgx_edac.c
@@ -12,7 +12,7 @@
  * 440GX fix by Jason Uhlenkott <juhlenko@akamai.com>.
  *
  * Written with reference to 82443BX Host Bridge Datasheet:
- * http://download.intel.com/design/chipsets/datashts/29063301.pdf 
+ * http://download.intel.com/design/chipsets/datashts/29063301.pdf
  * references to this document given in [].
  *
  * This module doesn't support the 440LX, but it may be possible to
@@ -189,6 +189,7 @@ static void i82443bxgx_init_csrows(struct mem_ctl_info *mci,
 				enum mem_type mtype)
 {
 	struct csrow_info *csrow;
+	struct dimm_info *dimm;
 	int index;
 	u8 drbar, dramc;
 	u32 row_base, row_high_limit, row_high_limit_last;
@@ -197,6 +198,8 @@ static void i82443bxgx_init_csrows(struct mem_ctl_info *mci,
 	row_high_limit_last = 0;
 	for (index = 0; index < mci->nr_csrows; index++) {
 		csrow = &mci->csrows[index];
+		dimm = csrow->channels[0].dimm;
+
 		pci_read_config_byte(pdev, I82443BXGX_DRB + index, &drbar);
 		debugf1("MC%d: %s: %s() Row=%d DRB = %#0x\n",
 			mci->mc_idx, __FILE__, __func__, index, drbar);
@@ -219,12 +222,12 @@ static void i82443bxgx_init_csrows(struct mem_ctl_info *mci,
 		csrow->last_page = (row_high_limit >> PAGE_SHIFT) - 1;
 		csrow->nr_pages = csrow->last_page - csrow->first_page + 1;
 		/* EAP reports in 4kilobyte granularity [61] */
-		csrow->grain = 1 << 12;
-		csrow->mtype = mtype;
+		dimm->grain = 1 << 12;
+		dimm->mtype = mtype;
 		/* I don't think 440BX can tell you device type? FIXME? */
-		csrow->dtype = DEV_UNKNOWN;
+		dimm->dtype = DEV_UNKNOWN;
 		/* Mode is global to all rows on 440BX */
-		csrow->edac_mode = edac_mode;
+		dimm->edac_mode = edac_mode;
 		row_high_limit_last = row_high_limit;
 	}
 }
diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c
index 931a057..acbd924 100644
--- a/drivers/edac/i82860_edac.c
+++ b/drivers/edac/i82860_edac.c
@@ -140,6 +140,7 @@ static void i82860_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev)
 	u16 value;
 	u32 cumul_size;
 	struct csrow_info *csrow;
+	struct dimm_info *dimm;
 	int index;
 
 	pci_read_config_word(pdev, I82860_MCHCFG, &mchcfg_ddim);
@@ -153,6 +154,8 @@ static void i82860_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev)
 	 */
 	for (index = 0; index < mci->nr_csrows; index++) {
 		csrow = &mci->csrows[index];
+		dimm = csrow->channels[0].dimm;
+
 		pci_read_config_word(pdev, I82860_GBA + index * 2, &value);
 		cumul_size = (value & I82860_GBA_MASK) <<
 			(I82860_GBA_SHIFT - PAGE_SHIFT);
@@ -166,10 +169,10 @@ static void i82860_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev)
 		csrow->last_page = cumul_size - 1;
 		csrow->nr_pages = cumul_size - last_cumul_size;
 		last_cumul_size = cumul_size;
-		csrow->grain = 1 << 12;	/* I82860_EAP has 4KiB reolution */
-		csrow->mtype = MEM_RMBS;
-		csrow->dtype = DEV_UNKNOWN;
-		csrow->edac_mode = mchcfg_ddim ? EDAC_SECDED : EDAC_NONE;
+		dimm->grain = 1 << 12;	/* I82860_EAP has 4KiB reolution */
+		dimm->mtype = MEM_RMBS;
+		dimm->dtype = DEV_UNKNOWN;
+		dimm->edac_mode = mchcfg_ddim ? EDAC_SECDED : EDAC_NONE;
 	}
 }
 
diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c
index 33864c6..81f79e2 100644
--- a/drivers/edac/i82875p_edac.c
+++ b/drivers/edac/i82875p_edac.c
@@ -342,11 +342,13 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci,
 				void __iomem * ovrfl_window, u32 drc)
 {
 	struct csrow_info *csrow;
+	struct dimm_info *dimm;
+	unsigned nr_chans = dual_channel_active(drc) + 1;
 	unsigned long last_cumul_size;
 	u8 value;
 	u32 drc_ddim;		/* DRAM Data Integrity Mode 0=none,2=edac */
 	u32 cumul_size;
-	int index;
+	int index, j;
 
 	drc_ddim = (drc >> 18) & 0x1;
 	last_cumul_size = 0;
@@ -371,10 +373,15 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci,
 		csrow->last_page = cumul_size - 1;
 		csrow->nr_pages = cumul_size - last_cumul_size;
 		last_cumul_size = cumul_size;
-		csrow->grain = 1 << 12;	/* I82875P_EAP has 4KiB reolution */
-		csrow->mtype = MEM_DDR;
-		csrow->dtype = DEV_UNKNOWN;
-		csrow->edac_mode = drc_ddim ? EDAC_SECDED : EDAC_NONE;
+
+		for (j = 0; j < nr_chans; j++) {
+			dimm = csrow->channels[j].dimm;
+
+			dimm->grain = 1 << 12;	/* I82875P_EAP has 4KiB reolution */
+			dimm->mtype = MEM_DDR;
+			dimm->dtype = DEV_UNKNOWN;
+			dimm->edac_mode = drc_ddim ? EDAC_SECDED : EDAC_NONE;
+		}
 	}
 }
 
diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c
index d12e571..d29effb 100644
--- a/drivers/edac/i82975x_edac.c
+++ b/drivers/edac/i82975x_edac.c
@@ -364,6 +364,8 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
 	u8 value;
 	u32 cumul_size;
 	int index, chan;
+	struct dimm_info *dimm;
+	enum dev_type dtype;
 
 	last_cumul_size = 0;
 
@@ -398,10 +400,17 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
 		 *   [0-7] for single-channel; i.e. csrow->nr_channels = 1
 		 *   [0-3] for dual-channel; i.e. csrow->nr_channels = 2
 		 */
-		for (chan = 0; chan < csrow->nr_channels; chan++)
+		dtype = i82975x_dram_type(mch_window, index);
+		for (chan = 0; chan < csrow->nr_channels; chan++) {
+			dimm = mci->csrows[index].channels[chan].dimm;
 			strncpy(csrow->channels[chan].dimm->label,
 					labels[(index >> 1) + (chan * 2)],
 					EDAC_MC_LABEL_LEN);
+			dimm->grain = 1 << 6;	/* I82975X_EAP has 64B resolution */
+			dimm->dtype = dtype;
+			dimm->mtype = MEM_DDR2; /* I82975x supports only DDR2 */
+			dimm->edac_mode = EDAC_SECDED; /* only supported */
+		}
 
 		if (cumul_size == last_cumul_size)
 			continue;	/* not populated */
@@ -410,10 +419,6 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
 		csrow->last_page = cumul_size - 1;
 		csrow->nr_pages = cumul_size - last_cumul_size;
 		last_cumul_size = cumul_size;
-		csrow->grain = 1 << 6;	/* I82975X_EAP has 64B resolution */
-		csrow->mtype = MEM_DDR2; /* I82975x supports only DDR2 */
-		csrow->dtype = i82975x_dram_type(mch_window, index);
-		csrow->edac_mode = EDAC_SECDED; /* only supported */
 	}
 }
 
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
index 73464a6..fb92916 100644
--- a/drivers/edac/mpc85xx_edac.c
+++ b/drivers/edac/mpc85xx_edac.c
@@ -883,6 +883,7 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci)
 {
 	struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
 	struct csrow_info *csrow;
+	struct dimm_info *dimm;
 	u32 sdram_ctl;
 	u32 sdtype;
 	enum mem_type mtype;
@@ -929,6 +930,8 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci)
 		u32 end;
 
 		csrow = &mci->csrows[index];
+		dimm = csrow->channels[0].dimm;
+
 		cs_bnds = in_be32(pdata->mc_vbase + MPC85XX_MC_CS_BNDS_0 +
 				  (index * MPC85XX_MC_CS_BNDS_OFS));
 
@@ -945,12 +948,12 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci)
 		csrow->first_page = start;
 		csrow->last_page = end;
 		csrow->nr_pages = end + 1 - start;
-		csrow->grain = 8;
-		csrow->mtype = mtype;
-		csrow->dtype = DEV_UNKNOWN;
+		dimm->grain = 8;
+		dimm->mtype = mtype;
+		dimm->dtype = DEV_UNKNOWN;
 		if (sdram_ctl & DSC_X32_EN)
-			csrow->dtype = DEV_X32;
-		csrow->edac_mode = EDAC_SECDED;
+			dimm->dtype = DEV_X32;
+		dimm->edac_mode = EDAC_SECDED;
 	}
 }
 
diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c
index 7e5ff36..12d7fe0 100644
--- a/drivers/edac/mv64x60_edac.c
+++ b/drivers/edac/mv64x60_edac.c
@@ -656,6 +656,8 @@ static void mv64x60_init_csrows(struct mem_ctl_info *mci,
 				struct mv64x60_mc_pdata *pdata)
 {
 	struct csrow_info *csrow;
+	struct dimm_info *dimm;
+
 	u32 devtype;
 	u32 ctl;
 
@@ -664,30 +666,30 @@ static void mv64x60_init_csrows(struct mem_ctl_info *mci,
 	ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG);
 
 	csrow = &mci->csrows[0];
-	csrow->first_page = 0;
+	dimm = csrow->channels[0].dimm;
 	csrow->nr_pages = pdata->total_mem >> PAGE_SHIFT;
 	csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
-	csrow->grain = 8;
+	dimm->grain = 8;
 
-	csrow->mtype = (ctl & MV64X60_SDRAM_REGISTERED) ? MEM_RDDR : MEM_DDR;
+	dimm->mtype = (ctl & MV64X60_SDRAM_REGISTERED) ? MEM_RDDR : MEM_DDR;
 
 	devtype = (ctl >> 20) & 0x3;
 	switch (devtype) {
 	case 0x0:
-		csrow->dtype = DEV_X32;
+		dimm->dtype = DEV_X32;
 		break;
 	case 0x2:		/* could be X8 too, but no way to tell */
-		csrow->dtype = DEV_X16;
+		dimm->dtype = DEV_X16;
 		break;
 	case 0x3:
-		csrow->dtype = DEV_X4;
+		dimm->dtype = DEV_X4;
 		break;
 	default:
-		csrow->dtype = DEV_UNKNOWN;
+		dimm->dtype = DEV_UNKNOWN;
 		break;
 	}
 
-	csrow->edac_mode = EDAC_SECDED;
+	dimm->edac_mode = EDAC_SECDED;
 }
 
 static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev)
diff --git a/drivers/edac/pasemi_edac.c b/drivers/edac/pasemi_edac.c
index 7f71ee4..4e53270 100644
--- a/drivers/edac/pasemi_edac.c
+++ b/drivers/edac/pasemi_edac.c
@@ -135,11 +135,13 @@ static int pasemi_edac_init_csrows(struct mem_ctl_info *mci,
 				   enum edac_type edac_mode)
 {
 	struct csrow_info *csrow;
+	struct dimm_info *dimm;
 	u32 rankcfg;
 	int index;
 
 	for (index = 0; index < mci->nr_csrows; index++) {
 		csrow = &mci->csrows[index];
+		dimm = csrow->channels[0].dimm;
 
 		pci_read_config_dword(pdev,
 				      MCDRAM_RANKCFG + (index * 12),
@@ -177,10 +179,10 @@ static int pasemi_edac_init_csrows(struct mem_ctl_info *mci,
 		csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
 		last_page_in_mmc += csrow->nr_pages;
 		csrow->page_mask = 0;
-		csrow->grain = PASEMI_EDAC_ERROR_GRAIN;
-		csrow->mtype = MEM_DDR;
-		csrow->dtype = DEV_UNKNOWN;
-		csrow->edac_mode = edac_mode;
+		dimm->grain = PASEMI_EDAC_ERROR_GRAIN;
+		dimm->mtype = MEM_DDR;
+		dimm->dtype = DEV_UNKNOWN;
+		dimm->edac_mode = edac_mode;
 	}
 	return 0;
 }
diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c
index 2fd8f19..00efc5f 100644
--- a/drivers/edac/ppc4xx_edac.c
+++ b/drivers/edac/ppc4xx_edac.c
@@ -895,7 +895,7 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
 	enum mem_type mtype;
 	enum dev_type dtype;
 	enum edac_type edac_mode;
-	int row;
+	int row, j;
 	u32 mbxcf, size;
 	static u32 ppc4xx_last_page;
 
@@ -975,15 +975,18 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
 		 * possible values would be the PLB width (16), the
 		 * page size (PAGE_SIZE) or the memory width (2 or 4).
 		 */
+		for (j = 0; j < csi->nr_channels; j++) {
+			struct dimm_info *dimm = csi->channels[j].dimm;
 
-		csi->grain	= 1;
+			dimm->grain	= 1;
 
-		csi->mtype	= mtype;
-		csi->dtype	= dtype;
+			dimm->mtype	= mtype;
+			dimm->dtype	= dtype;
 
-		csi->edac_mode	= edac_mode;
+			dimm->edac_mode	= edac_mode;
 
 		ppc4xx_last_page += csi->nr_pages;
+		}
 	}
 
  done:
diff --git a/drivers/edac/r82600_edac.c b/drivers/edac/r82600_edac.c
index b153674..c8b774d 100644
--- a/drivers/edac/r82600_edac.c
+++ b/drivers/edac/r82600_edac.c
@@ -216,6 +216,7 @@ static void r82600_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 			u8 dramcr)
 {
 	struct csrow_info *csrow;
+	struct dimm_info *dimm;
 	int index;
 	u8 drbar;		/* SDRAM Row Boundary Address Register */
 	u32 row_high_limit, row_high_limit_last;
@@ -227,6 +228,7 @@ static void r82600_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 
 	for (index = 0; index < mci->nr_csrows; index++) {
 		csrow = &mci->csrows[index];
+		dimm = csrow->channels[0].dimm;
 
 		/* find the DRAM Chip Select Base address and mask */
 		pci_read_config_byte(pdev, R82600_DRBA + index, &drbar);
@@ -250,13 +252,13 @@ static void r82600_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 		csrow->nr_pages = csrow->last_page - csrow->first_page + 1;
 		/* Error address is top 19 bits - so granularity is      *
 		 * 14 bits                                               */
-		csrow->grain = 1 << 14;
-		csrow->mtype = reg_sdram ? MEM_RDDR : MEM_DDR;
+		dimm->grain = 1 << 14;
+		dimm->mtype = reg_sdram ? MEM_RDDR : MEM_DDR;
 		/* FIXME - check that this is unknowable with this chipset */
-		csrow->dtype = DEV_UNKNOWN;
+		dimm->dtype = DEV_UNKNOWN;
 
 		/* Mode is global on 82600 */
-		csrow->edac_mode = ecc_on ? EDAC_SECDED : EDAC_NONE;
+		dimm->edac_mode = ecc_on ? EDAC_SECDED : EDAC_NONE;
 		row_high_limit_last = row_high_limit;
 	}
 }
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index 7a402bf..773a69c 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -550,7 +550,7 @@ static int sbridge_get_active_channels(const u8 bus, unsigned *channels,
 	return 0;
 }
 
-static int get_dimm_config(const struct mem_ctl_info *mci)
+static int get_dimm_config(struct mem_ctl_info *mci)
 {
 	struct sbridge_pvt *pvt = mci->pvt_info;
 	struct csrow_info *csr;
@@ -560,6 +560,7 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
 	u32 reg;
 	enum edac_type mode;
 	enum mem_type mtype;
+	struct dimm_info *dimm;
 
 	pci_read_config_dword(pvt->pci_br, SAD_TARGET, &reg);
 	pvt->sbridge_dev->source_id = SOURCE_ID(reg);
@@ -611,6 +612,7 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
 	/* On all supported DDR3 DIMM types, there are 8 banks available */
 	banks = 8;
 
+	dimm = mci->dimms;
 	for (i = 0; i < NUM_CHANNELS; i++) {
 		u32 mtr;
 
@@ -633,29 +635,32 @@ static int get_dimm_config(const struct mem_ctl_info *mci)
 					pvt->sbridge_dev->mc, i, j,
 					size, npages,
 					banks, ranks, rows, cols);
-				csr = &mci->csrows[csrow];
 
+				/*
+				 * Fake stuff. This controller doesn't see
+				 * csrows.
+				 */
+				csr = &mci->csrows[csrow];
 				csr->first_page = last_page;
 				csr->last_page = last_page + npages - 1;
-				csr->page_mask = 0UL;	/* Unused */
 				csr->nr_pages = npages;
-				csr->grain = 32;
 				csr->csrow_idx = csrow;
-				csr->dtype = (banks == 8) ? DEV_X8 : DEV_X4;
-				csr->ce_count = 0;
-				csr->ue_count = 0;
-				csr->mtype = mtype;
-				csr->edac_mode = mode;
 				csr->nr_channels = 1;
 				csr->channels[0].chan_idx = i;
-				csr->channels[0].ce_count = 0;
 				pvt->csrow_map[i][j] = csrow;
-				snprintf(csr->channels[0].label,
-					 sizeof(csr->channels[0].label),
-					 "CPU_SrcID#%u_Channel#%u_DIMM#%u",
-					 pvt->sbridge_dev->source_id, i, j);
 				last_page += npages;
 				csrow++;
+
+				csr->channels[0].dimm = dimm;
+				dimm->location.mc_channel = i;
+				dimm->location.mc_dimm_number = j;
+				dimm->grain = 32;
+				dimm->dtype = (banks == 8) ? DEV_X8 : DEV_X4;
+				dimm->mtype = mtype;
+				dimm->edac_mode = mode;
+				snprintf(dimm->label, sizeof(dimm->label),
+					 "CPU_SrcID#%u_Channel#%u_DIMM#%u",
+					 pvt->sbridge_dev->source_id, i, j);
 			}
 		}
 	}
diff --git a/drivers/edac/tile_edac.c b/drivers/edac/tile_edac.c
index 1d5cf06..db7d2ae 100644
--- a/drivers/edac/tile_edac.c
+++ b/drivers/edac/tile_edac.c
@@ -84,6 +84,7 @@ static int __devinit tile_edac_init_csrows(struct mem_ctl_info *mci)
 	struct csrow_info	*csrow = &mci->csrows[0];
 	struct tile_edac_priv	*priv = mci->pvt_info;
 	struct mshim_mem_info	mem_info;
+	struct dimm_info *dimm = csrow->channels[0].dimm;
 
 	if (hv_dev_pread(priv->hv_devhdl, 0, (HV_VirtAddr)&mem_info,
 		sizeof(struct mshim_mem_info), MSHIM_MEM_INFO_OFF) !=
@@ -93,16 +94,16 @@ static int __devinit tile_edac_init_csrows(struct mem_ctl_info *mci)
 	}
 
 	if (mem_info.mem_ecc)
-		csrow->edac_mode = EDAC_SECDED;
+		dimm->edac_mode = EDAC_SECDED;
 	else
-		csrow->edac_mode = EDAC_NONE;
+		dimm->edac_mode = EDAC_NONE;
 	switch (mem_info.mem_type) {
 	case DDR2:
-		csrow->mtype = MEM_DDR2;
+		dimm->mtype = MEM_DDR2;
 		break;
 
 	case DDR3:
-		csrow->mtype = MEM_DDR3;
+		dimm->mtype = MEM_DDR3;
 		break;
 
 	default:
@@ -112,8 +113,8 @@ static int __devinit tile_edac_init_csrows(struct mem_ctl_info *mci)
 	csrow->first_page = 0;
 	csrow->nr_pages = mem_info.mem_size >> PAGE_SHIFT;
 	csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
-	csrow->grain = TILE_EDAC_ERROR_GRAIN;
-	csrow->dtype = DEV_UNKNOWN;
+	dimm->grain = TILE_EDAC_ERROR_GRAIN;
+	dimm->dtype = DEV_UNKNOWN;
 
 	return 0;
 }
diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c
index b6f47de..52c8d69 100644
--- a/drivers/edac/x38_edac.c
+++ b/drivers/edac/x38_edac.c
@@ -317,7 +317,7 @@ static unsigned long drb_to_nr_pages(
 static int x38_probe1(struct pci_dev *pdev, int dev_idx)
 {
 	int rc;
-	int i;
+	int i, j;
 	struct mem_ctl_info *mci = NULL;
 	unsigned long last_page;
 	u16 drbs[X38_CHANNELS][X38_RANKS_PER_CHANNEL];
@@ -372,20 +372,21 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
 			i / X38_RANKS_PER_CHANNEL,
 			i % X38_RANKS_PER_CHANNEL);
 
-		if (nr_pages == 0) {
-			csrow->mtype = MEM_EMPTY;
+		if (nr_pages == 0)
 			continue;
-		}
 
 		csrow->first_page = last_page + 1;
 		last_page += nr_pages;
 		csrow->last_page = last_page;
 		csrow->nr_pages = nr_pages;
 
-		csrow->grain = nr_pages << PAGE_SHIFT;
-		csrow->mtype = MEM_DDR2;
-		csrow->dtype = DEV_UNKNOWN;
-		csrow->edac_mode = EDAC_UNKNOWN;
+		for (j = 0; j < x38_channel_num; j++) {
+			struct dimm_info *dimm = csrow->channels[j].dimm;
+			dimm->grain = nr_pages << PAGE_SHIFT;
+			dimm->mtype = MEM_DDR2;
+			dimm->dtype = DEV_UNKNOWN;
+			dimm->edac_mode = EDAC_UNKNOWN;
+		}
 	}
 
 	x38_clear_error_info(mci);
diff --git a/include/linux/edac.h b/include/linux/edac.h
index 296fc7b..26b9a58 100644
--- a/include/linux/edac.h
+++ b/include/linux/edac.h
@@ -343,6 +343,13 @@ struct dimm_info {
 	unsigned csrow_channel;
 	struct kobject kobj;		/* sysfs kobject for this csrow */
 	struct mem_ctl_info *mci;	/* the parent */
+
+	u32 grain;		/* granularity of reported error in bytes */
+	enum dev_type dtype;	/* memory device type */
+	enum mem_type mtype;	/* memory dimm type */
+	enum edac_type edac_mode;	/* EDAC mode for this dimm */
+
+	u32 ce_count;		/* Correctable Errors for this dimm */
 };
 
 /**
@@ -368,19 +375,17 @@ struct rank_info {
 };
 
 struct csrow_info {
-	unsigned long first_page;	/* first page number in dimm */
-	unsigned long last_page;	/* last page number in dimm */
+	unsigned long first_page;	/* first page number in csrow */
+	unsigned long last_page;	/* last page number in csrow */
+	u32 nr_pages;			/* number of pages in csrow */
 	unsigned long page_mask;	/* used for interleaving -
 					 * 0UL for non intlv
 					 */
-	u32 nr_pages;		/* number of pages in csrow */
-	u32 grain;		/* granularity of reported error in bytes */
-	int csrow_idx;		/* the chip-select row */
-	enum dev_type dtype;	/* memory device type */
+	int csrow_idx;			/* the chip-select row */
+
 	u32 ue_count;		/* Uncorrectable Errors for this csrow */
 	u32 ce_count;		/* Correctable Errors for this csrow */
-	enum mem_type mtype;	/* memory csrow type */
-	enum edac_type edac_mode;	/* EDAC mode for this csrow */
+
 	struct mem_ctl_info *mci;	/* the parent */
 
 	struct kobject kobj;	/* sysfs kobject for this csrow */
-- 
1.7.8


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 4/6] edac: Don't initialize csrow's first_page & friends when not needed
  2012-03-07 11:40 [PATCH 0/6] Add a per-dimm structure Mauro Carvalho Chehab
                   ` (2 preceding siblings ...)
  2012-03-07 11:40 ` [PATCH 3/6] edac: move dimm properties to struct memset_info Mauro Carvalho Chehab
@ 2012-03-07 11:40 ` Mauro Carvalho Chehab
  2012-03-07 11:40 ` [PATCH 5/6] edac: move nr_pages to dimm struct Mauro Carvalho Chehab
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-07 11:40 UTC (permalink / raw)
  Cc: Mauro Carvalho Chehab, Linux Edac Mailing List,
	Linux Kernel Mailing List

Almost all edac	drivers	initialize first_page, last_page and
page_mask. Those vars are used inside the EDAC core, in	order to
calculate the csrow affected by	an error, by using the routine
edac_mc_find_csrow_by_page().

However, very few drivers actually use it:
        e752x_edac.c
        e7xxx_edac.c
        i3000_edac.c
        i82443bxgx_edac.c
        i82860_edac.c
        i82875p_edac.c
        i82975x_edac.c
        r82600_edac.c

There also a few other drivers that have their own calculus
formula internally using those vars.

All the others are just wasting time by initializing those
data.

While initializing data without using them won't cause any troubles, as
those information is stored at the wrong place (at csrows structure), it
is better to remove what is unused, in order to simplify the next patch.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
 drivers/edac/amd64_edac.c   |   38 ++------------------------------------
 drivers/edac/i3200_edac.c   |    5 -----
 drivers/edac/i5000_edac.c   |    5 -----
 drivers/edac/i5100_edac.c   |    2 --
 drivers/edac/i5400_edac.c   |    5 -----
 drivers/edac/i7300_edac.c   |    5 +----
 drivers/edac/i7core_edac.c  |    5 -----
 drivers/edac/mv64x60_edac.c |    1 -
 drivers/edac/ppc4xx_edac.c  |    7 -------
 drivers/edac/sb_edac.c      |    2 --
 drivers/edac/tile_edac.c    |    2 --
 drivers/edac/x38_edac.c     |    5 -----
 12 files changed, 3 insertions(+), 79 deletions(-)

diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 3e7bddc..b1b1551 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -715,25 +715,6 @@ static inline u64 input_addr_to_sys_addr(struct mem_ctl_info *mci,
 				     input_addr_to_dram_addr(mci, input_addr));
 }
 
-/*
- * Find the minimum and maximum InputAddr values that map to the given @csrow.
- * Pass back these values in *input_addr_min and *input_addr_max.
- */
-static void find_csrow_limits(struct mem_ctl_info *mci, int csrow,
-			      u64 *input_addr_min, u64 *input_addr_max)
-{
-	struct amd64_pvt *pvt;
-	u64 base, mask;
-
-	pvt = mci->pvt_info;
-	BUG_ON((csrow < 0) || (csrow >= pvt->csels[0].b_cnt));
-
-	get_cs_base_and_mask(pvt, csrow, 0, &base, &mask);
-
-	*input_addr_min = base & ~mask;
-	*input_addr_max = base | mask;
-}
-
 /* Map the Error address to a PAGE and PAGE OFFSET. */
 static inline void error_address_to_page_and_offset(u64 error_address,
 						    u32 *page, u32 *offset)
@@ -2166,7 +2147,7 @@ static int init_csrows(struct mem_ctl_info *mci)
 {
 	struct csrow_info *csrow;
 	struct amd64_pvt *pvt = mci->pvt_info;
-	u64 input_addr_min, input_addr_max, sys_addr, base, mask;
+	u64 base, mask;
 	u32 val;
 	int i, j, empty = 1;
 	enum mem_type mtype;
@@ -2194,14 +2175,7 @@ static int init_csrows(struct mem_ctl_info *mci)
 
 		empty = 0;
 		csrow->nr_pages = amd64_csrow_nr_pages(pvt, 0, i);
-		find_csrow_limits(mci, i, &input_addr_min, &input_addr_max);
-		sys_addr = input_addr_to_sys_addr(mci, input_addr_min);
-		csrow->first_page = (u32) (sys_addr >> PAGE_SHIFT);
-		sys_addr = input_addr_to_sys_addr(mci, input_addr_max);
-		csrow->last_page = (u32) (sys_addr >> PAGE_SHIFT);
-
 		get_cs_base_and_mask(pvt, i, 0, &base, &mask);
-		csrow->page_mask = ~mask;
 		/* 8 bytes of resolution */
 
 		mtype = amd64_determine_memory_type(pvt, i);
@@ -2221,15 +2195,7 @@ static int init_csrows(struct mem_ctl_info *mci)
 		}
 
 		debugf1("  for MC node %d csrow %d:\n", pvt->mc_node_id, i);
-		debugf1("    input_addr_min: 0x%lx input_addr_max: 0x%lx\n",
-			(unsigned long)input_addr_min,
-			(unsigned long)input_addr_max);
-		debugf1("    sys_addr: 0x%lx  page_mask: 0x%lx\n",
-			(unsigned long)sys_addr, csrow->page_mask);
-		debugf1("    nr_pages: %u  first_page: 0x%lx "
-			"last_page: 0x%lx\n",
-			(unsigned)csrow->nr_pages,
-			csrow->first_page, csrow->last_page);
+		debugf1("    nr_pages: %u\n", csrow->nr_pages);
 	}
 
 	return empty;
diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c
index 38d1e87..8086693 100644
--- a/drivers/edac/i3200_edac.c
+++ b/drivers/edac/i3200_edac.c
@@ -332,7 +332,6 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
 	int rc;
 	int i, j;
 	struct mem_ctl_info *mci = NULL;
-	unsigned long last_page;
 	u16 drbs[I3200_CHANNELS][I3200_RANKS_PER_CHANNEL];
 	bool stacked;
 	void __iomem *window;
@@ -377,7 +376,6 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
 	 * cumulative; the last one will contain the total memory
 	 * contained in all ranks.
 	 */
-	last_page = -1UL;
 	for (i = 0; i < mci->nr_csrows; i++) {
 		unsigned long nr_pages;
 		struct csrow_info *csrow = &mci->csrows[i];
@@ -389,9 +387,6 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
 		if (nr_pages == 0)
 			continue;
 
-		csrow->first_page = last_page + 1;
-		last_page += nr_pages;
-		csrow->last_page = last_page;
 		csrow->nr_pages = nr_pages;
 
 		for (j = 0; j < nr_channels; j++) {
diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c
index e612f1e..f00f684 100644
--- a/drivers/edac/i5000_edac.c
+++ b/drivers/edac/i5000_edac.c
@@ -1263,11 +1263,6 @@ static int i5000_init_csrows(struct mem_ctl_info *mci)
 		if (!MTR_DIMMS_PRESENT(mtr) && !MTR_DIMMS_PRESENT(mtr1))
 			continue;
 
-		/* FAKE OUT VALUES, FIXME */
-		p_csrow->first_page = 0 + csrow * 20;
-		p_csrow->last_page = 9 + csrow * 20;
-		p_csrow->page_mask = 0xFFF;
-
 		csrow_megs = 0;
 		for (channel = 0; channel < pvt->maxch; channel++) {
 			csrow_megs += pvt->dimm_info[csrow][channel].megabytes;
diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c
index 50e6d6a..6a53661 100644
--- a/drivers/edac/i5100_edac.c
+++ b/drivers/edac/i5100_edac.c
@@ -862,8 +862,6 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
 		 * FIXME: these two are totally bogus -- I don't see how to
 		 * map them correctly to this structure...
 		 */
-		mci->csrows[i].first_page = total_pages;
-		mci->csrows[i].last_page = total_pages + npages - 1;
 		mci->csrows[i].nr_pages = npages;
 		mci->csrows[i].csrow_idx = i;
 		mci->csrows[i].mci = mci;
diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c
index 229aff5..4a23813 100644
--- a/drivers/edac/i5400_edac.c
+++ b/drivers/edac/i5400_edac.c
@@ -1180,11 +1180,6 @@ static int i5400_init_csrows(struct mem_ctl_info *mci)
 		if (!MTR_DIMMS_PRESENT(mtr))
 			continue;
 
-		/* FAKE OUT VALUES, FIXME */
-		p_csrow->first_page = 0 + csrow * 20;
-		p_csrow->last_page = 9 + csrow * 20;
-		p_csrow->page_mask = 0xFFF;
-
 		csrow_megs = 0;
 		for (channel = 0; channel < pvt->maxch; channel++) {
 			csrow_megs += pvt->dimm_info[csrow][channel].megabytes;
diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c
index 07a5927..df6cd59 100644
--- a/drivers/edac/i7300_edac.c
+++ b/drivers/edac/i7300_edac.c
@@ -778,7 +778,7 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
 	int rc = -ENODEV;
 	int mtr;
 	int ch, branch, slot, channel;
-	u32 last_page = 0, nr_pages;
+	u32 nr_pages;
 	struct dimm_info *dimm;
 
 	pvt = mci->pvt_info;
@@ -828,9 +828,6 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
 
 				/* Update per_csrow memory count */
 				p_csrow->nr_pages += nr_pages;
-				p_csrow->first_page = last_page;
-				last_page += nr_pages;
-				p_csrow->last_page = last_page;
 
 				rc = 0;
 			}
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index e325314..dd45ef8 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -599,7 +599,6 @@ static int get_dimm_config(struct mem_ctl_info *mci)
 	struct pci_dev *pdev;
 	int i, j;
 	int csrow = 0;
-	unsigned long last_page = 0;
 	enum edac_type mode;
 	enum mem_type mtype;
 	struct dimm_info *dimm;
@@ -716,12 +715,8 @@ static int get_dimm_config(struct mem_ctl_info *mci)
 			npages = MiB_TO_PAGES(size);
 
 			csr = &mci->csrows[csrow];
-			csr->first_page = last_page + 1;
-			last_page += npages;
-			csr->last_page = last_page;
 			csr->nr_pages = npages;
 
-			csr->page_mask = 0;
 			csr->csrow_idx = csrow;
 			csr->nr_channels = 1;
 
diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c
index 12d7fe0..d2e3c39 100644
--- a/drivers/edac/mv64x60_edac.c
+++ b/drivers/edac/mv64x60_edac.c
@@ -668,7 +668,6 @@ static void mv64x60_init_csrows(struct mem_ctl_info *mci,
 	csrow = &mci->csrows[0];
 	dimm = csrow->channels[0].dimm;
 	csrow->nr_pages = pdata->total_mem >> PAGE_SHIFT;
-	csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
 	dimm->grain = 8;
 
 	dimm->mtype = (ctl & MV64X60_SDRAM_REGISTERED) ? MEM_RDDR : MEM_DDR;
diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c
index 00efc5f..13dbbbe 100644
--- a/drivers/edac/ppc4xx_edac.c
+++ b/drivers/edac/ppc4xx_edac.c
@@ -897,7 +897,6 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
 	enum edac_type edac_mode;
 	int row, j;
 	u32 mbxcf, size;
-	static u32 ppc4xx_last_page;
 
 	/* Establish the memory type and width */
 
@@ -959,10 +958,6 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
 			goto done;
 		}
 
-		csi->first_page = ppc4xx_last_page;
-		csi->last_page	= csi->first_page + csi->nr_pages - 1;
-		csi->page_mask	= 0;
-
 		/*
 		 * It's unclear exactly what grain should be set to
 		 * here. The SDRAM_ECCES register allows resolution of
@@ -984,8 +979,6 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
 			dimm->dtype	= dtype;
 
 			dimm->edac_mode	= edac_mode;
-
-		ppc4xx_last_page += csi->nr_pages;
 		}
 	}
 
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index 773a69c..b595628 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -641,8 +641,6 @@ static int get_dimm_config(struct mem_ctl_info *mci)
 				 * csrows.
 				 */
 				csr = &mci->csrows[csrow];
-				csr->first_page = last_page;
-				csr->last_page = last_page + npages - 1;
 				csr->nr_pages = npages;
 				csr->csrow_idx = csrow;
 				csr->nr_channels = 1;
diff --git a/drivers/edac/tile_edac.c b/drivers/edac/tile_edac.c
index db7d2ae..ba0917b 100644
--- a/drivers/edac/tile_edac.c
+++ b/drivers/edac/tile_edac.c
@@ -110,9 +110,7 @@ static int __devinit tile_edac_init_csrows(struct mem_ctl_info *mci)
 		return -1;
 	}
 
-	csrow->first_page = 0;
 	csrow->nr_pages = mem_info.mem_size >> PAGE_SHIFT;
-	csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
 	dimm->grain = TILE_EDAC_ERROR_GRAIN;
 	dimm->dtype = DEV_UNKNOWN;
 
diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c
index 52c8d69..7be10dd 100644
--- a/drivers/edac/x38_edac.c
+++ b/drivers/edac/x38_edac.c
@@ -319,7 +319,6 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
 	int rc;
 	int i, j;
 	struct mem_ctl_info *mci = NULL;
-	unsigned long last_page;
 	u16 drbs[X38_CHANNELS][X38_RANKS_PER_CHANNEL];
 	bool stacked;
 	void __iomem *window;
@@ -363,7 +362,6 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
 	 * cumulative; the last one will contain the total memory
 	 * contained in all ranks.
 	 */
-	last_page = -1UL;
 	for (i = 0; i < mci->nr_csrows; i++) {
 		unsigned long nr_pages;
 		struct csrow_info *csrow = &mci->csrows[i];
@@ -375,9 +373,6 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
 		if (nr_pages == 0)
 			continue;
 
-		csrow->first_page = last_page + 1;
-		last_page += nr_pages;
-		csrow->last_page = last_page;
 		csrow->nr_pages = nr_pages;
 
 		for (j = 0; j < x38_channel_num; j++) {
-- 
1.7.8


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 5/6] edac: move nr_pages to dimm struct
  2012-03-07 11:40 [PATCH 0/6] Add a per-dimm structure Mauro Carvalho Chehab
                   ` (3 preceding siblings ...)
  2012-03-07 11:40 ` [PATCH 4/6] edac: Don't initialize csrow's first_page & friends when not needed Mauro Carvalho Chehab
@ 2012-03-07 11:40 ` Mauro Carvalho Chehab
  2012-03-07 11:40 ` [PATCH 6/6] edac: Add per-dimm sysfs show nodes Mauro Carvalho Chehab
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-07 11:40 UTC (permalink / raw)
  Cc: Mauro Carvalho Chehab, Linux Edac Mailing List,
	Linux Kernel Mailing List

The number of pages is a dimm property. Move it to the dimm struct.

After this change, it is possible to add sysfs nodes for the DIMM's that
will properly represent the DIMM stick properties, including its size.

A TODO fix here is to properly represent dual-rank/quad-rank DIMMs when
the memory controller represents the memory via chip select rows.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
 drivers/edac/amd64_edac.c      |   15 ++++------
 drivers/edac/amd76x_edac.c     |    6 ++--
 drivers/edac/cell_edac.c       |    8 +++--
 drivers/edac/cpc925_edac.c     |    8 +++--
 drivers/edac/e752x_edac.c      |    6 +++-
 drivers/edac/e7xxx_edac.c      |    5 ++-
 drivers/edac/edac_mc.c         |   29 +++++++++++--------
 drivers/edac/edac_mc_sysfs.c   |   58 +++++++++++++++++++++++++++++++---------
 drivers/edac/i3000_edac.c      |    6 +++-
 drivers/edac/i3200_edac.c      |    3 +-
 drivers/edac/i5000_edac.c      |   14 +++++----
 drivers/edac/i5100_edac.c      |   16 ++++++++++-
 drivers/edac/i5400_edac.c      |    9 ++----
 drivers/edac/i7300_edac.c      |   22 ++++-----------
 drivers/edac/i7core_edac.c     |   13 +++-----
 drivers/edac/i82443bxgx_edac.c |    2 +-
 drivers/edac/i82860_edac.c     |    2 +-
 drivers/edac/i82875p_edac.c    |    5 ++-
 drivers/edac/i82975x_edac.c    |   11 +++++--
 drivers/edac/mpc85xx_edac.c    |    3 +-
 drivers/edac/mv64x60_edac.c    |    3 +-
 drivers/edac/pasemi_edac.c     |   14 +++++-----
 drivers/edac/ppc4xx_edac.c     |    5 ++-
 drivers/edac/r82600_edac.c     |    3 +-
 drivers/edac/sb_edac.c         |   12 +++-----
 drivers/edac/tile_edac.c       |    2 +-
 drivers/edac/x38_edac.c        |    4 +-
 include/linux/edac.h           |   10 ++++---
 28 files changed, 172 insertions(+), 122 deletions(-)

diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index b1b1551..377eed8 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -2126,14 +2126,8 @@ static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
 
 	nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT);
 
-	/*
-	 * If dual channel then double the memory size of single channel.
-	 * Channel count is 1 or 2
-	 */
-	nr_pages <<= (pvt->channel_count - 1);
-
 	debugf0("  (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode);
-	debugf0("    nr_pages= %u  channel-count = %d\n",
+	debugf0("    nr_pages/dimm= %u  channel-count = %d\n",
 		nr_pages, pvt->channel_count);
 
 	return nr_pages;
@@ -2152,6 +2146,7 @@ static int init_csrows(struct mem_ctl_info *mci)
 	int i, j, empty = 1;
 	enum mem_type mtype;
 	enum edac_type edac_mode;
+	int nr_pages;
 
 	amd64_read_pci_cfg(pvt->F3, NBCFG, &val);
 
@@ -2174,7 +2169,7 @@ static int init_csrows(struct mem_ctl_info *mci)
 			i, pvt->mc_node_id);
 
 		empty = 0;
-		csrow->nr_pages = amd64_csrow_nr_pages(pvt, 0, i);
+		nr_pages = amd64_csrow_nr_pages(pvt, 0, i);
 		get_cs_base_and_mask(pvt, i, 0, &base, &mask);
 		/* 8 bytes of resolution */
 
@@ -2192,10 +2187,12 @@ static int init_csrows(struct mem_ctl_info *mci)
 		for (j = 0; j < pvt->channel_count; j++) {
 			csrow->channels[j].dimm->mtype = mtype;
 			csrow->channels[j].dimm->edac_mode = edac_mode;
+			csrow->channels[j].dimm->nr_pages = nr_pages;
+
 		}
 
 		debugf1("  for MC node %d csrow %d:\n", pvt->mc_node_id, i);
-		debugf1("    nr_pages: %u\n", csrow->nr_pages);
+		debugf1("    nr_pages: %u\n", nr_pages);
 	}
 
 	return empty;
diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c
index 2a63ed0..1532750 100644
--- a/drivers/edac/amd76x_edac.c
+++ b/drivers/edac/amd76x_edac.c
@@ -205,10 +205,10 @@ static void amd76x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 		mba_mask = ((mba & 0xff80) << 16) | 0x7fffffUL;
 		pci_read_config_dword(pdev, AMD76X_DRAM_MODE_STATUS, &dms);
 		csrow->first_page = mba_base >> PAGE_SHIFT;
-		csrow->nr_pages = (mba_mask + 1) >> PAGE_SHIFT;
-		csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
+		dimm->nr_pages = (mba_mask + 1) >> PAGE_SHIFT;
+		csrow->last_page = csrow->first_page + dimm->nr_pages - 1;
 		csrow->page_mask = mba_mask >> PAGE_SHIFT;
-		dimm->grain = csrow->nr_pages << PAGE_SHIFT;
+		dimm->grain = dimm->nr_pages << PAGE_SHIFT;
 		dimm->mtype = MEM_RDDR;
 		dimm->dtype = ((dms >> index) & 0x1) ? DEV_X4 : DEV_UNKNOWN;
 		dimm->edac_mode = edac_mode;
diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c
index 94fbb12..09e1b5d 100644
--- a/drivers/edac/cell_edac.c
+++ b/drivers/edac/cell_edac.c
@@ -128,6 +128,7 @@ static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci)
 	struct cell_edac_priv		*priv = mci->pvt_info;
 	struct device_node		*np;
 	int				j;
+	u32				nr_pages;
 
 	for (np = NULL;
 	     (np = of_find_node_by_name(np, "memory")) != NULL;) {
@@ -142,19 +143,20 @@ static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci)
 		if (of_node_to_nid(np) != priv->node)
 			continue;
 		csrow->first_page = r.start >> PAGE_SHIFT;
-		csrow->nr_pages = resource_size(&r) >> PAGE_SHIFT;
-		csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
+		nr_pages = resource_size(&r) >> PAGE_SHIFT;
+		csrow->last_page = csrow->first_page + nr_pages - 1;
 
 		for (j = 0; j < csrow->nr_channels; j++) {
 			dimm = csrow->channels[j].dimm;
 			dimm->mtype = MEM_XDR;
 			dimm->edac_mode = EDAC_SECDED;
+			dimm->nr_pages = nr_pages / csrow->nr_channels;
 		}
 		dev_dbg(mci->dev,
 			"Initialized on node %d, chanmask=0x%x,"
 			" first_page=0x%lx, nr_pages=0x%x\n",
 			priv->node, priv->chanmask,
-			csrow->first_page, csrow->nr_pages);
+			csrow->first_page, dimm->nr_pages);
 		break;
 	}
 }
diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c
index ee90f3d..7b764a8 100644
--- a/drivers/edac/cpc925_edac.c
+++ b/drivers/edac/cpc925_edac.c
@@ -332,7 +332,7 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci)
 	struct dimm_info *dimm;
 	int index, j;
 	u32 mbmr, mbbar, bba;
-	unsigned long row_size, last_nr_pages = 0;
+	unsigned long row_size, nr_pages, last_nr_pages = 0;
 
 	get_total_mem(pdata);
 
@@ -351,12 +351,14 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci)
 
 		row_size = bba * (1UL << 28);	/* 256M */
 		csrow->first_page = last_nr_pages;
-		csrow->nr_pages = row_size >> PAGE_SHIFT;
-		csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
+		nr_pages = row_size >> PAGE_SHIFT;
+		csrow->last_page = csrow->first_page + nr_pages - 1;
 		last_nr_pages = csrow->last_page + 1;
 
 		for (j = 0; j < csrow->nr_channels; j++) {
 			dimm = csrow->channels[j].dimm;
+
+			dimm->nr_pages = nr_pages / csrow->nr_channels;
 			dimm->mtype = MEM_RDDR;
 			dimm->edac_mode = EDAC_SECDED;
 
diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c
index db291ea..310f657 100644
--- a/drivers/edac/e752x_edac.c
+++ b/drivers/edac/e752x_edac.c
@@ -1044,7 +1044,7 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 	int drc_drbg;		/* DRB granularity 0=64mb, 1=128mb */
 	int drc_ddim;		/* DRAM Data Integrity Mode 0=none, 2=edac */
 	u8 value;
-	u32 dra, drc, cumul_size, i;
+	u32 dra, drc, cumul_size, i, nr_pages;
 
 	dra = 0;
 	for (index = 0; index < 4; index++) {
@@ -1078,11 +1078,13 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 
 		csrow->first_page = last_cumul_size;
 		csrow->last_page = cumul_size - 1;
-		csrow->nr_pages = cumul_size - last_cumul_size;
+		nr_pages = cumul_size - last_cumul_size;
 		last_cumul_size = cumul_size;
 
 		for (i = 0; i < drc_chan + 1; i++) {
 			struct dimm_info *dimm = csrow->channels[i].dimm;
+
+			dimm->nr_pages = nr_pages / drc_chan;
 			dimm->grain = 1 << 12;	/* 4KiB - resolution of CELOG */
 			dimm->mtype = MEM_RDDR;	/* only one type supported */
 			dimm->dtype = mem_dev ? DEV_X4 : DEV_X8;
diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c
index 178d2af..2005d80 100644
--- a/drivers/edac/e7xxx_edac.c
+++ b/drivers/edac/e7xxx_edac.c
@@ -349,7 +349,7 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 	unsigned long last_cumul_size;
 	int index, j;
 	u8 value;
-	u32 dra, cumul_size;
+	u32 dra, cumul_size, nr_pages;
 	int drc_chan, drc_drbg, drc_ddim, mem_dev;
 	struct csrow_info *csrow;
 	struct dimm_info *dimm;
@@ -380,12 +380,13 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 
 		csrow->first_page = last_cumul_size;
 		csrow->last_page = cumul_size - 1;
-		csrow->nr_pages = cumul_size - last_cumul_size;
+		nr_pages = cumul_size - last_cumul_size;
 		last_cumul_size = cumul_size;
 
 		for (j = 0; j < drc_chan + 1; j++) {
 			dimm = csrow->channels[j].dimm;
 
+			dimm->nr_pages = nr_pages / drc_chan;
 			dimm->grain = 1 << 12;	/* 4KiB - resolution of CELOG */
 			dimm->mtype = MEM_RDDR;	/* only one type supported */
 			dimm->dtype = mem_dev ? DEV_X4 : DEV_X8;
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 7e857ea..c7a565a 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -44,22 +44,23 @@ static void edac_mc_dump_channel(struct rank_info *chan)
 {
 	debugf4("\tchannel = %p\n", chan);
 	debugf4("\tchannel->chan_idx = %d\n", chan->chan_idx);
-	debugf4("\tchannel->ce_count = %d\n", chan->dimm->ce_count);
-		debugf4("\tchannel->label = '%s'\n", chan->dimm->label);
 	debugf4("\tchannel->csrow = %p\n\n", chan->csrow);
+
+	debugf4("\tdimm->ce_count = %d\n", chan->dimm->ce_count);
+	debugf4("\tdimm->label = '%s'\n", chan->dimm->label);
+	debugf4("\tdimm->nr_pages = 0x%x\n", chan->dimm->nr_pages);
 }
 
 static void edac_mc_dump_csrow(struct csrow_info *csrow)
 {
 	debugf4("\tcsrow = %p\n", csrow);
 	debugf4("\tcsrow->csrow_idx = %d\n", csrow->csrow_idx);
-	debugf4("\tcsrow->first_page = 0x%lx\n", csrow->first_page);
-	debugf4("\tcsrow->last_page = 0x%lx\n", csrow->last_page);
-	debugf4("\tcsrow->page_mask = 0x%lx\n", csrow->page_mask);
-	debugf4("\tcsrow->nr_pages = 0x%x\n", csrow->nr_pages);
 	debugf4("\tcsrow->nr_channels = %d\n", csrow->nr_channels);
 	debugf4("\tcsrow->channels = %p\n", csrow->channels);
 	debugf4("\tcsrow->mci = %p\n\n", csrow->mci);
+	debugf4("\tcsrow->first_page = 0x%lx\n", csrow->first_page);
+	debugf4("\tcsrow->last_page = 0x%lx\n", csrow->last_page);
+	debugf4("\tcsrow->page_mask = 0x%lx\n", csrow->page_mask);
 }
 
 static void edac_mc_dump_mci(struct mem_ctl_info *mci)
@@ -653,15 +654,19 @@ static void edac_mc_scrub_block(unsigned long page, unsigned long offset,
 int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
 {
 	struct csrow_info *csrows = mci->csrows;
-	int row, i;
+	int row, i, j, n;
 
 	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)
+		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;
 
 		debugf3("MC%d: %s(): first(0x%lx) page(0x%lx) last(0x%lx) "
@@ -670,9 +675,9 @@ int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page)
 			csrow->page_mask);
 
 		if ((page >= csrow->first_page) &&
-		    (page <= csrow->last_page) &&
-		    ((page & csrow->page_mask) ==
-		     (csrow->first_page & csrow->page_mask))) {
+		(page <= csrow->last_page) &&
+		((page & csrow->page_mask) ==
+		(csrow->first_page & csrow->page_mask))) {
 			row = i;
 			break;
 		}
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index 80fdbf5..9bfc930 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -144,7 +144,13 @@ 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,
@@ -669,16 +675,17 @@ 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, csrow_idx, j;
 
 	for (total_pages = csrow_idx = 0; csrow_idx < mci->nr_csrows;
-		csrow_idx++) {
+	     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));
@@ -1084,10 +1091,15 @@ 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 n = 0;
+
 		csrow = &mci->csrows[i];
+		for (j = 0; j < csrow->nr_channels; j++) {
+			struct dimm_info *dimm = csrow->channels[j].dimm;
+			n += dimm->nr_pages;
+		}
 
-		/* Only expose populated CSROWs */
-		if (csrow->nr_pages > 0) {
+		if (n > 0) {
 			err = edac_create_csrow_object(mci, csrow, i);
 			if (err) {
 				debugf1("%s() failure: create csrow %d obj\n",
@@ -1101,6 +1113,9 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
 	 * Make directories for each DIMM object under the mc<id> kobject
 	 */
 	for (j = 0; j < mci->nr_dimms; j++) {
+		/* Only expose populated CSROWs */
+		if (mci->dimms[j].nr_pages == 0)
+			continue;
 		err = edac_create_dimm_object(mci, &mci->dimms[j] , j);
 		if (err) {
 			debugf1("%s() failure: create dimm %d obj\n",
@@ -1112,13 +1127,22 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci)
 	return 0;
 
 fail2:
-	for (j--; j >= 0; j--)
-		kobject_put(&mci->dimms[i].kobj);
+	for (j--; j >= 0; j--) {
+		if (mci->dimms[j].nr_pages)
+			kobject_put(&mci->dimms[i].kobj);
+	}
 
 	/* CSROW error: backout what has already been registered,  */
 fail1:
 	for (i--; i >= 0; i--) {
-		if (mci->csrows[i].nr_pages > 0)
+		int n = 0;
+
+		csrow = &mci->csrows[i];
+		for (j = 0; j < csrow->nr_channels; j++) {
+			struct dimm_info *dimm = csrow->channels[j].dimm;
+			n += dimm->nr_pages;
+		}
+		if (n > 0)
 			kobject_put(&mci->csrows[i].kobj);
 	}
 
@@ -1138,7 +1162,8 @@ 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__);
 
@@ -1149,7 +1174,14 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
 		kobject_put(&mci->dimms[i].kobj);
 	}
 	for (i = 0; i < mci->nr_csrows; i++) {
-		if (mci->csrows[i].nr_pages > 0) {
+		int n = 0;
+
+		csrow = &mci->csrows[i];
+		for (j = 0; j < csrow->nr_channels; j++) {
+			struct dimm_info *dimm = csrow->channels[j].dimm;
+			n += dimm->nr_pages;
+		}
+		if (n > 0) {
 			debugf0("%s()  unreg csrow-%d\n", __func__, i);
 			kobject_put(&mci->csrows[i].kobj);
 		}
diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c
index 1498c5f..bf8a230 100644
--- a/drivers/edac/i3000_edac.c
+++ b/drivers/edac/i3000_edac.c
@@ -306,7 +306,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
 	int rc;
 	int i, j;
 	struct mem_ctl_info *mci = NULL;
-	unsigned long last_cumul_size;
+	unsigned long last_cumul_size, nr_pages;
 	int interleaved, nr_channels;
 	unsigned char dra[I3000_RANKS / 2], drb[I3000_RANKS];
 	unsigned char *c0dra = dra, *c1dra = &dra[I3000_RANKS_PER_CHANNEL / 2];
@@ -391,11 +391,13 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
 
 		csrow->first_page = last_cumul_size;
 		csrow->last_page = cumul_size - 1;
-		csrow->nr_pages = cumul_size - last_cumul_size;
+		nr_pages = cumul_size - last_cumul_size;
 		last_cumul_size = cumul_size;
 
 		for (j = 0; j < nr_channels; j++) {
 			struct dimm_info *dimm = csrow->channels[j].dimm;
+
+			dimm->nr_pages = nr_pages / nr_channels;
 			dimm->grain = I3000_DEAP_GRAIN;
 			dimm->mtype = MEM_DDR2;
 			dimm->dtype = DEV_UNKNOWN;
diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c
index 8086693..b3dc867 100644
--- a/drivers/edac/i3200_edac.c
+++ b/drivers/edac/i3200_edac.c
@@ -387,11 +387,10 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
 		if (nr_pages == 0)
 			continue;
 
-		csrow->nr_pages = nr_pages;
-
 		for (j = 0; j < nr_channels; j++) {
 			struct dimm_info *dimm = csrow->channels[j].dimm;
 
+			dimm->nr_pages = nr_pages / nr_channels;
 			dimm->grain = nr_pages << PAGE_SHIFT;
 			dimm->mtype = MEM_DDR2;
 			dimm->dtype = DEV_UNKNOWN;
diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c
index f00f684..e8d32e8 100644
--- a/drivers/edac/i5000_edac.c
+++ b/drivers/edac/i5000_edac.c
@@ -1236,6 +1236,7 @@ static int i5000_init_csrows(struct mem_ctl_info *mci)
 {
 	struct i5000_pvt *pvt;
 	struct csrow_info *p_csrow;
+	struct dimm_info *dimm;
 	int empty, channel_count;
 	int max_csrows;
 	int mtr, mtr1;
@@ -1265,21 +1266,22 @@ static int i5000_init_csrows(struct mem_ctl_info *mci)
 
 		csrow_megs = 0;
 		for (channel = 0; channel < pvt->maxch; channel++) {
+			dimm = p_csrow->channels[channel].dimm;
 			csrow_megs += pvt->dimm_info[csrow][channel].megabytes;
-			p_csrow->channels[channel].dimm->grain = 8;
+			dimm->grain = 8;
 
 			/* Assume DDR2 for now */
-			p_csrow->channels[channel].dimm->mtype = MEM_FB_DDR2;
+			dimm->mtype = MEM_FB_DDR2;
 
 			/* ask what device type on this row */
 			if (MTR_DRAM_WIDTH(mtr))
-				p_csrow->channels[channel].dimm->dtype = DEV_X8;
+				dimm->dtype = DEV_X8;
 			else
-				p_csrow->channels[channel].dimm->dtype = DEV_X4;
+				dimm->dtype = DEV_X4;
 
-			p_csrow->channels[channel].dimm->edac_mode = EDAC_S8ECD8ED;
+			dimm->edac_mode = EDAC_S8ECD8ED;
+			dimm->nr_pages = (csrow_megs << 8) / pvt->maxch;
 		}
-		p_csrow->nr_pages = csrow_megs << 8;
 
 		empty = 0;
 	}
diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c
index 6a53661..d564d68 100644
--- a/drivers/edac/i5100_edac.c
+++ b/drivers/edac/i5100_edac.c
@@ -862,7 +862,6 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
 		 * FIXME: these two are totally bogus -- I don't see how to
 		 * map them correctly to this structure...
 		 */
-		mci->csrows[i].nr_pages = npages;
 		mci->csrows[i].csrow_idx = i;
 		mci->csrows[i].mci = mci;
 		mci->csrows[i].nr_channels = 1;
@@ -878,6 +877,21 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
 		snprintf(dimm->label, sizeof(dimm->label),
 			 "DIMM%u",
 			 i5100_rank_to_slot(mci, chan, rank));
+
+		dimm->nr_pages = npages;
+
+		if (npages) {
+			total_pages += npages;
+
+			dimm->grain = 32;
+			dimm->dtype = (priv->mtr[chan][rank].width == 4) ?
+				DEV_X4 : DEV_X8;
+			dimm->mtype = MEM_RDDR2;
+			dimm->edac_mode = EDAC_SECDED;
+			snprintf(dimm->label, sizeof(dimm->label),
+				"DIMM%u",
+				i5100_rank_to_slot(mci, chan, rank));
+		}
 	}
 }
 
diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c
index 4a23813..784d6dc 100644
--- a/drivers/edac/i5400_edac.c
+++ b/drivers/edac/i5400_edac.c
@@ -1156,7 +1156,7 @@ static int i5400_init_csrows(struct mem_ctl_info *mci)
 	int empty, channel_count;
 	int max_csrows;
 	int mtr;
-	int csrow_megs;
+	int size_mb;
 	int channel;
 	int csrow;
 	struct dimm_info *dimm;
@@ -1171,8 +1171,6 @@ static int i5400_init_csrows(struct mem_ctl_info *mci)
 	for (csrow = 0; csrow < max_csrows; csrow++) {
 		p_csrow = &mci->csrows[csrow];
 
-		p_csrow->csrow_idx = csrow;
-
 		/* use branch 0 for the basis */
 		mtr = determine_mtr(pvt, csrow, 0);
 
@@ -1180,12 +1178,11 @@ static int i5400_init_csrows(struct mem_ctl_info *mci)
 		if (!MTR_DIMMS_PRESENT(mtr))
 			continue;
 
-		csrow_megs = 0;
 		for (channel = 0; channel < pvt->maxch; channel++) {
-			csrow_megs += pvt->dimm_info[csrow][channel].megabytes;
+			size_mb = pvt->dimm_info[csrow][channel].megabytes;
 
-			p_csrow->nr_pages = csrow_megs << 8;
 			dimm = p_csrow->channels[channel].dimm;
+			dimm->nr_pages = size_mb << 8;
 			dimm->grain = 8;
 			dimm->dtype = MTR_DRAM_WIDTH(mtr) ? DEV_X8 : DEV_X4;
 			dimm->mtype = MEM_RDDR2;
diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c
index df6cd59..5e594ae 100644
--- a/drivers/edac/i7300_edac.c
+++ b/drivers/edac/i7300_edac.c
@@ -617,9 +617,7 @@ static void i7300_enable_error_reporting(struct mem_ctl_info *mci)
 static int decode_mtr(struct i7300_pvt *pvt,
 		      int slot, int ch, int branch,
 		      struct i7300_dimm_info *dinfo,
-		      struct csrow_info *p_csrow,
-		      struct dimm_info *dimm,
-		      u32 *nr_pages)
+		      struct dimm_info *dimm)
 {
 	int mtr, ans, addrBits, channel;
 
@@ -651,7 +649,6 @@ static int decode_mtr(struct i7300_pvt *pvt,
 	addrBits -= 3;	/* 8 bits per bytes */
 
 	dinfo->megabytes = 1 << addrBits;
-	*nr_pages = dinfo->megabytes << 8;
 
 	debugf2("\t\tWIDTH: x%d\n", MTR_DRAM_WIDTH(mtr));
 
@@ -664,8 +661,6 @@ static int decode_mtr(struct i7300_pvt *pvt,
 	debugf2("\t\tNUMCOL: %s\n", numcol_toString[MTR_DIMM_COLS(mtr)]);
 	debugf2("\t\tSIZE: %d MB\n", dinfo->megabytes);
 
-	p_csrow->csrow_idx = slot;
-
 	/*
 	 * The type of error detection actually depends of the
 	 * mode of operation. When it is just one single memory chip, at
@@ -675,6 +670,7 @@ static int decode_mtr(struct i7300_pvt *pvt,
 	 * See datasheet Sections 7.3.6 to 7.3.8
 	 */
 
+	dimm->nr_pages = MiB_TO_PAGES(dinfo->megabytes);
 	dimm->grain = 8;
 	dimm->mtype = MEM_FB_DDR2;
 	if (IS_SINGLE_MODE(pvt->mc_settings_a)) {
@@ -774,11 +770,9 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
 {
 	struct i7300_pvt *pvt;
 	struct i7300_dimm_info *dinfo;
-	struct csrow_info *p_csrow;
 	int rc = -ENODEV;
 	int mtr;
 	int ch, branch, slot, channel;
-	u32 nr_pages;
 	struct dimm_info *dimm;
 
 	pvt = mci->pvt_info;
@@ -804,7 +798,6 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
 	}
 
 	/* Get the set of MTR[0-7] regs by each branch */
-	nr_pages = 0;
 	for (slot = 0; slot < MAX_SLOTS; slot++) {
 		int where = mtr_regs[slot];
 		for (branch = 0; branch < MAX_BRANCHES; branch++) {
@@ -815,21 +808,18 @@ static int i7300_init_csrows(struct mem_ctl_info *mci)
 				int channel = to_channel(ch, branch);
 
 				dinfo = &pvt->dimm_info[slot][channel];
-				p_csrow = &mci->csrows[slot];
 
-				dimm = p_csrow->channels[branch * MAX_CH_PER_BRANCH + ch].dimm;
+				dimm = mci->csrows[slot].channels[branch * MAX_CH_PER_BRANCH + ch].dimm;
 
 				mtr = decode_mtr(pvt, slot, ch, branch,
-						 dinfo, p_csrow, dimm,
-						 &nr_pages);
+						 dinfo, dimm);
+
 				/* if no DIMMS on this row, continue */
 				if (!MTR_DIMMS_PRESENT(mtr))
 					continue;
 
-				/* Update per_csrow memory count */
-				p_csrow->nr_pages += nr_pages;
-
 				rc = 0;
+
 			}
 		}
 	}
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index dd45ef8..1a7246b 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -601,7 +601,6 @@ static int get_dimm_config(struct mem_ctl_info *mci)
 	int csrow = 0;
 	enum edac_type mode;
 	enum mem_type mtype;
-	struct dimm_info *dimm;
 
 	/* Get data from the MC register, function 0 */
 	pdev = pvt->pci_mcr[0];
@@ -690,6 +689,7 @@ static int get_dimm_config(struct mem_ctl_info *mci)
 			(data & REGISTERED_DIMM) ? 'R' : 'U');
 
 		for (j = 0; j < 3; j++) {
+			struct dimm_info *dimm = &mci->dimms[i * 3 + j];
 			u32 banks, ranks, rows, cols;
 			u32 size, npages;
 
@@ -715,17 +715,13 @@ static int get_dimm_config(struct mem_ctl_info *mci)
 			npages = MiB_TO_PAGES(size);
 
 			csr = &mci->csrows[csrow];
-			csr->nr_pages = npages;
-
-			csr->csrow_idx = csrow;
-			csr->nr_channels = 1;
-
-			csr->channels[0].chan_idx = i;
-			csr->channels[0].ce_count = 0;
+			csr->channels[0].dimm = dimm;
 
 			pvt->csrow_map[i][j] = csrow;
 
 			dimm = csr->channels[0].dimm;
+			dimm->nr_pages = npages;
+
 			switch (banks) {
 			case 4:
 				dimm->dtype = DEV_X4;
@@ -746,6 +742,7 @@ static int get_dimm_config(struct mem_ctl_info *mci)
 			dimm->grain = 8;
 			dimm->edac_mode = mode;
 			dimm->mtype = mtype;
+			csrow++;
 		}
 
 		pci_read_config_dword(pdev, MC_SAG_CH_0, &value[0]);
diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c
index 1e19492..74166ae 100644
--- a/drivers/edac/i82443bxgx_edac.c
+++ b/drivers/edac/i82443bxgx_edac.c
@@ -220,7 +220,7 @@ static void i82443bxgx_init_csrows(struct mem_ctl_info *mci,
 		row_base = row_high_limit_last;
 		csrow->first_page = row_base >> PAGE_SHIFT;
 		csrow->last_page = (row_high_limit >> PAGE_SHIFT) - 1;
-		csrow->nr_pages = csrow->last_page - csrow->first_page + 1;
+		dimm->nr_pages = csrow->last_page - csrow->first_page + 1;
 		/* EAP reports in 4kilobyte granularity [61] */
 		dimm->grain = 1 << 12;
 		dimm->mtype = mtype;
diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c
index acbd924..48e0ecd 100644
--- a/drivers/edac/i82860_edac.c
+++ b/drivers/edac/i82860_edac.c
@@ -167,7 +167,7 @@ static void i82860_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev)
 
 		csrow->first_page = last_cumul_size;
 		csrow->last_page = cumul_size - 1;
-		csrow->nr_pages = cumul_size - last_cumul_size;
+		dimm->nr_pages = cumul_size - last_cumul_size;
 		last_cumul_size = cumul_size;
 		dimm->grain = 1 << 12;	/* I82860_EAP has 4KiB reolution */
 		dimm->mtype = MEM_RMBS;
diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c
index 81f79e2..dc207dc 100644
--- a/drivers/edac/i82875p_edac.c
+++ b/drivers/edac/i82875p_edac.c
@@ -347,7 +347,7 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci,
 	unsigned long last_cumul_size;
 	u8 value;
 	u32 drc_ddim;		/* DRAM Data Integrity Mode 0=none,2=edac */
-	u32 cumul_size;
+	u32 cumul_size, nr_pages;
 	int index, j;
 
 	drc_ddim = (drc >> 18) & 0x1;
@@ -371,12 +371,13 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci,
 
 		csrow->first_page = last_cumul_size;
 		csrow->last_page = cumul_size - 1;
-		csrow->nr_pages = cumul_size - last_cumul_size;
+		nr_pages = cumul_size - last_cumul_size;
 		last_cumul_size = cumul_size;
 
 		for (j = 0; j < nr_chans; j++) {
 			dimm = csrow->channels[j].dimm;
 
+			dimm->nr_pages = nr_pages / nr_chans;
 			dimm->grain = 1 << 12;	/* I82875P_EAP has 4KiB reolution */
 			dimm->mtype = MEM_DDR;
 			dimm->dtype = DEV_UNKNOWN;
diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c
index d29effb..d7dc455 100644
--- a/drivers/edac/i82975x_edac.c
+++ b/drivers/edac/i82975x_edac.c
@@ -362,7 +362,7 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
 	struct csrow_info *csrow;
 	unsigned long last_cumul_size;
 	u8 value;
-	u32 cumul_size;
+	u32 cumul_size, nr_pages;
 	int index, chan;
 	struct dimm_info *dimm;
 	enum dev_type dtype;
@@ -394,6 +394,7 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
 		debugf3("%s(): (%d) cumul_size 0x%x\n", __func__, index,
 			cumul_size);
 
+		nr_pages = cumul_size - last_cumul_size;
 		/*
 		 * Initialise dram labels
 		 * index values:
@@ -403,6 +404,11 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
 		dtype = i82975x_dram_type(mch_window, index);
 		for (chan = 0; chan < csrow->nr_channels; chan++) {
 			dimm = mci->csrows[index].channels[chan].dimm;
+
+			if (!nr_pages)
+				continue;
+
+			dimm->nr_pages = nr_pages / csrow->nr_channels;
 			strncpy(csrow->channels[chan].dimm->label,
 					labels[(index >> 1) + (chan * 2)],
 					EDAC_MC_LABEL_LEN);
@@ -412,12 +418,11 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci,
 			dimm->edac_mode = EDAC_SECDED; /* only supported */
 		}
 
-		if (cumul_size == last_cumul_size)
+		if (!nr_pages)
 			continue;	/* not populated */
 
 		csrow->first_page = last_cumul_size;
 		csrow->last_page = cumul_size - 1;
-		csrow->nr_pages = cumul_size - last_cumul_size;
 		last_cumul_size = cumul_size;
 	}
 }
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
index fb92916..c1d9e15 100644
--- a/drivers/edac/mpc85xx_edac.c
+++ b/drivers/edac/mpc85xx_edac.c
@@ -947,7 +947,8 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci)
 
 		csrow->first_page = start;
 		csrow->last_page = end;
-		csrow->nr_pages = end + 1 - start;
+
+		dimm->nr_pages = end + 1 - start;
 		dimm->grain = 8;
 		dimm->mtype = mtype;
 		dimm->dtype = DEV_UNKNOWN;
diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c
index d2e3c39..281e245 100644
--- a/drivers/edac/mv64x60_edac.c
+++ b/drivers/edac/mv64x60_edac.c
@@ -667,7 +667,8 @@ static void mv64x60_init_csrows(struct mem_ctl_info *mci,
 
 	csrow = &mci->csrows[0];
 	dimm = csrow->channels[0].dimm;
-	csrow->nr_pages = pdata->total_mem >> PAGE_SHIFT;
+
+	dimm->nr_pages = pdata->total_mem >> PAGE_SHIFT;
 	dimm->grain = 8;
 
 	dimm->mtype = (ctl & MV64X60_SDRAM_REGISTERED) ? MEM_RDDR : MEM_DDR;
diff --git a/drivers/edac/pasemi_edac.c b/drivers/edac/pasemi_edac.c
index 4e53270..3fcefda 100644
--- a/drivers/edac/pasemi_edac.c
+++ b/drivers/edac/pasemi_edac.c
@@ -153,20 +153,20 @@ static int pasemi_edac_init_csrows(struct mem_ctl_info *mci,
 		switch ((rankcfg & MCDRAM_RANKCFG_TYPE_SIZE_M) >>
 			MCDRAM_RANKCFG_TYPE_SIZE_S) {
 		case 0:
-			csrow->nr_pages = 128 << (20 - PAGE_SHIFT);
+			dimm->nr_pages = 128 << (20 - PAGE_SHIFT);
 			break;
 		case 1:
-			csrow->nr_pages = 256 << (20 - PAGE_SHIFT);
+			dimm->nr_pages = 256 << (20 - PAGE_SHIFT);
 			break;
 		case 2:
 		case 3:
-			csrow->nr_pages = 512 << (20 - PAGE_SHIFT);
+			dimm->nr_pages = 512 << (20 - PAGE_SHIFT);
 			break;
 		case 4:
-			csrow->nr_pages = 1024 << (20 - PAGE_SHIFT);
+			dimm->nr_pages = 1024 << (20 - PAGE_SHIFT);
 			break;
 		case 5:
-			csrow->nr_pages = 2048 << (20 - PAGE_SHIFT);
+			dimm->nr_pages = 2048 << (20 - PAGE_SHIFT);
 			break;
 		default:
 			edac_mc_printk(mci, KERN_ERR,
@@ -176,8 +176,8 @@ static int pasemi_edac_init_csrows(struct mem_ctl_info *mci,
 		}
 
 		csrow->first_page = last_page_in_mmc;
-		csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
-		last_page_in_mmc += csrow->nr_pages;
+		csrow->last_page = csrow->first_page + dimm->nr_pages - 1;
+		last_page_in_mmc += dimm->nr_pages;
 		csrow->page_mask = 0;
 		dimm->grain = PASEMI_EDAC_ERROR_GRAIN;
 		dimm->mtype = MEM_DDR;
diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c
index 13dbbbe..1adaddf 100644
--- a/drivers/edac/ppc4xx_edac.c
+++ b/drivers/edac/ppc4xx_edac.c
@@ -896,7 +896,7 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
 	enum dev_type dtype;
 	enum edac_type edac_mode;
 	int row, j;
-	u32 mbxcf, size;
+	u32 mbxcf, size, nr_pages;
 
 	/* Establish the memory type and width */
 
@@ -947,7 +947,7 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
 		case SDRAM_MBCF_SZ_2GB:
 		case SDRAM_MBCF_SZ_4GB:
 		case SDRAM_MBCF_SZ_8GB:
-			csi->nr_pages = SDRAM_MBCF_SZ_TO_PAGES(size);
+			nr_pages = SDRAM_MBCF_SZ_TO_PAGES(size);
 			break;
 		default:
 			ppc4xx_edac_mc_printk(KERN_ERR, mci,
@@ -973,6 +973,7 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
 		for (j = 0; j < csi->nr_channels; j++) {
 			struct dimm_info *dimm = csi->channels[j].dimm;
 
+			dimm->nr_pages  = nr_pages;
 			dimm->grain	= 1;
 
 			dimm->mtype	= mtype;
diff --git a/drivers/edac/r82600_edac.c b/drivers/edac/r82600_edac.c
index c8b774d..a4b0626 100644
--- a/drivers/edac/r82600_edac.c
+++ b/drivers/edac/r82600_edac.c
@@ -249,7 +249,8 @@ static void r82600_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev,
 
 		csrow->first_page = row_base >> PAGE_SHIFT;
 		csrow->last_page = (row_high_limit >> PAGE_SHIFT) - 1;
-		csrow->nr_pages = csrow->last_page - csrow->first_page + 1;
+
+		dimm->nr_pages = csrow->last_page - csrow->first_page + 1;
 		/* Error address is top 19 bits - so granularity is      *
 		 * 14 bits                                               */
 		dimm->grain = 1 << 14;
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index b595628..981262b 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -560,7 +560,6 @@ static int get_dimm_config(struct mem_ctl_info *mci)
 	u32 reg;
 	enum edac_type mode;
 	enum mem_type mtype;
-	struct dimm_info *dimm;
 
 	pci_read_config_dword(pvt->pci_br, SAD_TARGET, &reg);
 	pvt->sbridge_dev->source_id = SOURCE_ID(reg);
@@ -612,11 +611,11 @@ static int get_dimm_config(struct mem_ctl_info *mci)
 	/* On all supported DDR3 DIMM types, there are 8 banks available */
 	banks = 8;
 
-	dimm = mci->dimms;
 	for (i = 0; i < NUM_CHANNELS; i++) {
 		u32 mtr;
 
 		for (j = 0; j < ARRAY_SIZE(mtr_regs); j++) {
+			struct dimm_info *dimm = &mci->dimms[j];
 			pci_read_config_dword(pvt->pci_tad[i],
 					      mtr_regs[j], &mtr);
 			debugf4("Channel #%d  MTR%d = %x\n", i, j, mtr);
@@ -641,17 +640,14 @@ static int get_dimm_config(struct mem_ctl_info *mci)
 				 * csrows.
 				 */
 				csr = &mci->csrows[csrow];
-				csr->nr_pages = npages;
-				csr->csrow_idx = csrow;
-				csr->nr_channels = 1;
-				csr->channels[0].chan_idx = i;
 				pvt->csrow_map[i][j] = csrow;
 				last_page += npages;
 				csrow++;
 
 				csr->channels[0].dimm = dimm;
-				dimm->location.mc_channel = i;
-				dimm->location.mc_dimm_number = j;
+				dimm->nr_pages = npages;
+				dimm->mc_channel = i;
+				dimm->mc_dimm_number = j;
 				dimm->grain = 32;
 				dimm->dtype = (banks == 8) ? DEV_X8 : DEV_X4;
 				dimm->mtype = mtype;
diff --git a/drivers/edac/tile_edac.c b/drivers/edac/tile_edac.c
index ba0917b..6314ff9 100644
--- a/drivers/edac/tile_edac.c
+++ b/drivers/edac/tile_edac.c
@@ -110,7 +110,7 @@ static int __devinit tile_edac_init_csrows(struct mem_ctl_info *mci)
 		return -1;
 	}
 
-	csrow->nr_pages = mem_info.mem_size >> PAGE_SHIFT;
+	dimm->nr_pages = mem_info.mem_size >> PAGE_SHIFT;
 	dimm->grain = TILE_EDAC_ERROR_GRAIN;
 	dimm->dtype = DEV_UNKNOWN;
 
diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c
index 7be10dd..0de288f 100644
--- a/drivers/edac/x38_edac.c
+++ b/drivers/edac/x38_edac.c
@@ -373,10 +373,10 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx)
 		if (nr_pages == 0)
 			continue;
 
-		csrow->nr_pages = nr_pages;
-
 		for (j = 0; j < x38_channel_num; j++) {
 			struct dimm_info *dimm = csrow->channels[j].dimm;
+
+			dimm->nr_pages = nr_pages / x38_channel_num;
 			dimm->grain = nr_pages << PAGE_SHIFT;
 			dimm->mtype = MEM_DDR2;
 			dimm->dtype = DEV_UNKNOWN;
diff --git a/include/linux/edac.h b/include/linux/edac.h
index 26b9a58..e9c376a 100644
--- a/include/linux/edac.h
+++ b/include/linux/edac.h
@@ -349,6 +349,8 @@ struct dimm_info {
 	enum mem_type mtype;	/* memory dimm type */
 	enum edac_type edac_mode;	/* EDAC mode for this dimm */
 
+	u32 nr_pages;			/* number of pages in csrow */
+
 	u32 ce_count;		/* Correctable Errors for this dimm */
 };
 
@@ -375,13 +377,13 @@ struct rank_info {
 };
 
 struct csrow_info {
+	int csrow_idx;			/* the chip-select row */
+
+	/* Used only by edac_mc_find_csrow_by_page() */
 	unsigned long first_page;	/* first page number in csrow */
 	unsigned long last_page;	/* last page number in csrow */
-	u32 nr_pages;			/* number of pages in csrow */
 	unsigned long page_mask;	/* used for interleaving -
-					 * 0UL for non intlv
-					 */
-	int csrow_idx;			/* the chip-select row */
+					 * 0UL for non intlv */
 
 	u32 ue_count;		/* Uncorrectable Errors for this csrow */
 	u32 ce_count;		/* Correctable Errors for this csrow */
-- 
1.7.8


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 6/6] edac: Add per-dimm sysfs show nodes
  2012-03-07 11:40 [PATCH 0/6] Add a per-dimm structure Mauro Carvalho Chehab
                   ` (4 preceding siblings ...)
  2012-03-07 11:40 ` [PATCH 5/6] edac: move nr_pages to dimm struct Mauro Carvalho Chehab
@ 2012-03-07 11:40 ` Mauro Carvalho Chehab
  2012-03-08 21:57 ` [PATCH 0/6] Add a per-dimm structure Borislav Petkov
  2012-03-13 23:32 ` Greg KH
  7 siblings, 0 replies; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-07 11:40 UTC (permalink / raw)
  Cc: Mauro Carvalho Chehab, Linux Edac Mailing List,
	Linux Kernel Mailing List

Add sysfs nodes to describe DIMM properties: size, memory type,
dev type and edac mode.

With this change, the physical memory characteristics of the dimm stick
is now properly presented, as detected by the memory controller.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
 drivers/edac/edac_mc_sysfs.c |   36 ++++++++++++++++--
 include/linux/edac.h         |   82 +++++++++++++++++++++++++++++------------
 2 files changed, 90 insertions(+), 28 deletions(-)

diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index 9bfc930..875cd01d 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -498,14 +498,42 @@ static ssize_t dimmdev_label_store(struct dimm_info *dimm,
 	return max_size;
 }
 
+static ssize_t dimmdev_size_show(struct dimm_info *dimm, char *data)
+{
+	return sprintf(data, "%u\n", PAGES_TO_MiB(dimm->nr_pages));
+}
+
+static ssize_t dimmdev_mem_type_show(struct dimm_info *dimm, char *data)
+{
+	return sprintf(data, "%s\n", mem_types[dimm->mtype]);
+}
+
+static ssize_t dimmdev_dev_type_show(struct dimm_info *dimm, char *data)
+{
+	return sprintf(data, "%s\n", dev_types[dimm->dtype]);
+}
+
+static ssize_t dimmdev_edac_mode_show(struct dimm_info *dimm, char *data)
+{
+	return sprintf(data, "%s\n", edac_caps[dimm->edac_mode]);
+}
+
 /* default cwrow<id>/attribute files */
-DIMMDEV_ATTR(label, S_IRUGO | S_IWUSR, dimmdev_label_show, dimmdev_label_store);
-DIMMDEV_ATTR(location, S_IRUGO, dimmdev_location_show, NULL);
+DIMMDEV_ATTR(dimm_label, S_IRUGO | S_IWUSR, dimmdev_label_show, dimmdev_label_store);
+DIMMDEV_ATTR(dimm_location, S_IRUGO, dimmdev_location_show, NULL);
+DIMMDEV_ATTR(dimm_size, S_IRUGO, dimmdev_size_show, NULL);
+DIMMDEV_ATTR(dimm_mem_type, S_IRUGO, dimmdev_mem_type_show, NULL);
+DIMMDEV_ATTR(dimm_dev_type, S_IRUGO, dimmdev_dev_type_show, NULL);
+DIMMDEV_ATTR(dimm_edac_mode, S_IRUGO, dimmdev_edac_mode_show, NULL);
 
 /* default attributes of the DIMM<id> object */
 static struct dimmdev_attribute *default_dimm_attr[] = {
-	&attr_label,
-	&attr_location,
+	&attr_dimm_label,
+	&attr_dimm_location,
+	&attr_dimm_size,
+	&attr_dimm_mem_type,
+	&attr_dimm_dev_type,
+	&attr_dimm_edac_mode,
 	NULL,
 };
 
diff --git a/include/linux/edac.h b/include/linux/edac.h
index e9c376a..1862a4b 100644
--- a/include/linux/edac.h
+++ b/include/linux/edac.h
@@ -45,7 +45,19 @@ static inline void opstate_init(void)
 #define EDAC_MC_LABEL_LEN	31
 #define MC_PROC_NAME_MAX_LEN	7
 
-/* memory devices */
+/**
+ * enum dev_type - describe the type of memory DRAM chips used at the stick
+ * @DEV_UNKNOWN:	Can't be determined, or MC doesn't support detect it
+ * @DEV_X1:		1 bit for data
+ * @DEV_X2:		2 bits for data
+ * @DEV_X4:		4 bits for data
+ * @DEV_X8:		8 bits for data
+ * @DEV_X16:		16 bits for data
+ * @DEV_X32:		32 bits for data
+ * @DEV_X64:		64 bits for data
+ *
+ * Typical values are x4 and x8.
+ */
 enum dev_type {
 	DEV_UNKNOWN = 0,
 	DEV_X1,
@@ -67,7 +79,7 @@ enum dev_type {
 #define DEV_FLAG_X64		BIT(DEV_X64)
 
 /**
- * enum mem_type - memory types
+ * enum mem_type - Type of the memory stick
  *
  * @MEM_EMPTY		Empty csrow
  * @MEM_RESERVED:	Reserved csrow type
@@ -171,18 +183,29 @@ enum mem_type {
 #define MEM_FLAG_DDR3		 BIT(MEM_DDR3)
 #define MEM_FLAG_RDDR3		 BIT(MEM_RDDR3)
 
-/* chipset Error Detection and Correction capabilities and mode */
+/** enum edac-type - Error Detection and Correction capabilities and mode
+ * @EDAC_UNKNOWN:	Unknown if ECC is available
+ * @EDAC_NONE:		Doesn't support ECC
+ * @EDAC_RESERVED:	Reserved ECC type
+ * @EDAC_PARITY:	Detects parity errors
+ * @EDAC_EC:		Error Checking - no correction
+ * @EDAC_SECDED:	Single bit error correction, Double detection
+ * @EDAC_S2ECD2ED:	Chipkill x2 devices - do these exist?
+ * @EDAC_S4ECD4ED:	Chipkill x4 devices
+ * @EDAC_S8ECD8ED:	Chipkill x8 devices
+ * @EDAC_S16ECD16ED:	Chipkill x16 devices
+ */
 enum edac_type {
-	EDAC_UNKNOWN = 0,	/* Unknown if ECC is available */
-	EDAC_NONE,		/* Doesn't support ECC */
-	EDAC_RESERVED,		/* Reserved ECC type */
-	EDAC_PARITY,		/* Detects parity errors */
-	EDAC_EC,		/* Error Checking - no correction */
-	EDAC_SECDED,		/* Single bit error correction, Double detection */
-	EDAC_S2ECD2ED,		/* Chipkill x2 devices - do these exist? */
-	EDAC_S4ECD4ED,		/* Chipkill x4 devices */
-	EDAC_S8ECD8ED,		/* Chipkill x8 devices */
-	EDAC_S16ECD16ED,	/* Chipkill x16 devices */
+	EDAC_UNKNOWN =	0,
+	EDAC_NONE,
+	EDAC_RESERVED,
+	EDAC_PARITY,
+	EDAC_EC,
+	EDAC_SECDED,
+	EDAC_S2ECD2ED,
+	EDAC_S4ECD4ED,
+	EDAC_S8ECD8ED,
+	EDAC_S16ECD16ED,
 };
 
 #define EDAC_FLAG_UNKNOWN	BIT(EDAC_UNKNOWN)
@@ -195,18 +218,29 @@ enum edac_type {
 #define EDAC_FLAG_S8ECD8ED	BIT(EDAC_S8ECD8ED)
 #define EDAC_FLAG_S16ECD16ED	BIT(EDAC_S16ECD16ED)
 
-/* scrubbing capabilities */
+/** enum scrub_type - scrubbing capabilities
+ * @SCRUB_UNKNOWN		Unknown if scrubber is available
+ * @SCRUB_NONE:			No scrubber
+ * @SCRUB_SW_PROG:		SW progressive (sequential) scrubbing
+ * @SCRUB_SW_SRC:		Software scrub only errors
+ * @SCRUB_SW_PROG_SRC:		Progressive software scrub from an error
+ * @SCRUB_SW_TUNABLE:		Software scrub frequency is tunable
+ * @SCRUB_HW_PROG:		HW progressive (sequential) scrubbing
+ * @SCRUB_HW_SRC:		Hardware scrub only errors
+ * @SCRUB_HW_PROG_SRC:		Progressive hardware scrub from an error
+ * SCRUB_HW_TUNABLE:		Hardware scrub frequency is tunable
+ */
 enum scrub_type {
-	SCRUB_UNKNOWN = 0,	/* Unknown if scrubber is available */
-	SCRUB_NONE,		/* No scrubber */
-	SCRUB_SW_PROG,		/* SW progressive (sequential) scrubbing */
-	SCRUB_SW_SRC,		/* Software scrub only errors */
-	SCRUB_SW_PROG_SRC,	/* Progressive software scrub from an error */
-	SCRUB_SW_TUNABLE,	/* Software scrub frequency is tunable */
-	SCRUB_HW_PROG,		/* HW progressive (sequential) scrubbing */
-	SCRUB_HW_SRC,		/* Hardware scrub only errors */
-	SCRUB_HW_PROG_SRC,	/* Progressive hardware scrub from an error */
-	SCRUB_HW_TUNABLE	/* Hardware scrub frequency is tunable */
+	SCRUB_UNKNOWN =	0,
+	SCRUB_NONE,
+	SCRUB_SW_PROG,
+	SCRUB_SW_SRC,
+	SCRUB_SW_PROG_SRC,
+	SCRUB_SW_TUNABLE,
+	SCRUB_HW_PROG,
+	SCRUB_HW_SRC,
+	SCRUB_HW_PROG_SRC,
+	SCRUB_HW_TUNABLE
 };
 
 #define SCRUB_FLAG_SW_PROG	BIT(SCRUB_SW_PROG)
-- 
1.7.8


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-07 11:40 [PATCH 0/6] Add a per-dimm structure Mauro Carvalho Chehab
                   ` (5 preceding siblings ...)
  2012-03-07 11:40 ` [PATCH 6/6] edac: Add per-dimm sysfs show nodes Mauro Carvalho Chehab
@ 2012-03-08 21:57 ` Borislav Petkov
  2012-03-09 10:32   ` Mauro Carvalho Chehab
  2012-03-13 23:32 ` Greg KH
  7 siblings, 1 reply; 41+ messages in thread
From: Borislav Petkov @ 2012-03-08 21:57 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: Linux Edac Mailing List, Linux Kernel Mailing List

On Wed, Mar 07, 2012 at 08:40:32AM -0300, Mauro Carvalho Chehab wrote:
> Prepare the internal structures to represent the memory properties per dimm, 
> instead of per csrow. 
> 
> This is needed for modern controllers with more than 2 channels, as the memories 
> at the same slot number but on different channels (or channel pairs) may be 
> different.

Ok, so I this thing looks pretty fishy to me. I've booted it on a box which has
the following config on the first memory controller:

[   12.058897] EDAC MC: DCT0 chip selects:
[   12.063091] EDAC amd64: MC: 0:  2048MB 1:  2048MB
[   12.068155] EDAC amd64: MC: 2:  2048MB 3:  2048MB
[   12.073219] EDAC amd64: MC: 4:     0MB 5:     0MB
[   12.078281] EDAC amd64: MC: 6:     0MB 7:     0MB
[   12.093305] EDAC MC: DCT1 chip selects:
[   12.097499] EDAC amd64: MC: 0:  2048MB 1:  2048MB
[   12.102562] EDAC amd64: MC: 2:  2048MB 3:  2048MB
[   12.107623] EDAC amd64: MC: 4:     0MB 5:     0MB
[   12.112690] EDAC amd64: MC: 6:     0MB 7:     0MB

Yes, 2 dual-ranked DIMMs per MCT, i.e. 4 DIMMs in the DIMM slots on the
node (+ 4 more for the other MCT because it is a dual-node CPU). With
your patchset I got 8 ranks, 1024MB each, not good.

$ tree /sys/devices/system/edac/mc/mc0/rank?/
/sys/devices/system/edac/mc/mc0/rank0/
|-- dimm_dev_type
|-- dimm_edac_mode
|-- dimm_label
|-- dimm_location
|-- dimm_mem_type
`-- dimm_size
/sys/devices/system/edac/mc/mc0/rank1/
|-- dimm_dev_type
|-- dimm_edac_mode
|-- dimm_label
|-- dimm_location
|-- dimm_mem_type
`-- dimm_size
/sys/devices/system/edac/mc/mc0/rank2/
|-- dimm_dev_type
|-- dimm_edac_mode
|-- dimm_label
|-- dimm_location
|-- dimm_mem_type
`-- dimm_size
/sys/devices/system/edac/mc/mc0/rank3/
|-- dimm_dev_type
|-- dimm_edac_mode
|-- dimm_label
|-- dimm_location
|-- dimm_mem_type
`-- dimm_size
/sys/devices/system/edac/mc/mc0/rank4/
|-- dimm_dev_type
|-- dimm_edac_mode
|-- dimm_label
|-- dimm_location
|-- dimm_mem_type
`-- dimm_size
/sys/devices/system/edac/mc/mc0/rank5/
|-- dimm_dev_type
|-- dimm_edac_mode
|-- dimm_label
|-- dimm_location
|-- dimm_mem_type
`-- dimm_size
/sys/devices/system/edac/mc/mc0/rank6/
|-- dimm_dev_type
|-- dimm_edac_mode
|-- dimm_label
|-- dimm_location
|-- dimm_mem_type
`-- dimm_size
/sys/devices/system/edac/mc/mc0/rank7/
|-- dimm_dev_type
|-- dimm_edac_mode
|-- dimm_label
|-- dimm_location
|-- dimm_mem_type
`-- dimm_size

Also, what does the nomenclature

[   12.196138] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 0: dimm0 (0:0:0): row 0, chan 0
[   12.204636] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 1: dimm1 (0:1:0): row 0, chan 1
[   12.213127] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 2: dimm2 (1:0:0): row 1, chan 0
[   12.221613] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 3: dimm3 (1:1:0): row 1, chan 1
[   12.230103] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 4: dimm4 (2:0:0): row 2, chan 0
[   12.238590] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 5: dimm5 (2:1:0): row 2, chan 1
[   12.247078] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 6: dimm6 (3:0:0): row 3, chan 0
[   12.255560] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 7: dimm7 (3:1:0): row 3, chan 1
[   12.264058] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 8: dimm8 (4:0:0): row 4, chan 0
[   12.272552] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 9: dimm9 (4:1:0): row 4, chan 1
[   12.281041] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 10: dimm10 (5:0:0): row 5, chan 0
[   12.289699] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 11: dimm11 (5:1:0): row 5, chan 1
[   12.298362] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 12: dimm12 (6:0:0): row 6, chan 0
[   12.307018] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 13: dimm13 (6:1:0): row 6, chan 1
[   12.315684] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 14: dimm14 (7:0:0): row 7, chan 0
[   12.324352] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 15: dimm15 (7:1:0): row 7, chan 1

mean? 16 DIMMs? No way.

Basically, the problem with the DIMM nomenclature is that you cannot
know from the hardware how many chip selects, aka ranks, comprise
one DIMM. IOW, you cannot know whether your DIMMs are single-ranked,
dual-ranked or quad-ranked and thus you cannot combine the csrows into
DIMM structs.

Thanks.

-- 
Regards/Gruss,
Boris.

Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
GM: Alberto Bozzo
Reg: Dornach, Landkreis Muenchen
HRB Nr. 43632 WEEE Registernr: 129 19551


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-08 21:57 ` [PATCH 0/6] Add a per-dimm structure Borislav Petkov
@ 2012-03-09 10:32   ` Mauro Carvalho Chehab
  2012-03-09 14:38     ` Borislav Petkov
  0 siblings, 1 reply; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-09 10:32 UTC (permalink / raw)
  To: Borislav Petkov; +Cc: Linux Edac Mailing List, Linux Kernel Mailing List

Em 08-03-2012 18:57, Borislav Petkov escreveu:
> On Wed, Mar 07, 2012 at 08:40:32AM -0300, Mauro Carvalho Chehab wrote:
>> Prepare the internal structures to represent the memory properties per dimm, 
>> instead of per csrow. 
>>
>> This is needed for modern controllers with more than 2 channels, as the memories 
>> at the same slot number but on different channels (or channel pairs) may be 
>> different.
> 
> Ok, so I this thing looks pretty fishy to me. I've booted it on a box which has
> the following config on the first memory controller:
> 
> [   12.058897] EDAC MC: DCT0 chip selects:
> [   12.063091] EDAC amd64: MC: 0:  2048MB 1:  2048MB
> [   12.068155] EDAC amd64: MC: 2:  2048MB 3:  2048MB
> [   12.073219] EDAC amd64: MC: 4:     0MB 5:     0MB
> [   12.078281] EDAC amd64: MC: 6:     0MB 7:     0MB
> [   12.093305] EDAC MC: DCT1 chip selects:
> [   12.097499] EDAC amd64: MC: 0:  2048MB 1:  2048MB
> [   12.102562] EDAC amd64: MC: 2:  2048MB 3:  2048MB
> [   12.107623] EDAC amd64: MC: 4:     0MB 5:     0MB
> [   12.112690] EDAC amd64: MC: 6:     0MB 7:     0MB
> 
> Yes, 2 dual-ranked DIMMs per MCT, i.e. 4 DIMMs in the DIMM slots on the
> node (+ 4 more for the other MCT because it is a dual-node CPU). With
> your patchset I got 8 ranks, 1024MB each, not good.

Hmm... it seems it is dividing the memory size by the number of hanks.

I think that the error is on this patch:
	[PATCH 1/2] edac: Fix core support for MC's that see DIMMS instead of ranks

This hunk seems wrong:

diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 377eed8..ea7eb9a 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -2187,7 +2227,7 @@ static int init_csrows(struct mem_ctl_info *mci)
 		for (j = 0; j < pvt->channel_count; j++) {
 			csrow->channels[j].dimm->mtype = mtype;
 			csrow->channels[j].dimm->edac_mode = edac_mode;
-			csrow->channels[j].dimm->nr_pages = nr_pages;
+			csrow->channels[j].dimm->nr_pages = nr_pages / pvt->channel_count;
 
 		}

> 
> $ tree /sys/devices/system/edac/mc/mc0/rank?/
> /sys/devices/system/edac/mc/mc0/rank0/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> `-- dimm_size
> /sys/devices/system/edac/mc/mc0/rank1/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> `-- dimm_size
> /sys/devices/system/edac/mc/mc0/rank2/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> `-- dimm_size
> /sys/devices/system/edac/mc/mc0/rank3/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> `-- dimm_size
> /sys/devices/system/edac/mc/mc0/rank4/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> `-- dimm_size
> /sys/devices/system/edac/mc/mc0/rank5/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> `-- dimm_size
> /sys/devices/system/edac/mc/mc0/rank6/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> `-- dimm_size
> /sys/devices/system/edac/mc/mc0/rank7/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> `-- dimm_size

Ok, 8 ranks were filled.

> Also, what does the nomenclature
> 
> [   12.196138] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 0: dimm0 (0:0:0): row 0, chan 0
> [   12.204636] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 1: dimm1 (0:1:0): row 0, chan 1
> [   12.213127] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 2: dimm2 (1:0:0): row 1, chan 0
> [   12.221613] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 3: dimm3 (1:1:0): row 1, chan 1
> [   12.230103] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 4: dimm4 (2:0:0): row 2, chan 0
> [   12.238590] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 5: dimm5 (2:1:0): row 2, chan 1
> [   12.247078] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 6: dimm6 (3:0:0): row 3, chan 0
> [   12.255560] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 7: dimm7 (3:1:0): row 3, chan 1
> [   12.264058] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 8: dimm8 (4:0:0): row 4, chan 0
> [   12.272552] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 9: dimm9 (4:1:0): row 4, chan 1
> [   12.281041] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 10: dimm10 (5:0:0): row 5, chan 0
> [   12.289699] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 11: dimm11 (5:1:0): row 5, chan 1
> [   12.298362] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 12: dimm12 (6:0:0): row 6, chan 0
> [   12.307018] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 13: dimm13 (6:1:0): row 6, chan 1
> [   12.315684] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 14: dimm14 (7:0:0): row 7, chan 0
> [   12.324352] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 15: dimm15 (7:1:0): row 7, chan 1
> 
> mean? 16 DIMMs? No way.

The debug message needs to be fixed. The above message shows how many ranks were
allocated, and not DIMMs. That means that patch 5/6 of the last series is incomplete,
as it doesn't touch on the debug messages.

This debug info has the purpose of showing how the dimm or rank real location
is mapped into the virtual csrow/channel notation.

>From your logs, the machine you're testing has 16 ranks, so, except for the
debug log fix, it is properly detecting everything.

The rank location (the number in parenthesis) is being mapped to the right
row/channel. On this MC, the location has just 2 addresses, so, the above
message is showing "0" for the third location, as expected on this debug msg.

On a machine where the csrow/channel is virtualized, the above map would be 
different. For example, on a machine with the i5000 Memory Controller, the
memory is organized as:

       +-----------------------------------------------+
       |                      mc0                      |
       |        branch0        |        branch1        |
       | channel0  | channel1  | channel0  | channel1  |
-------+-----------------------------------------------+
slot3: |     0 MB  |     0 MB  |     0 MB  |     0 MB  |
slot2: |     0 MB  |     0 MB  |     0 MB  |     0 MB  |
-------+-----------------------------------------------+
slot1: |     0 MB  |     0 MB  |     0 MB  |     0 MB  |
slot0: |   512 MB  |   512 MB  |   512 MB  |   512 MB  |
-------+-----------------------------------------------+

This is the map for it (in this case, the debug is correct, as the memory is organized
per dimm):

[   16.946841] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 0: dimm0 (0:0:0): row 0, chan 0
[   16.946845] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 1: dimm1 (0:0:1): row 0, chan 1
[   16.946848] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 2: dimm2 (0:0:2): row 0, chan 2
[   16.946852] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 3: dimm3 (0:0:3): row 0, chan 3
[   16.946855] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 4: dimm4 (0:1:0): row 1, chan 0
[   16.946859] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 5: dimm5 (0:1:1): row 1, chan 1
[   16.946862] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 6: dimm6 (0:1:2): row 1, chan 2
[   16.946866] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 7: dimm7 (0:1:3): row 1, chan 3
[   16.946869] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 8: dimm8 (1:0:0): row 2, chan 0
[   16.946873] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 9: dimm9 (1:0:1): row 2, chan 1
[   16.946876] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 10: dimm10 (1:0:2): row 2, chan 2
[   16.946880] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 11: dimm11 (1:0:3): row 2, chan 3
[   16.946883] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 12: dimm12 (1:1:0): row 3, chan 0
[   16.946887] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 13: dimm13 (1:1:1): row 3, chan 1
[   16.946890] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 14: dimm14 (1:1:2): row 3, chan 2
[   16.946894] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 15: dimm15 (1:1:3): row 3, chan 3

It means that, on this driver, the dimm that it is at branch 1, channel 0
slot 0 is mapped, according with this debug message:
	[   16.946869] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 8: dimm8 (1:0:0): row 2, chan 0
as row 2, channel 0, on the per-csrow node:

/sys/devices/system/edac/mc/mc0/csrow2/ch0_dimm_label:mc#0branch#1channel#0slot#0

> Basically, the problem with the DIMM nomenclature is that you cannot
> know from the hardware how many chip selects, aka ranks, comprise
> one DIMM. IOW, you cannot know whether your DIMMs are single-ranked,
> dual-ranked or quad-ranked and thus you cannot combine the csrows into
> DIMM structs.

This may not be possible on amd64 hardware, but there are other memory
controllers that allow it. On several ones, the registers are per DIMM,
and there are fields there that counts the number of ranks per dimm.

There are other memory controllers that use a simpler strategy: they only
support single or dual ranks, and the even ranks are always used for the
second rank on the same DIMM. On them, if you divide csrow by 2, you got
the DIMM.

On this patch series, I didn't add any logic on the existing drivers to
convert the ones that internally represent memories as ranks into DIMMs.

Instead, the internal representation can be either per dimm or per rank.

I was tempted to fix it, as it sucks that the core would allow two 
different ranks from the same dimm to receive different labels (and
I even made some patches internally, fixing it for a few drivers), but
I ended to simplify the approach and add patch 5/6 to address the
duality.

After having this series applied, I'll likely convert a few drivers to
represent memories per DIMM, instead of per rank.

That would mean to convert the csrow location field inside the dimm struct
into an array with 4 elements, in order to support 1, 2 and 4R memories and
properly represent its location, and to add a way for the driver to tell
the EDAC core how ranks are mapped into DIMMs.

Regards,
Mauro

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-09 10:32   ` Mauro Carvalho Chehab
@ 2012-03-09 14:38     ` Borislav Petkov
  2012-03-09 16:40       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 41+ messages in thread
From: Borislav Petkov @ 2012-03-09 14:38 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: Linux Edac Mailing List, Linux Kernel Mailing List

On Fri, Mar 09, 2012 at 07:32:24AM -0300, Mauro Carvalho Chehab wrote:

[..]

> > Also, what does the nomenclature
> > 
> > [   12.196138] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 0: dimm0 (0:0:0): row 0, chan 0
> > [   12.204636] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 1: dimm1 (0:1:0): row 0, chan 1
> > [   12.213127] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 2: dimm2 (1:0:0): row 1, chan 0
> > [   12.221613] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 3: dimm3 (1:1:0): row 1, chan 1
> > [   12.230103] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 4: dimm4 (2:0:0): row 2, chan 0
> > [   12.238590] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 5: dimm5 (2:1:0): row 2, chan 1
> > [   12.247078] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 6: dimm6 (3:0:0): row 3, chan 0
> > [   12.255560] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 7: dimm7 (3:1:0): row 3, chan 1
> > [   12.264058] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 8: dimm8 (4:0:0): row 4, chan 0
> > [   12.272552] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 9: dimm9 (4:1:0): row 4, chan 1
> > [   12.281041] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 10: dimm10 (5:0:0): row 5, chan 0
> > [   12.289699] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 11: dimm11 (5:1:0): row 5, chan 1
> > [   12.298362] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 12: dimm12 (6:0:0): row 6, chan 0
> > [   12.307018] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 13: dimm13 (6:1:0): row 6, chan 1
> > [   12.315684] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 14: dimm14 (7:0:0): row 7, chan 0
> > [   12.324352] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 15: dimm15 (7:1:0): row 7, chan 1
> > 
> > mean? 16 DIMMs? No way.
> 
> The debug message needs to be fixed. The above message shows how many ranks were
> allocated, and not DIMMs. That means that patch 5/6 of the last series is incomplete,
> as it doesn't touch on the debug messages.
> 
> This debug info has the purpose of showing how the dimm or rank real location
> is mapped into the virtual csrow/channel notation.
> 
> From your logs, the machine you're testing has 16 ranks, so, except for the
> debug log fix, it is properly detecting everything.

No, it has 8 ranks (4 dual-ranked DIMMs on MCT 0 and the same on the
3 other MCTs). So rank0-7 is correct, actually, sorry. The dimm0-15
labeling above is rather wrong though and needs fixing.

> The rank location (the number in parenthesis) is being mapped to the right
> row/channel. On this MC, the location has just 2 addresses, so, the above
> message is showing "0" for the third location, as expected on this debug msg.
> 
> On a machine where the csrow/channel is virtualized, the above map would be 
> different. For example, on a machine with the i5000 Memory Controller, the
> memory is organized as:
> 
>        +-----------------------------------------------+
>        |                      mc0                      |
>        |        branch0        |        branch1        |
>        | channel0  | channel1  | channel0  | channel1  |
> -------+-----------------------------------------------+
> slot3: |     0 MB  |     0 MB  |     0 MB  |     0 MB  |
> slot2: |     0 MB  |     0 MB  |     0 MB  |     0 MB  |
> -------+-----------------------------------------------+
> slot1: |     0 MB  |     0 MB  |     0 MB  |     0 MB  |
> slot0: |   512 MB  |   512 MB  |   512 MB  |   512 MB  |
> -------+-----------------------------------------------+
> 
> This is the map for it (in this case, the debug is correct, as the memory is organized
> per dimm):
> 
> [   16.946841] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 0: dimm0 (0:0:0): row 0, chan 0
> [   16.946845] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 1: dimm1 (0:0:1): row 0, chan 1
> [   16.946848] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 2: dimm2 (0:0:2): row 0, chan 2
> [   16.946852] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 3: dimm3 (0:0:3): row 0, chan 3
> [   16.946855] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 4: dimm4 (0:1:0): row 1, chan 0
> [   16.946859] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 5: dimm5 (0:1:1): row 1, chan 1
> [   16.946862] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 6: dimm6 (0:1:2): row 1, chan 2
> [   16.946866] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 7: dimm7 (0:1:3): row 1, chan 3
> [   16.946869] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 8: dimm8 (1:0:0): row 2, chan 0
> [   16.946873] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 9: dimm9 (1:0:1): row 2, chan 1
> [   16.946876] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 10: dimm10 (1:0:2): row 2, chan 2
> [   16.946880] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 11: dimm11 (1:0:3): row 2, chan 3
> [   16.946883] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 12: dimm12 (1:1:0): row 3, chan 0
> [   16.946887] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 13: dimm13 (1:1:1): row 3, chan 1
> [   16.946890] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 14: dimm14 (1:1:2): row 3, chan 2
> [   16.946894] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 15: dimm15 (1:1:3): row 3, chan 3
> 
> It means that, on this driver, the dimm that it is at branch 1, channel 0
> slot 0 is mapped, according with this debug message:
> 	[   16.946869] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 8: dimm8 (1:0:0): row 2, chan 0
> as row 2, channel 0, on the per-csrow node:
> 
> /sys/devices/system/edac/mc/mc0/csrow2/ch0_dimm_label:mc#0branch#1channel#0slot#0
> 
> > Basically, the problem with the DIMM nomenclature is that you cannot
> > know from the hardware how many chip selects, aka ranks, comprise
> > one DIMM. IOW, you cannot know whether your DIMMs are single-ranked,
> > dual-ranked or quad-ranked and thus you cannot combine the csrows into
> > DIMM structs.
> 
> This may not be possible on amd64 hardware, but there are other memory
> controllers that allow it. On several ones, the registers are per DIMM,
> and there are fields there that counts the number of ranks per dimm.

Right, so all I'm saying is that on the drivers which have ranks but
cannot tell you to which DIMMs they belong, we shouldn't have the word
"DIMM" anywhere in sysfs or printk output because it is misleading
anyway. On those other drivers which explicitly support DIMMs, you can
do the per-DIMM splitting in /sysfs or whatever.

Also, now we have:

csrow0
|-- ce_count
|-- ch0_ce_count
|-- ch0_dimm_label
|-- ch1_ce_count
|-- ch1_dimm_label
|-- dev_type
|-- edac_mode
|-- mem_type
|-- size_mb
`-- ue_count
csrow1
|-- ce_count
|-- ch0_ce_count
|-- ch0_dimm_label
|-- ch1_ce_count
|-- ch1_dimm_label
|-- dev_type
|-- edac_mode
|-- mem_type
|-- size_mb
`-- ue_count
csrow2
...



with your patches we get:

rank0/
|-- dimm_dev_type
|-- dimm_edac_mode
|-- dimm_label
|-- dimm_location
|-- dimm_mem_type
`-- dimm_size
rank1/
|-- dimm_dev_type
|-- dimm_edac_mode
|-- dimm_label
|-- dimm_location
|-- dimm_mem_type
`-- dimm_size
rank2/
|-- dimm_dev_type
|-- dimm_edac_mode
|-- dimm_label
|-- dimm_location
|-- dimm_mem_type
`-- dimm_size
...


which splits the ch0 and ch1 of the csrow? dir above into ranks. All
fine and dandy but that doesn't change the whole situation - we simply
talk about ranks and not chip select rows anymore. Oh well...

Also, the following hierarchy looks ugly:

 ce_csrow0
 ce_csrow0_channel0
 ce_csrow0_channel1
 ce_csrow1
 ce_csrow1_channel0
 ce_csrow1_channel1
 ce_csrow2
 ce_csrow2_channel0
 ce_csrow2_channel1
 ce_csrow3
 ce_csrow3_channel0
 ce_csrow3_channel1

Much better would it be if you put the ch0 and ch1 CE error count into
the csrow?/ directory, i.e. something like:

csrow?/ce/ch{0,1}
csrow?/ue/ch{0,1}

so that all is clear just from looking at the directory structure. Or
put it into the rank?/ hierarchy and have all per-rank info in one
concentrated, self-describing location.

Thanks.

-- 
Regards/Gruss,
Boris.

Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
GM: Alberto Bozzo
Reg: Dornach, Landkreis Muenchen
HRB Nr. 43632 WEEE Registernr: 129 19551

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-09 14:38     ` Borislav Petkov
@ 2012-03-09 16:40       ` Mauro Carvalho Chehab
  2012-03-09 18:47         ` Borislav Petkov
  0 siblings, 1 reply; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-09 16:40 UTC (permalink / raw)
  To: Borislav Petkov; +Cc: Linux Edac Mailing List, Linux Kernel Mailing List

Em 09-03-2012 11:38, Borislav Petkov escreveu:
> On Fri, Mar 09, 2012 at 07:32:24AM -0300, Mauro Carvalho Chehab wrote:
> 
> [..]
> 
>>> Also, what does the nomenclature
>>>
>>> [   12.196138] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 0: dimm0 (0:0:0): row 0, chan 0
>>> [   12.204636] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 1: dimm1 (0:1:0): row 0, chan 1
>>> [   12.213127] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 2: dimm2 (1:0:0): row 1, chan 0
>>> [   12.221613] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 3: dimm3 (1:1:0): row 1, chan 1
>>> [   12.230103] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 4: dimm4 (2:0:0): row 2, chan 0
>>> [   12.238590] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 5: dimm5 (2:1:0): row 2, chan 1
>>> [   12.247078] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 6: dimm6 (3:0:0): row 3, chan 0
>>> [   12.255560] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 7: dimm7 (3:1:0): row 3, chan 1
>>> [   12.264058] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 8: dimm8 (4:0:0): row 4, chan 0
>>> [   12.272552] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 9: dimm9 (4:1:0): row 4, chan 1
>>> [   12.281041] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 10: dimm10 (5:0:0): row 5, chan 0
>>> [   12.289699] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 11: dimm11 (5:1:0): row 5, chan 1
>>> [   12.298362] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 12: dimm12 (6:0:0): row 6, chan 0
>>> [   12.307018] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 13: dimm13 (6:1:0): row 6, chan 1
>>> [   12.315684] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 14: dimm14 (7:0:0): row 7, chan 0
>>> [   12.324352] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 15: dimm15 (7:1:0): row 7, chan 1
>>>
>>> mean? 16 DIMMs? No way.
>>
>> The debug message needs to be fixed. The above message shows how many ranks were
>> allocated, and not DIMMs. That means that patch 5/6 of the last series is incomplete,
>> as it doesn't touch on the debug messages.
>>
>> This debug info has the purpose of showing how the dimm or rank real location
>> is mapped into the virtual csrow/channel notation.
>>
>> From your logs, the machine you're testing has 16 ranks, so, except for the
>> debug log fix, it is properly detecting everything.
> 
> No, it has 8 ranks (4 dual-ranked DIMMs on MCT 0 and the same on the
> 3 other MCTs).

Your logs show:

[   12.058897] EDAC MC: DCT0 chip selects:
[   12.063091] EDAC amd64: MC: 0:  2048MB 1:  2048MB
[   12.068155] EDAC amd64: MC: 2:  2048MB 3:  2048MB
[   12.073219] EDAC amd64: MC: 4:     0MB 5:     0MB
[   12.078281] EDAC amd64: MC: 6:     0MB 7:     0MB
[   12.093305] EDAC MC: DCT1 chip selects:
[   12.097499] EDAC amd64: MC: 0:  2048MB 1:  2048MB
[   12.102562] EDAC amd64: MC: 2:  2048MB 3:  2048MB
[   12.107623] EDAC amd64: MC: 4:     0MB 5:     0MB
[   12.112690] EDAC amd64: MC: 6:     0MB 7:     0MB

This memory controller has 16 ranks (8 filled/8 empty). The above
debug info is at edac_mc_alloc(). The EDAC core doesn't know yet how 
many of those were filled or not, as the driver didn't fill the rank
size yet.

> So rank0-7 is correct, actually, sorry.

> The dimm0-15
> labeling above is rather wrong though and needs fixing.

Ok, I'll write a patch fixing it, or resent patch 5/6 with the fix.

>> The rank location (the number in parenthesis) is being mapped to the right
>> row/channel. On this MC, the location has just 2 addresses, so, the above
>> message is showing "0" for the third location, as expected on this debug msg.
>>
>> On a machine where the csrow/channel is virtualized, the above map would be 
>> different. For example, on a machine with the i5000 Memory Controller, the
>> memory is organized as:
>>
>>        +-----------------------------------------------+
>>        |                      mc0                      |
>>        |        branch0        |        branch1        |
>>        | channel0  | channel1  | channel0  | channel1  |
>> -------+-----------------------------------------------+
>> slot3: |     0 MB  |     0 MB  |     0 MB  |     0 MB  |
>> slot2: |     0 MB  |     0 MB  |     0 MB  |     0 MB  |
>> -------+-----------------------------------------------+
>> slot1: |     0 MB  |     0 MB  |     0 MB  |     0 MB  |
>> slot0: |   512 MB  |   512 MB  |   512 MB  |   512 MB  |
>> -------+-----------------------------------------------+
>>
>> This is the map for it (in this case, the debug is correct, as the memory is organized
>> per dimm):
>>
>> [   16.946841] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 0: dimm0 (0:0:0): row 0, chan 0
>> [   16.946845] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 1: dimm1 (0:0:1): row 0, chan 1
>> [   16.946848] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 2: dimm2 (0:0:2): row 0, chan 2
>> [   16.946852] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 3: dimm3 (0:0:3): row 0, chan 3
>> [   16.946855] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 4: dimm4 (0:1:0): row 1, chan 0
>> [   16.946859] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 5: dimm5 (0:1:1): row 1, chan 1
>> [   16.946862] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 6: dimm6 (0:1:2): row 1, chan 2
>> [   16.946866] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 7: dimm7 (0:1:3): row 1, chan 3
>> [   16.946869] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 8: dimm8 (1:0:0): row 2, chan 0
>> [   16.946873] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 9: dimm9 (1:0:1): row 2, chan 1
>> [   16.946876] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 10: dimm10 (1:0:2): row 2, chan 2
>> [   16.946880] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 11: dimm11 (1:0:3): row 2, chan 3
>> [   16.946883] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 12: dimm12 (1:1:0): row 3, chan 0
>> [   16.946887] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 13: dimm13 (1:1:1): row 3, chan 1
>> [   16.946890] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 14: dimm14 (1:1:2): row 3, chan 2
>> [   16.946894] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 15: dimm15 (1:1:3): row 3, chan 3
>>
>> It means that, on this driver, the dimm that it is at branch 1, channel 0
>> slot 0 is mapped, according with this debug message:
>> 	[   16.946869] EDAC DEBUG: edac_mc_alloc: edac_mc_alloc: 8: dimm8 (1:0:0): row 2, chan 0
>> as row 2, channel 0, on the per-csrow node:
>>
>> /sys/devices/system/edac/mc/mc0/csrow2/ch0_dimm_label:mc#0branch#1channel#0slot#0
>>
>>> Basically, the problem with the DIMM nomenclature is that you cannot
>>> know from the hardware how many chip selects, aka ranks, comprise
>>> one DIMM. IOW, you cannot know whether your DIMMs are single-ranked,
>>> dual-ranked or quad-ranked and thus you cannot combine the csrows into
>>> DIMM structs.
>>
>> This may not be possible on amd64 hardware, but there are other memory
>> controllers that allow it. On several ones, the registers are per DIMM,
>> and there are fields there that counts the number of ranks per dimm.
> 
> Right, so all I'm saying is that on the drivers which have ranks but
> cannot tell you to which DIMMs they belong, we shouldn't have the word
> "DIMM" anywhere in sysfs or printk output because it is misleading
> anyway.

Agreed. I'll fix that.

> On those other drivers which explicitly support DIMMs, you can
> do the per-DIMM splitting in /sysfs or whatever.
> 
> Also, now we have:
> 
> csrow0
> |-- ce_count
> |-- ch0_ce_count
> |-- ch0_dimm_label
> |-- ch1_ce_count
> |-- ch1_dimm_label
> |-- dev_type
> |-- edac_mode
> |-- mem_type
> |-- size_mb
> `-- ue_count
> csrow1
> |-- ce_count
> |-- ch0_ce_count
> |-- ch0_dimm_label
> |-- ch1_ce_count
> |-- ch1_dimm_label
> |-- dev_type
> |-- edac_mode
> |-- mem_type
> |-- size_mb
> `-- ue_count
> csrow2
> ...
> 
> 
> 
> with your patches we get:
> 
> rank0/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> `-- dimm_size
> rank1/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> `-- dimm_size
> rank2/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> `-- dimm_size
> ...
> 
> 
> which splits the ch0 and ch1 of the csrow? dir above into ranks. All
> fine and dandy but that doesn't change the whole situation - we simply
> talk about ranks and not chip select rows anymore. Oh well...

Yes, it won't bring anything new there for rank-based hierarchy.
The only benefit in this case is that newer userspace programs/versions
would use the same struct as the one shown with DIMMs:

dimm0
├── dimm_dev_type
├── dimm_edac_mode
├── dimm_label
├── dimm_location
├── dimm_mem_type
└── dimm_size
dimm4
├── dimm_dev_type
├── dimm_edac_mode
├── dimm_label
├── dimm_location
├── dimm_mem_type
└── dimm_size
dimm8
├── dimm_dev_type
├── dimm_edac_mode
├── dimm_label
├── dimm_location
├── dimm_mem_type
└── dimm_size


> Also, the following hierarchy looks ugly:
> 
>  ce_csrow0
>  ce_csrow0_channel0
>  ce_csrow0_channel1
>  ce_csrow1
>  ce_csrow1_channel0
>  ce_csrow1_channel1
>  ce_csrow2
>  ce_csrow2_channel0
>  ce_csrow2_channel1
>  ce_csrow3
>  ce_csrow3_channel0
>  ce_csrow3_channel1

The actual error count node names depend on how the hierarchy is 
organized. This is how it looks like at the i5000 machine:

mc0
├── ce_branch0
├── ce_branch0_channel0
├── ce_branch0_channel0_slot0
├── ce_branch0_channel1
├── ce_branch0_channel1_slot0
├── ce_branch1
├── ce_branch1_channel0
├── ce_branch1_channel0_slot0
├── ce_branch1_channel1
├── ce_branch1_channel1_slot0
├── ce_count
├── ce_noinfo_count
├── ue_branch0
├── ue_branch0_channel0
├── ue_branch0_channel0_slot0
├── ue_branch0_channel1
├── ue_branch0_channel1_slot0
├── ue_branch1
├── ue_branch1_channel0
├── ue_branch1_channel0_slot0
├── ue_branch1_channel1
├── ue_branch1_channel1_slot0
├── ue_count
└── ue_noinfo_count

> 
> Much better would it be if you put the ch0 and ch1 CE error count into
> the csrow?/ directory, i.e. something like:
> 
> csrow?/ce/ch{0,1}
> csrow?/ue/ch{0,1}

There is a strong technical reason for not doing it. Now, the rank or
dimm information is not stored anymore at the legacy structures.
The "csrow?" sysfs nodes are created from the struct csrow_info,
while the rank/dimm nodes, are created from struct dimm_info.

A csrow? node only makes sense if the hierarchy is csrow/channel.
As the code should work with all kinds of hierarchy, it would require
two different implementations, one for each case.

It would also mix the old representation with the new one, creating
a complex code for the csrow-based sysfs nodes. After having
the new way adopted from some kernel versions, we may think on remove
the old nodes, as removing the csrow/ch hierarchy means to simplify
the EDAC code by removing the compatibility logic and data. 

So, keeping it on a separate hierarchy helps to have a sane implementation.

>From userspace POV, creating a node tree may make it  harder to parse, as 
there would be, currently, at least 3 different ways for them with the 
current hierarchies:
	csrow?/channel?/ce?
	branch?/channel?/slot?/ce?
	channel?/slot?/ce?

(and more could be added, as new memory technologies/arrangements
may be created).

I considered two other alternatives, before adopting this one on my patches:

1) remove the layer name from the name. So, the nodes could be called instead:

	ce_count_0:0:0
	ce_count_0:0:1
	ce_count_0:0
	ce_count_0
	ce_count_0:1:0
	ce_count_0:1:1
	ce_count_0:1
	ce_count_1
	...

It shouldn't be hard to change the code to use this way, but it is a little
more confusing for users to understand.

2) Put all error counters into a single sub-directory, like:
	error_counters
	├── ce_branch0
	├── ce_branch0_channel0
	├── ce_branch0_channel0_slot0
	├── ce_branch0_channel1
	├── ce_branch0_channel1_slot0	
	├── ce_branch1
	├── ce_branch1_channel0
	├── ce_branch1_channel0_slot0
	├── ce_branch1_channel1
	├── ce_branch1_channel1_slot0
	├── ce_count
	├── ce_noinfo_count
	├── ue_branch0
	├── ue_branch0_channel0
	├── ue_branch0_channel0_slot0
	├── ue_branch0_channel1
	├── ue_branch0_channel1_slot0
	├── ue_branch1
	├── ue_branch1_channel0
	├── ue_branch1_channel0_slot0
	├── ue_branch1_channel1
	├── ue_branch1_channel1_slot0
	├── ue_count
	└── ue_noinfo_count

In the last case, the EDAC core would need to keep the 4
"general" error counters also under the mc? level (ce_count & friends).

> 
> so that all is clear just from looking at the directory structure. Or
> put it into the rank?/ hierarchy and have all per-rank info in one
> concentrated, self-describing location.

Not sure if I understood what you're meaning here. Could you give an
example about what kind of tree are you imagining in this case?

> 
> Thanks.
> 

Regards,
Mauro

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-09 16:40       ` Mauro Carvalho Chehab
@ 2012-03-09 18:47         ` Borislav Petkov
  2012-03-09 19:46           ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 41+ messages in thread
From: Borislav Petkov @ 2012-03-09 18:47 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: Linux Edac Mailing List, Linux Kernel Mailing List

On Fri, Mar 09, 2012 at 01:40:58PM -0300, Mauro Carvalho Chehab wrote:
> Your logs show:
> 
> [   12.058897] EDAC MC: DCT0 chip selects:
> [   12.063091] EDAC amd64: MC: 0:  2048MB 1:  2048MB
> [   12.068155] EDAC amd64: MC: 2:  2048MB 3:  2048MB
> [   12.073219] EDAC amd64: MC: 4:     0MB 5:     0MB
> [   12.078281] EDAC amd64: MC: 6:     0MB 7:     0MB
> [   12.093305] EDAC MC: DCT1 chip selects:
> [   12.097499] EDAC amd64: MC: 0:  2048MB 1:  2048MB
> [   12.102562] EDAC amd64: MC: 2:  2048MB 3:  2048MB
> [   12.107623] EDAC amd64: MC: 4:     0MB 5:     0MB
> [   12.112690] EDAC amd64: MC: 6:     0MB 7:     0MB
> 
> This memory controller has 16 ranks (8 filled/8 empty). The above
> debug info is at edac_mc_alloc(). The EDAC core doesn't know yet how 
> many of those were filled or not, as the driver didn't fill the rank
> size yet.

Yeah, yeah, 16 ranks total, but 8 used. So we're on the same page.

[..]

> > which splits the ch0 and ch1 of the csrow? dir above into ranks. All
> > fine and dandy but that doesn't change the whole situation - we simply
> > talk about ranks and not chip select rows anymore. Oh well...
> 
> Yes, it won't bring anything new there for rank-based hierarchy.
> The only benefit in this case is that newer userspace programs/versions
> would use the same struct as the one shown with DIMMs:
> 
> dimm0
> ├── dimm_dev_type
> ├── dimm_edac_mode
> ├── dimm_label
> ├── dimm_location
> ├── dimm_mem_type
> └── dimm_size
> dimm4
> ├── dimm_dev_type
> ├── dimm_edac_mode
> ├── dimm_label
> ├── dimm_location
> ├── dimm_mem_type
> └── dimm_size
> dimm8
> ├── dimm_dev_type
> ├── dimm_edac_mode
> ├── dimm_label
> ├── dimm_location
> ├── dimm_mem_type
> └── dimm_size

Just to make sure I understand you correctly: are you saying the
topnodes dimm{0,4,8} are called ranks on the CS-based hw? If so, yes, I
agree.

> 
> 
> > Also, the following hierarchy looks ugly:
> > 
> >  ce_csrow0
> >  ce_csrow0_channel0
> >  ce_csrow0_channel1
> >  ce_csrow1
> >  ce_csrow1_channel0
> >  ce_csrow1_channel1
> >  ce_csrow2
> >  ce_csrow2_channel0
> >  ce_csrow2_channel1
> >  ce_csrow3
> >  ce_csrow3_channel0
> >  ce_csrow3_channel1
> 
> The actual error count node names depend on how the hierarchy is 
> organized. This is how it looks like at the i5000 machine:
> 
> mc0
> ├── ce_branch0
> ├── ce_branch0_channel0
> ├── ce_branch0_channel0_slot0
> ├── ce_branch0_channel1
> ├── ce_branch0_channel1_slot0

Yes, but this names have subdirectories written all over them, don't you
see that in redundant substrings in every filename?

IOW, it is much nicer to have:

mc0
|-> CE
    |-> branch0
        |-> ch0
	    |-> slot0
	    |-> slot1
	    |-> ...
	|-> ch1
	    |-> slot0
	    |-> slot1
	    ...
    |-> branch1
    ...
|-> UE
...


On CS-based controllers we simply skip "branch".

[..]

> > Much better would it be if you put the ch0 and ch1 CE error count into
> > the csrow?/ directory, i.e. something like:
> > 
> > csrow?/ce/ch{0,1}
> > csrow?/ue/ch{0,1}
> 
> There is a strong technical reason for not doing it. Now, the rank or
> dimm information is not stored anymore at the legacy structures.
> The "csrow?" sysfs nodes are created from the struct csrow_info,
> while the rank/dimm nodes, are created from struct dimm_info.
> 
> A csrow? node only makes sense if the hierarchy is csrow/channel.
> As the code should work with all kinds of hierarchy, it would require
> two different implementations, one for each case.
> 
> It would also mix the old representation with the new one, creating
> a complex code for the csrow-based sysfs nodes. After having
> the new way adopted from some kernel versions, we may think on remove
> the old nodes,

yeah, we already talked about this - we need to know that no userspace
uses them before we do that.

> as removing the csrow/ch hierarchy means to simplify
> the EDAC code by removing the compatibility logic and data. 
> 
> So, keeping it on a separate hierarchy helps to have a sane implementation.

Ok.

> From userspace POV, creating a node tree may make it  harder to parse, as 
> there would be, currently, at least 3 different ways for them with the 
> current hierarchies:
> 	csrow?/channel?/ce?
> 	branch?/channel?/slot?/ce?
> 	channel?/slot?/ce?
> 
> (and more could be added, as new memory technologies/arrangements
> may be created).
> 
> I considered two other alternatives, before adopting this one on my patches:
> 
> 1) remove the layer name from the name. So, the nodes could be called instead:
> 
> 	ce_count_0:0:0
> 	ce_count_0:0:1
> 	ce_count_0:0
> 	ce_count_0
> 	ce_count_0:1:0
> 	ce_count_0:1:1
> 	ce_count_0:1
> 	ce_count_1
> 	...
> 
> It shouldn't be hard to change the code to use this way, but it is a little
> more confusing for users to understand.
> 
> 2) Put all error counters into a single sub-directory, like:
> 	error_counters
> 	├── ce_branch0
> 	├── ce_branch0_channel0
> 	├── ce_branch0_channel0_slot0
> 	├── ce_branch0_channel1
> 	├── ce_branch0_channel1_slot0	
> 	├── ce_branch1
> 	├── ce_branch1_channel0
> 	├── ce_branch1_channel0_slot0
> 	├── ce_branch1_channel1
> 	├── ce_branch1_channel1_slot0
> 	├── ce_count
> 	├── ce_noinfo_count
> 	├── ue_branch0
> 	├── ue_branch0_channel0
> 	├── ue_branch0_channel0_slot0
> 	├── ue_branch0_channel1
> 	├── ue_branch0_channel1_slot0
> 	├── ue_branch1
> 	├── ue_branch1_channel0
> 	├── ue_branch1_channel0_slot0
> 	├── ue_branch1_channel1
> 	├── ue_branch1_channel1_slot0
> 	├── ue_count
> 	└── ue_noinfo_count
> 
> In the last case, the EDAC core would need to keep the 4
> "general" error counters also under the mc? level (ce_count & friends).
>
> > 
> > so that all is clear just from looking at the directory structure. Or
> > put it into the rank?/ hierarchy and have all per-rank info in one
> > concentrated, self-describing location.
> 
> Not sure if I understood what you're meaning here. Could you give an
> example about what kind of tree are you imagining in this case?

Right, what I mean is that the rank?/ already contains some info:

rank0/
|-- dimm_dev_type
|-- dimm_edac_mode
|-- dimm_label
|-- dimm_location
|-- dimm_mem_type
`-- dimm_size

Now, we do the CE/UE error counting on a per-rank granularity anyway, so
the most natural way to have that is to add those counts to the ranks:

rank0/
|-- dimm_dev_type
|-- dimm_edac_mode
|-- dimm_label
|-- dimm_location
|-- dimm_mem_type
|-- CE
|-- UE
`-- dimm_size

And this has to be _very_ easy to do without any adding additional
sysfs nodes with ugly names to /sys/devices/system/edac etc. This is
even better grouping than the mc?/-based hierarchy I suggested above,
actually.

Hmm...

-- 
Regards/Gruss,
Boris.

Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
GM: Alberto Bozzo
Reg: Dornach, Landkreis Muenchen
HRB Nr. 43632 WEEE Registernr: 129 19551

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-09 18:47         ` Borislav Petkov
@ 2012-03-09 19:46           ` Mauro Carvalho Chehab
  2012-03-11 11:34             ` Borislav Petkov
  0 siblings, 1 reply; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-09 19:46 UTC (permalink / raw)
  To: Borislav Petkov; +Cc: Linux Edac Mailing List, Linux Kernel Mailing List

Em 09-03-2012 15:47, Borislav Petkov escreveu:
> On Fri, Mar 09, 2012 at 01:40:58PM -0300, Mauro Carvalho Chehab wrote:


[..]

>>> which splits the ch0 and ch1 of the csrow? dir above into ranks. All
>>> fine and dandy but that doesn't change the whole situation - we simply
>>> talk about ranks and not chip select rows anymore. Oh well...
>>
>> Yes, it won't bring anything new there for rank-based hierarchy.
>> The only benefit in this case is that newer userspace programs/versions
>> would use the same struct as the one shown with DIMMs:
>>
>> dimm0
>> ├── dimm_dev_type
>> ├── dimm_edac_mode
>> ├── dimm_label
>> ├── dimm_location
>> ├── dimm_mem_type
>> └── dimm_size
>> dimm4
>> ├── dimm_dev_type
>> ├── dimm_edac_mode
>> ├── dimm_label
>> ├── dimm_location
>> ├── dimm_mem_type
>> └── dimm_size
>> dimm8
>> ├── dimm_dev_type
>> ├── dimm_edac_mode
>> ├── dimm_label
>> ├── dimm_location
>> ├── dimm_mem_type
>> └── dimm_size
> 
> Just to make sure I understand you correctly: are you saying the
> topnodes dimm{0,4,8} are called ranks on the CS-based hw? If so, yes, I
> agree.

Yes. 

>>
>>
>>> Also, the following hierarchy looks ugly:
>>>
>>>  ce_csrow0
>>>  ce_csrow0_channel0
>>>  ce_csrow0_channel1
>>>  ce_csrow1
>>>  ce_csrow1_channel0
>>>  ce_csrow1_channel1
>>>  ce_csrow2
>>>  ce_csrow2_channel0
>>>  ce_csrow2_channel1
>>>  ce_csrow3
>>>  ce_csrow3_channel0
>>>  ce_csrow3_channel1
>>
>> The actual error count node names depend on how the hierarchy is 
>> organized. This is how it looks like at the i5000 machine:
>>
>> mc0
>> ├── ce_branch0
>> ├── ce_branch0_channel0
>> ├── ce_branch0_channel0_slot0
>> ├── ce_branch0_channel1
>> ├── ce_branch0_channel1_slot0
> 
> Yes, but this names have subdirectories written all over them, don't you
> see that in redundant substrings in every filename?
> 
> IOW, it is much nicer to have:
> 
> mc0
> |-> CE
>     |-> branch0
>         |-> ch0
> 	    |-> slot0
> 	    |-> slot1
> 	    |-> ...
> 	|-> ch1
> 	    |-> slot0
> 	    |-> slot1
> 	    ...
>     |-> branch1
>     ...
> |-> UE
> ...

Well, that works for me.

> 
> 
> On CS-based controllers we simply skip "branch".

Maybe we could call the last directory as "row" instead of "slot",
in the case of CS-based controllers, in order to differentiate from
the ones that aren't CS-based and don't have branches.

There are currently one FB-DIMM memory controller like that, plus
Sandy Bridge/Nehalem and i5100 MC's that maps memory per channel/slot.

Btw, i5100 is an interesting driver... it is for DDR2 memories.
Internally, it has a table that maps from csrow to ranks (dimm_csmap).

The dimm_csmap table depends on the memory space mode: if the max allowed
memory is 24 GB, it uses one map table; if the max allowed memory is 48GB,
it uses another table.

The memory controller works just like any other CS-based memory
controller, but the driver itself takes care of not exposing the rank
complexity to the EDAC core. This driver is now properly reported as
having the minimal location "grain" as "dimms".

> 
> [..]
> 
>>> Much better would it be if you put the ch0 and ch1 CE error count into
>>> the csrow?/ directory, i.e. something like:
>>>
>>> csrow?/ce/ch{0,1}
>>> csrow?/ue/ch{0,1}
>>
>> There is a strong technical reason for not doing it. Now, the rank or
>> dimm information is not stored anymore at the legacy structures.
>> The "csrow?" sysfs nodes are created from the struct csrow_info,
>> while the rank/dimm nodes, are created from struct dimm_info.
>>
>> A csrow? node only makes sense if the hierarchy is csrow/channel.
>> As the code should work with all kinds of hierarchy, it would require
>> two different implementations, one for each case.
>>
>> It would also mix the old representation with the new one, creating
>> a complex code for the csrow-based sysfs nodes. After having
>> the new way adopted from some kernel versions, we may think on remove
>> the old nodes,
> 
> yeah, we already talked about this - we need to know that no userspace
> uses them before we do that.

Sure.

> 
>> as removing the csrow/ch hierarchy means to simplify
>> the EDAC code by removing the compatibility logic and data. 
>>
>> So, keeping it on a separate hierarchy helps to have a sane implementation.
> 
> Ok.
> 
>> From userspace POV, creating a node tree may make it  harder to parse, as 
>> there would be, currently, at least 3 different ways for them with the 
>> current hierarchies:
>> 	csrow?/channel?/ce?
>> 	branch?/channel?/slot?/ce?
>> 	channel?/slot?/ce?
>>
>> (and more could be added, as new memory technologies/arrangements
>> may be created).
>>
>> I considered two other alternatives, before adopting this one on my patches:
>>
>> 1) remove the layer name from the name. So, the nodes could be called instead:
>>
>> 	ce_count_0:0:0
>> 	ce_count_0:0:1
>> 	ce_count_0:0
>> 	ce_count_0
>> 	ce_count_0:1:0
>> 	ce_count_0:1:1
>> 	ce_count_0:1
>> 	ce_count_1
>> 	...
>>
>> It shouldn't be hard to change the code to use this way, but it is a little
>> more confusing for users to understand.
>>
>> 2) Put all error counters into a single sub-directory, like:
>> 	error_counters
>> 	├── ce_branch0
>> 	├── ce_branch0_channel0
>> 	├── ce_branch0_channel0_slot0
>> 	├── ce_branch0_channel1
>> 	├── ce_branch0_channel1_slot0	
>> 	├── ce_branch1
>> 	├── ce_branch1_channel0
>> 	├── ce_branch1_channel0_slot0
>> 	├── ce_branch1_channel1
>> 	├── ce_branch1_channel1_slot0
>> 	├── ce_count
>> 	├── ce_noinfo_count
>> 	├── ue_branch0
>> 	├── ue_branch0_channel0
>> 	├── ue_branch0_channel0_slot0
>> 	├── ue_branch0_channel1
>> 	├── ue_branch0_channel1_slot0
>> 	├── ue_branch1
>> 	├── ue_branch1_channel0
>> 	├── ue_branch1_channel0_slot0
>> 	├── ue_branch1_channel1
>> 	├── ue_branch1_channel1_slot0
>> 	├── ue_count
>> 	└── ue_noinfo_count
>>
>> In the last case, the EDAC core would need to keep the 4
>> "general" error counters also under the mc? level (ce_count & friends).
>>
>>>
>>> so that all is clear just from looking at the directory structure. Or
>>> put it into the rank?/ hierarchy and have all per-rank info in one
>>> concentrated, self-describing location.
>>
>> Not sure if I understood what you're meaning here. Could you give an
>> example about what kind of tree are you imagining in this case?
> 
> Right, what I mean is that the rank?/ already contains some info:
> 
> rank0/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> `-- dimm_size
> 
> Now, we do the CE/UE error counting on a per-rank granularity anyway, so
> the most natural way to have that is to add those counts to the ranks:
> 
> rank0/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> |-- CE
> |-- UE
> `-- dimm_size
> 
> And this has to be _very_ easy to do without any adding additional
> sysfs nodes with ugly names to /sys/devices/system/edac etc. This is
> even better grouping than the mc?/-based hierarchy I suggested above,
> actually.

Agreed. Yeah, it is easy to add CE/UE there. I actually implemented it
on one of my internal patches, but there's an issue:

The typical case for UE is to report errors by cacheline (128 bits), and
not by DIMM. This happens on all FB-DIMM memory controllers, and also on
several CS-based ones.

For example, this is how (currently) the amd64_handle_ue() handles an
Uncorrected Error:

		error_address_to_page_and_offset(sys_addr, &page, &offset);
		edac_mc_handle_ue(log_mci, page, offset, csrow, EDAC_MOD_STR);

There's no channel info there.

Maybe this is there just due to the current ABI limits. If so, with the new ABI,
it will be possible to report on what channel the error occurred, where supported,
but, at least on the FB-DIMM drivers I wrote, UE errors are reported by cacheline, 
as the ECC code used by those memory controllers now use the Chipkill algorithm,
with takes 128+16 bits, instead of 64+8 bits, in order to enhance the capability
of correcting errors. So, while they can detect where a single error is, for
double errors, it may not be able to correct the error, and be unable to point 
what of the 144 bits are wrong. So, the location should point to two DIMMs instead
of one.

So, except the ECC is calculated over 64+8 bits, the UE error counters for the
rank/dimm won't be incremented. instead, it will increment the "dual rank" or
"dual dimm" error counter.

In the case of CS-based MC's, this is the "per-csrow" UE counter. For FB-DIMMs,
the counter will be the branch one, as the errors will affect two dimms at the
same branch.

Hm... maybe we should map it then as "channel/branch/slot",
instead of "branch/channel/slot".

One alternative would simply to remove all those intermediate counters, letting
userspace to count the errors via perf (provided that we have a proper location
field).

Regards,
Mauro

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-09 19:46           ` Mauro Carvalho Chehab
@ 2012-03-11 11:34             ` Borislav Petkov
  2012-03-11 12:32               ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 41+ messages in thread
From: Borislav Petkov @ 2012-03-11 11:34 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: Linux Edac Mailing List, Linux Kernel Mailing List

On Fri, Mar 09, 2012 at 04:46:53PM -0300, Mauro Carvalho Chehab wrote:

[..]

> > Right, what I mean is that the rank?/ already contains some info:
> > 
> > rank0/
> > |-- dimm_dev_type
> > |-- dimm_edac_mode
> > |-- dimm_label
> > |-- dimm_location
> > |-- dimm_mem_type
> > `-- dimm_size
> > 
> > Now, we do the CE/UE error counting on a per-rank granularity anyway, so
> > the most natural way to have that is to add those counts to the ranks:
> > 
> > rank0/
> > |-- dimm_dev_type
> > |-- dimm_edac_mode
> > |-- dimm_label
> > |-- dimm_location
> > |-- dimm_mem_type
> > |-- CE
> > |-- UE
> > `-- dimm_size
> > 
> > And this has to be _very_ easy to do without any adding additional
> > sysfs nodes with ugly names to /sys/devices/system/edac etc. This is
> > even better grouping than the mc?/-based hierarchy I suggested above,
> > actually.
> 
> Agreed. Yeah, it is easy to add CE/UE there. I actually implemented it
> on one of my internal patches, but there's an issue:
> 
> The typical case for UE is to report errors by cacheline (128 bits), and
> not by DIMM. This happens on all FB-DIMM memory controllers, and also on
> several CS-based ones.
> 
> For example, this is how (currently) the amd64_handle_ue() handles an
> Uncorrected Error:
> 
> 		error_address_to_page_and_offset(sys_addr, &page, &offset);
> 		edac_mc_handle_ue(log_mci, page, offset, csrow, EDAC_MOD_STR);
> 
> There's no channel info there.

Right, this looks like a largely untested path which has been that way
since forever. But, since UEs generally cause the machine to syncflood
and warm reset (now, at least), I don't think it makes a whole lot of
sense to even have such a counter - if we did, it would either say 0 or
1 :).

So, I'd suggest the UE counter to be optional and to let the driver
decide whether it wants it or not.

[..]

> One alternative would simply to remove all those intermediate
> counters, letting userspace to count the errors via perf (provided
> that we have a proper location field).

Yes, that would be where we want to go eventually because I too don't
see any reason for those counters. Besides, they don't decay over time,
for example, say you have a DIMM which experiences a temporary failure
and generates k CEs. Then, the source of that error disappears and the
DIMM works fine for months.

Now, when you look at the counters, you'll still see k CEs in one of its
ranks which doesn't tell you when those errors happened and what their
rate was, etc.

So, I'm fine with dropping those counters since they don't give you the
flexibility of a userspace tool and they don't work properly anyway.

HOWEVER, I don't know who uses them still so probably a deprecation
warning is in order here...


> 
> Regards,
> Mauro
> 

-- 
Regards/Gruss,
Boris.

Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
GM: Alberto Bozzo
Reg: Dornach, Landkreis Muenchen
HRB Nr. 43632 WEEE Registernr: 129 19551

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-11 11:34             ` Borislav Petkov
@ 2012-03-11 12:32               ` Mauro Carvalho Chehab
  2012-03-12 16:39                 ` Borislav Petkov
  0 siblings, 1 reply; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-11 12:32 UTC (permalink / raw)
  To: Borislav Petkov, Mark A. Grondona
  Cc: Linux Edac Mailing List, Linux Kernel Mailing List

Em 11-03-2012 08:34, Borislav Petkov escreveu:
> On Fri, Mar 09, 2012 at 04:46:53PM -0300, Mauro Carvalho Chehab wrote:
> 
> [..]
> 
>>> Right, what I mean is that the rank?/ already contains some info:
>>>
>>> rank0/
>>> |-- dimm_dev_type
>>> |-- dimm_edac_mode
>>> |-- dimm_label
>>> |-- dimm_location
>>> |-- dimm_mem_type
>>> `-- dimm_size
>>>
>>> Now, we do the CE/UE error counting on a per-rank granularity anyway, so
>>> the most natural way to have that is to add those counts to the ranks:
>>>
>>> rank0/
>>> |-- dimm_dev_type
>>> |-- dimm_edac_mode
>>> |-- dimm_label
>>> |-- dimm_location
>>> |-- dimm_mem_type
>>> |-- CE
>>> |-- UE
>>> `-- dimm_size
>>>
>>> And this has to be _very_ easy to do without any adding additional
>>> sysfs nodes with ugly names to /sys/devices/system/edac etc. This is
>>> even better grouping than the mc?/-based hierarchy I suggested above,
>>> actually.
>>
>> Agreed. Yeah, it is easy to add CE/UE there. I actually implemented it
>> on one of my internal patches, but there's an issue:
>>
>> The typical case for UE is to report errors by cacheline (128 bits), and
>> not by DIMM. This happens on all FB-DIMM memory controllers, and also on
>> several CS-based ones.
>>
>> For example, this is how (currently) the amd64_handle_ue() handles an
>> Uncorrected Error:
>>
>> 		error_address_to_page_and_offset(sys_addr, &page, &offset);
>> 		edac_mc_handle_ue(log_mci, page, offset, csrow, EDAC_MOD_STR);
>>
>> There's no channel info there.
> 
> Right, this looks like a largely untested path which has been that way
> since forever. But, since UEs generally cause the machine to syncflood
> and warm reset (now, at least), I don't think it makes a whole lot of
> sense to even have such a counter - if we did, it would either say 0 or
> 1 :).
> 
> So, I'd suggest the UE counter to be optional and to let the driver
> decide whether it wants it or not.

Well, this change can be done, but still we need to decide how to export it ;)

The new edac_mc_handle_error() with replaces all the legacy edac_mc_handle* calls
does what the other calls used to do. I didn't change its behavior. Anyway, what
it does for UE errors is:

	...
	/* Some logic to get the memory DIMM labels */
        trace_mc_error(type, mci->mc_idx, msg, label, location,
                       detail, other_detail);
	
	if (type == HW_EVENT_ERR_CORRECTED) {
		...
        } else {
		...
                if (edac_mc_get_log_ue())
                        edac_mc_printk(mci, KERN_WARNING,
                                "UE %s on %s (%s%s %s)\n",
                                msg, label, location, detail, other_detail);

                if (edac_mc_get_panic_on_ue())
                        panic("UE %s on %s (%s%s %s)\n",
                              msg, label, location, detail, other_detail);

                edac_increment_ue_error(mci, enable_filter, pos);
        }

So, it basically:
	1) prints the memory location and the DIMM label(s) of the memory(ies)
	   from where the error originates;
	2) if edac_mc_panic_on_ue is set, it will panic;
	3) otherwise, it will increment the UE error counters.

It shouldn't be hard to add a patch to disable the sysfs error UE counters if 
edac_mc_panic_on_ue is enabled.

Anyway, an UE error with a 128 bits cacheline points to a location that has
two DIMMs (or 4 DIMMs, on memory controllers with mirror mode enabled). So,
incrementing a DIMM error counter doesn't seem to be the right thing to do.

Well, it may increment two DIMM error counters (or 4 DIMM error counters), but
it would change the current behavior.

It should also be noticed that the MCA-based Intel memory controllers have the 
(likely limited) capability of recovering from an UE error. So, an UE error 
may not mean a fatal error. So, the UE error counter value can actually be
bigger than 1.

> 
> [..]
> 
>> One alternative would simply to remove all those intermediate
>> counters, letting userspace to count the errors via perf (provided
>> that we have a proper location field).
> 
> Yes, that would be where we want to go eventually because I too don't
> see any reason for those counters. Besides, they don't decay over time,
> for example, say you have a DIMM which experiences a temporary failure
> and generates k CEs. Then, the source of that error disappears and the
> DIMM works fine for months.

Userspace applications may reset the error counters. There is a sysfs node
for it.

> Now, when you look at the counters, you'll still see k CEs in one of its
> ranks which doesn't tell you when those errors happened and what their
> rate was, etc.

Yeah, a proper handling for CE/UE errors is to log them into some
Element Management System (or Network Management System), and let the EMS/NMS
to generate not only the error counters, but also the error rate counters.

For this to happen, the EMS/NMS should be capable of parsing the error location
and the DIMM labels, in order to provide per-DIMM and per location counters.

> So, I'm fine with dropping those counters since they don't give you the
> flexibility of a userspace tool and they don't work properly anyway.
> 
> HOWEVER, I don't know who uses them still so probably a deprecation
> warning is in order here...

Mark's edac-utils edac-ctl application use those counters. I know it is
used on RHEL (and RHEL-based distros), Fedora and Debian/Ubuntu. Not sure
if it is packaged for other distros.

I don't know any other EDAC public tool.

Mark,

any comments with regards to the error counters?

Regards,
Mauro

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-11 12:32               ` Mauro Carvalho Chehab
@ 2012-03-12 16:39                 ` Borislav Petkov
  2012-03-12 17:03                   ` Luck, Tony
  0 siblings, 1 reply; 41+ messages in thread
From: Borislav Petkov @ 2012-03-12 16:39 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Mark A. Grondona, Linux Edac Mailing List,
	Linux Kernel Mailing List

On Sun, Mar 11, 2012 at 09:32:44AM -0300, Mauro Carvalho Chehab wrote:
> Well, this change can be done, but still we need to decide how to export it ;)
> 
> The new edac_mc_handle_error() with replaces all the legacy edac_mc_handle* calls
> does what the other calls used to do. I didn't change its behavior. Anyway, what
> it does for UE errors is:
> 
> 	...
> 	/* Some logic to get the memory DIMM labels */
>         trace_mc_error(type, mci->mc_idx, msg, label, location,
>                        detail, other_detail);
> 	
> 	if (type == HW_EVENT_ERR_CORRECTED) {
> 		...
>         } else {
> 		...
>                 if (edac_mc_get_log_ue())
>                         edac_mc_printk(mci, KERN_WARNING,
>                                 "UE %s on %s (%s%s %s)\n",
>                                 msg, label, location, detail, other_detail);
> 
>                 if (edac_mc_get_panic_on_ue())
>                         panic("UE %s on %s (%s%s %s)\n",
>                               msg, label, location, detail, other_detail);
> 
>                 edac_increment_ue_error(mci, enable_filter, pos);
>         }
> 
> So, it basically:
> 	1) prints the memory location and the DIMM label(s) of the memory(ies)
> 	   from where the error originates;
> 	2) if edac_mc_panic_on_ue is set, it will panic;
> 	3) otherwise, it will increment the UE error counters.
> 
> It shouldn't be hard to add a patch to disable the sysfs error UE counters if 
> edac_mc_panic_on_ue is enabled.

Err, the fact that you have UE counters doesn't have anything to do with
the request that you want to panic on an UE. Especially if conservative
systems would panic on the first UE anyway without asking software.

So what I meant was to make it optional in the core edac code whether
you want to install a UE counter in the ranks or not. So that, for
example, if amd64_edac doesn't want to have UE counters, it simply
says so and the core generates only CE counters per rank. Or, with
positive logic, an edac driver explicitly requests what counters it
wants installed.

> Anyway, an UE error with a 128 bits cacheline points to a location that has
> two DIMMs (or 4 DIMMs, on memory controllers with mirror mode enabled). So,
> incrementing a DIMM error counter doesn't seem to be the right thing to do.
> 
> Well, it may increment two DIMM error counters (or 4 DIMM error counters), but
> it would change the current behavior.
> 
> It should also be noticed that the MCA-based Intel memory controllers have the 
> (likely limited) capability of recovering from an UE error. So, an UE error 
> may not mean a fatal error. So, the UE error counter value can actually be
> bigger than 1.

Yes, that's why make it optional - if the hardware can support it,
it can have it. If it doesn't make sense, then no need for it - that
simple.

> 
> > 
> > [..]
> > 
> >> One alternative would simply to remove all those intermediate
> >> counters, letting userspace to count the errors via perf (provided
> >> that we have a proper location field).
> > 
> > Yes, that would be where we want to go eventually because I too don't
> > see any reason for those counters. Besides, they don't decay over time,
> > for example, say you have a DIMM which experiences a temporary failure
> > and generates k CEs. Then, the source of that error disappears and the
> > DIMM works fine for months.
> 
> Userspace applications may reset the error counters. There is a sysfs node
> for it.

No, I'm not talking about resetting but decaying. I.e., each
error counted has a certain validity and gets discarded
after a while - similar to the leaky bucket algorithm:
http://en.wikipedia.org/wiki/Leaky_bucket

Thanks.

-- 
Regards/Gruss,
Boris.

Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
GM: Alberto Bozzo
Reg: Dornach, Landkreis Muenchen
HRB Nr. 43632 WEEE Registernr: 129 19551

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 0/6] Add a per-dimm structure
  2012-03-12 16:39                 ` Borislav Petkov
@ 2012-03-12 17:03                   ` Luck, Tony
  2012-03-12 18:10                     ` Borislav Petkov
  0 siblings, 1 reply; 41+ messages in thread
From: Luck, Tony @ 2012-03-12 17:03 UTC (permalink / raw)
  To: Borislav Petkov, Mauro Carvalho Chehab
  Cc: Mark A. Grondona, Linux Edac Mailing List,
	Linux Kernel Mailing List

> Err, the fact that you have UE counters doesn't have anything to do with
> the request that you want to panic on an UE. Especially if conservative
> systems would panic on the first UE anyway without asking software.

We have some UE events for which even a conservative system might
think it reasonable to defer the panic (e.g. patrol scrub on a "free" page).

-Tony

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-12 17:03                   ` Luck, Tony
@ 2012-03-12 18:10                     ` Borislav Petkov
  0 siblings, 0 replies; 41+ messages in thread
From: Borislav Petkov @ 2012-03-12 18:10 UTC (permalink / raw)
  To: Luck, Tony
  Cc: Mauro Carvalho Chehab, Mark A. Grondona, Linux Edac Mailing List,
	Linux Kernel Mailing List

On Mon, Mar 12, 2012 at 05:03:47PM +0000, Luck, Tony wrote:
> > Err, the fact that you have UE counters doesn't have anything to do with
> > the request that you want to panic on an UE. Especially if conservative
> > systems would panic on the first UE anyway without asking software.
> 
> We have some UE events for which even a conservative system might
> think it reasonable to defer the panic (e.g. patrol scrub on a "free" page).

Right, and in that case you don't need an UE counter because it won't go
higher than 1, would it?

What my thought is, is that if the system panics on UE unconditionally
anyway (either by sw or a hw method), then you don't need to enable
panic_on_ue in edac because it does so anyway, and, as a result, you
don't need UE counters at all. So, in that case, you could "state" in
the driver that you don't want to init the UE counters because you're
not going to use them, no?

-- 
Regards/Gruss,
Boris.

Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
GM: Alberto Bozzo
Reg: Dornach, Landkreis Muenchen
HRB Nr. 43632 WEEE Registernr: 129 19551

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-07 11:40 [PATCH 0/6] Add a per-dimm structure Mauro Carvalho Chehab
                   ` (6 preceding siblings ...)
  2012-03-08 21:57 ` [PATCH 0/6] Add a per-dimm structure Borislav Petkov
@ 2012-03-13 23:32 ` Greg KH
  2012-03-14 19:35   ` Mauro Carvalho Chehab
  7 siblings, 1 reply; 41+ messages in thread
From: Greg KH @ 2012-03-13 23:32 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: Linux Edac Mailing List, Linux Kernel Mailing List

On Wed, Mar 07, 2012 at 08:40:32AM -0300, Mauro Carvalho Chehab wrote:
> Prepare the internal structures to represent the memory properties per dimm, 
> instead of per csrow. 
> 
> This is needed for modern controllers with more than 2 channels, as the memories 
> at the same slot number but on different channels (or channel pairs) may be 
> different.
> 
> Mauro Carvalho Chehab (6):
>   edac: Create a dimm struct and move the labels into it
>   edac: Add per dimm's sysfs nodes

You need Documentation/ABI entries for these new sysfs files.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-13 23:32 ` Greg KH
@ 2012-03-14 19:35   ` Mauro Carvalho Chehab
  2012-03-14 20:43     ` Greg KH
  0 siblings, 1 reply; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-14 19:35 UTC (permalink / raw)
  To: Greg KH; +Cc: Linux Edac Mailing List, Linux Kernel Mailing List

Em 13-03-2012 20:32, Greg KH escreveu:
> On Wed, Mar 07, 2012 at 08:40:32AM -0300, Mauro Carvalho Chehab wrote:
>> Prepare the internal structures to represent the memory properties per dimm, 
>> instead of per csrow. 
>>
>> This is needed for modern controllers with more than 2 channels, as the memories 
>> at the same slot number but on different channels (or channel pairs) may be 
>> different.
>>
>> Mauro Carvalho Chehab (6):
>>   edac: Create a dimm struct and move the labels into it
>>   edac: Add per dimm's sysfs nodes
> 
> You need Documentation/ABI entries for these new sysfs files.

Sure. I'll provide it on the final patchset.

Boris suggested some alternatives for the error counter sysfs nodes, but the 
discussion ended by diverging into an implementation detail of hiding the UE
error nodes, without any consensus about the sysfs structure for it.

At the current patchset, the error counter nodes are all under
	sys/devices/system/edac/mc/mc?/

He thinks that a multi-layer struct should be created inside that directory
(it could have 2 or 3 levels of directories, depending on how the memory is
organized at the memory controller), instead of having a large number of files
there.

Anyway, before adding unnedded complexity, I'd like to hear more comments from 
the others before writing a complex patch to create such structure.

So, maybe I could just add what it was there as ABI/testing, and give more
time for kernel and userspace developers to work with it and provide us a better
feedback.

Thanks,
Mauro

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-14 19:35   ` Mauro Carvalho Chehab
@ 2012-03-14 20:43     ` Greg KH
  2012-03-14 22:20       ` Mauro Carvalho Chehab
  2012-03-14 22:31       ` Borislav Petkov
  0 siblings, 2 replies; 41+ messages in thread
From: Greg KH @ 2012-03-14 20:43 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: Linux Edac Mailing List, Linux Kernel Mailing List

On Wed, Mar 14, 2012 at 04:35:00PM -0300, Mauro Carvalho Chehab wrote:
> Em 13-03-2012 20:32, Greg KH escreveu:
> > On Wed, Mar 07, 2012 at 08:40:32AM -0300, Mauro Carvalho Chehab wrote:
> >> Prepare the internal structures to represent the memory properties per dimm, 
> >> instead of per csrow. 
> >>
> >> This is needed for modern controllers with more than 2 channels, as the memories 
> >> at the same slot number but on different channels (or channel pairs) may be 
> >> different.
> >>
> >> Mauro Carvalho Chehab (6):
> >>   edac: Create a dimm struct and move the labels into it
> >>   edac: Add per dimm's sysfs nodes
> > 
> > You need Documentation/ABI entries for these new sysfs files.
> 
> Sure. I'll provide it on the final patchset.
> 
> Boris suggested some alternatives for the error counter sysfs nodes, but the 
> discussion ended by diverging into an implementation detail of hiding the UE
> error nodes, without any consensus about the sysfs structure for it.
> 
> At the current patchset, the error counter nodes are all under
> 	sys/devices/system/edac/mc/mc?/
> 
> He thinks that a multi-layer struct should be created inside that directory
> (it could have 2 or 3 levels of directories, depending on how the memory is
> organized at the memory controller), instead of having a large number of files
> there.

Why create subdirs?  If those subdirectories are not real devices,
showing a real hierarchy, then do not create them as userspace will get
very confused very quickly.

Easy rule to remember, never mix "raw" kobjects and 'struct device',
which is what you would be doing here, right?  We can handle many
hundreds of thousands of files and devices in a single directory, no
problem.

> Anyway, before adding unnedded complexity, I'd like to hear more comments from 
> the others before writing a complex patch to create such structure.
> 
> So, maybe I could just add what it was there as ABI/testing, and give more
> time for kernel and userspace developers to work with it and provide us a better
> feedback.

That's a nice dream, it usually never happens until a few kernel
releases, after people have already written tools that rely on the
existing structure :)

Feel free to cc: me on any of these patches if you want some review of
the sysfs layout and usage.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-14 20:43     ` Greg KH
@ 2012-03-14 22:20       ` Mauro Carvalho Chehab
  2012-03-14 23:32         ` Greg KH
  2012-03-14 22:31       ` Borislav Petkov
  1 sibling, 1 reply; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-14 22:20 UTC (permalink / raw)
  To: Greg KH; +Cc: Linux Edac Mailing List, Linux Kernel Mailing List

Em 14-03-2012 17:43, Greg KH a:
> On Wed, Mar 14, 2012 at 04:35:00PM -0300, Mauro Carvalho Chehab wrote:
>> Em 13-03-2012 20:32, Greg KH escreveu:
>>> On Wed, Mar 07, 2012 at 08:40:32AM -0300, Mauro Carvalho Chehab wrote:
>>>> Prepare the internal structures to represent the memory properties per dimm, 
>>>> instead of per csrow. 
>>>>
>>>> This is needed for modern controllers with more than 2 channels, as the memories 
>>>> at the same slot number but on different channels (or channel pairs) may be 
>>>> different.
>>>>
>>>> Mauro Carvalho Chehab (6):
>>>>   edac: Create a dimm struct and move the labels into it
>>>>   edac: Add per dimm's sysfs nodes
>>>
>>> You need Documentation/ABI entries for these new sysfs files.
>>
>> Sure. I'll provide it on the final patchset.
>>
>> Boris suggested some alternatives for the error counter sysfs nodes, but the 
>> discussion ended by diverging into an implementation detail of hiding the UE
>> error nodes, without any consensus about the sysfs structure for it.
>>
>> At the current patchset, the error counter nodes are all under
>> 	sys/devices/system/edac/mc/mc?/
>>
>> He thinks that a multi-layer struct should be created inside that directory
>> (it could have 2 or 3 levels of directories, depending on how the memory is
>> organized at the memory controller), instead of having a large number of files
>> there.
> 
> Why create subdirs?  If those subdirectories are not real devices,
> showing a real hierarchy, then do not create them as userspace will get
> very confused very quickly.

Yes, I think so. 

That's the sysfs structure proposed on those patchsets:

The error counter registers for corrected errors (CE) and uncorrected 
errors (UE) will be like:

/sys/devices/system/edac/
└── mc
    ├── mc0
    │   ├── ce_csrow[0-i]
    │   ├── ce_csrow[0-i]_channel[0-j]
...
    │   ├── ce_count
    │   ├── ce_noinfo_count
...
    │   ├── ue_csrow[0-i]
    │   ├── ue_csrow[0-i]_channel[0-j]
...
    ├── ue_count
    └── ue_noinfo_count

The actual names for the error counters will depend on how the memory
controller addresses the memory. Currently, there are 3 possibilities:

	- csrow/channel - for drivers where the memory is addressed by rank;
	- channel/slot - for devices where the memory controller can properly
			 identify a DIMM slot;
	- branch/channel/slot - for FB-DIMM memory controllers with more
				than 2 channels, as those memory controllers
				group each channel pair into a branch.

This basically reflects the hierarchy used by the memory controller in
order to see the memory chips.

When an error occurs, the driver increments the pertinent counters.
For example, a CE error on a dimm located at csrow 3 channel 1 will
increment:
	ce_csrow3_channel1
	ce_csrow3
	ce_count

On several cases, it is not possible to point to a single DIMM or rank. On
such case, only the higher hierarchy counters will be incremented. For
example, an Uncorrected Error at csrow3 with a 128 cacheline will
increment only:
	ce_csrow3
	ce_count

As the ECC chipkill algorithm in general is not able to tell if the error
happened at the rank located at channel 0 or channel 1.

The special ce_noinfo_count/ue_noinfo_count counters are there signalize
that an error occurred but the driver couldn't get the error location.

The ce_count/ue_count/ue_noinfo_count/ce_noinfo_count are part of the
current API. That's why I added the other counters there, and used a
nomenclature close to the existing one.


The per-rank/per-dimm memory struct that contains the memory information 
(size, type, location, etc), and it will be like:

/sys/devices/system/edac/
└── mc
    ├── mc0
    │   ├── (rank|dimm)[0-n]
    │   │   ├── dimm_dev_type
    │   │   ├── dimm_edac_mode
    │   │   ├── dimm_label
    │   │   ├── dimm_location
    │   │   ├── dimm_mem_type
    │   │   └── dimm_size

There are a few other nodes that are untouched by this patchset and
will remain there:

/sys/devices/system/edac/
└── mc
    ├── mc0
    │   ├── device -> ../../../../pci0000:00/0000:00:18.2
    │   ├── reset_counters
    │   ├── sdram_scrub_rate
    │   ├── seconds_since_reset
    │   └── size_mb

(there are also a few driver-specific sysfs nodes - most due to error injection,
 and, on some devices, device nodes for erros on the PCI bus - none of them
 touched on those series)

And finally, the nodes that are redundant and, IMO, should be deprecated on some
future kernel version:

/sys/devices/system/edac
├── mc
│   ├── mc0
│   │   ├── csrow[0-i]
│   │   │   ├── ce_count
│   │   │   ├── ch0_ce_count
│   │   │   ├── ch0_dimm_label
...
│   │   │   ├── ch[j]_ce_count
│   │   │   ├── ch[j]_dimm_label
│   │   │   ├── dev_type
│   │   │   ├── edac_mode
│   │   │   ├── mem_type
│   │   │   ├── size_mb
│   │   │   └── ue_count
...

On the above:
	/csrow3/ch1_ce_count
is equivalent to: 
	/ce_csrow3_channel1

and
	/csrow3/ce_count
is equivalent to:
	/ce_csrow3

> Easy rule to remember, never mix "raw" kobjects and 'struct device',
> which is what you would be doing here, right?  We can handle many
> hundreds of thousands of files and devices in a single directory, no
> problem.

No. They're all generated with raw kobjects, using kobject_init_and_add() or
sysfs_create_file() calls.

>> Anyway, before adding unnedded complexity, I'd like to hear more comments from 
>> the others before writing a complex patch to create such structure.
>>
>> So, maybe I could just add what it was there as ABI/testing, and give more
>> time for kernel and userspace developers to work with it and provide us a better
>> feedback.
> 
> That's a nice dream, it usually never happens until a few kernel
> releases, after people have already written tools that rely on the
> existing structure :)

:)

Well, on the test tools I wrote to test the patches, it is a way easier to
parse the error counters on just one directory, than to write a shell script
that would navigate on a complex multi-directory layer with 2 or 3 levels.

> Feel free to cc: me on any of these patches if you want some review of
> the sysfs layout and usage.

Thanks! I'll do it on my next submission.

Thanks,
Mauro

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-14 20:43     ` Greg KH
  2012-03-14 22:20       ` Mauro Carvalho Chehab
@ 2012-03-14 22:31       ` Borislav Petkov
  2012-03-14 22:40         ` Greg KH
  2012-03-15  1:44         ` Mauro Carvalho Chehab
  1 sibling, 2 replies; 41+ messages in thread
From: Borislav Petkov @ 2012-03-14 22:31 UTC (permalink / raw)
  To: Greg KH
  Cc: Mauro Carvalho Chehab, Linux Edac Mailing List,
	Linux Kernel Mailing List

On Wed, Mar 14, 2012 at 01:43:55PM -0700, Greg KH wrote:
> > He thinks that a multi-layer struct should be created inside that directory
> > (it could have 2 or 3 levels of directories, depending on how the memory is
> > organized at the memory controller), instead of having a large number of files
> > there.
> 
> Why create subdirs?  If those subdirectories are not real devices,
> showing a real hierarchy, then do not create them as userspace will get
> very confused very quickly.

Why, IMO, we agreed on the following layout

rank0/
|-- dimm_dev_type
|-- dimm_edac_mode
|-- dimm_label
|-- dimm_location
|-- dimm_mem_type
|-- CE
|-- UE
`-- dimm_size

with CE being the correctable errors counter and UE be optional and only
present when it makes sense for the hardware.

This is not multi-layered, it is very easy to implement and mirrors all
possible memory controllers from the point of view of the single DIMM.
no? Or am I missing something...?

Thanks.

-- 
Regards/Gruss,
Boris.

Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
GM: Alberto Bozzo
Reg: Dornach, Landkreis Muenchen
HRB Nr. 43632 WEEE Registernr: 129 19551

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-14 22:31       ` Borislav Petkov
@ 2012-03-14 22:40         ` Greg KH
  2012-03-15  1:37           ` Mauro Carvalho Chehab
  2012-03-15  1:44         ` Mauro Carvalho Chehab
  1 sibling, 1 reply; 41+ messages in thread
From: Greg KH @ 2012-03-14 22:40 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Mauro Carvalho Chehab, Linux Edac Mailing List,
	Linux Kernel Mailing List

On Wed, Mar 14, 2012 at 11:31:02PM +0100, Borislav Petkov wrote:
> On Wed, Mar 14, 2012 at 01:43:55PM -0700, Greg KH wrote:
> > > He thinks that a multi-layer struct should be created inside that directory
> > > (it could have 2 or 3 levels of directories, depending on how the memory is
> > > organized at the memory controller), instead of having a large number of files
> > > there.
> > 
> > Why create subdirs?  If those subdirectories are not real devices,
> > showing a real hierarchy, then do not create them as userspace will get
> > very confused very quickly.
> 
> Why, IMO, we agreed on the following layout
> 
> rank0/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> |-- CE
> |-- UE
> `-- dimm_size
> 
> with CE being the correctable errors counter and UE be optional and only
> present when it makes sense for the hardware.
> 
> This is not multi-layered, it is very easy to implement and mirrors all
> possible memory controllers from the point of view of the single DIMM.
> no? Or am I missing something...?

Looks fine to me, assuming "rankX" is the dimm itself?

greg k-h

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-14 22:20       ` Mauro Carvalho Chehab
@ 2012-03-14 23:32         ` Greg KH
  2012-03-15  2:22           ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 41+ messages in thread
From: Greg KH @ 2012-03-14 23:32 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: Linux Edac Mailing List, Linux Kernel Mailing List

On Wed, Mar 14, 2012 at 07:20:01PM -0300, Mauro Carvalho Chehab wrote:
> > Easy rule to remember, never mix "raw" kobjects and 'struct device',
> > which is what you would be doing here, right?  We can handle many
> > hundreds of thousands of files and devices in a single directory, no
> > problem.
> 
> No. They're all generated with raw kobjects, using kobject_init_and_add() or
> sysfs_create_file() calls.

Ick, no.

Please, never use "raw" kobjects.  Why doesn't 'struct device' work for
you properly here?  You just messed userspace all up in that it can not
properly account for the parts of sysfs here in the device tree.

Anything below /sys/devices/ should be using 'struct device' unless
there is a _very_ good reason not to.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-14 22:40         ` Greg KH
@ 2012-03-15  1:37           ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-15  1:37 UTC (permalink / raw)
  To: Greg KH; +Cc: Borislav Petkov, Linux Edac Mailing List,
	Linux Kernel Mailing List

Em 14-03-2012 19:40, Greg KH escreveu:
> On Wed, Mar 14, 2012 at 11:31:02PM +0100, Borislav Petkov wrote:
>> On Wed, Mar 14, 2012 at 01:43:55PM -0700, Greg KH wrote:
>>>> He thinks that a multi-layer struct should be created inside that directory
>>>> (it could have 2 or 3 levels of directories, depending on how the memory is
>>>> organized at the memory controller), instead of having a large number of files
>>>> there.
>>>
>>> Why create subdirs?  If those subdirectories are not real devices,
>>> showing a real hierarchy, then do not create them as userspace will get
>>> very confused very quickly.
>>
>> Why, IMO, we agreed on the following layout
>>
>> rank0/
>> |-- dimm_dev_type
>> |-- dimm_edac_mode
>> |-- dimm_label
>> |-- dimm_location
>> |-- dimm_mem_type
>> |-- CE
>> |-- UE
>> `-- dimm_size
>>
>> with CE being the correctable errors counter and UE be optional and only
>> present when it makes sense for the hardware.
>>
>> This is not multi-layered, it is very easy to implement and mirrors all
>> possible memory controllers from the point of view of the single DIMM.
>> no? Or am I missing something...?
> 
> Looks fine to me, assuming "rankX" is the dimm itself?

The node name can be either "rankX" or "dimmX", depending if the memory controller
is able to detect wich memory ranks belong to each DIMM or not.

> 
> greg k-h
> --
> To unsubscribe from this list: send the line "unsubscribe linux-edac" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-14 22:31       ` Borislav Petkov
  2012-03-14 22:40         ` Greg KH
@ 2012-03-15  1:44         ` Mauro Carvalho Chehab
  2012-03-15 11:31           ` Borislav Petkov
  1 sibling, 1 reply; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-15  1:44 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Greg KH, Linux Edac Mailing List, Linux Kernel Mailing List

Em 14-03-2012 19:31, Borislav Petkov escreveu:
> On Wed, Mar 14, 2012 at 01:43:55PM -0700, Greg KH wrote:
>>> He thinks that a multi-layer struct should be created inside that directory
>>> (it could have 2 or 3 levels of directories, depending on how the memory is
>>> organized at the memory controller), instead of having a large number of files
>>> there.
>>
>> Why create subdirs?  If those subdirectories are not real devices,
>> showing a real hierarchy, then do not create them as userspace will get
>> very confused very quickly.
> 
> Why, IMO, we agreed on the following layout
> 
> rank0/
> |-- dimm_dev_type
> |-- dimm_edac_mode
> |-- dimm_label
> |-- dimm_location
> |-- dimm_mem_type
> |-- CE
> |-- UE
> `-- dimm_size
> 
> with CE being the correctable errors counter and UE be optional and only
> present when it makes sense for the hardware.

As I said, that is easy to implement. The hard part would be what to do with
the per-csrow/per-branch error counters that exist currently at EDAC.

>From my side, I'm OK to remove them, but, as I said before, existing user tools
use them, especially because UE errors aren't per-rank/per-dimm on the
typical case (128 bits cacheline).

Of course, the EDAC logic could increment multiple UE error counters in such
case, (meaning that an error happened on either one of the affected DIMMs/Ranks)
but this is a different behavior than the current API.

Regards,
Mauro

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-14 23:32         ` Greg KH
@ 2012-03-15  2:22           ` Mauro Carvalho Chehab
  2012-03-15 15:00             ` Greg KH
  0 siblings, 1 reply; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-15  2:22 UTC (permalink / raw)
  To: Greg KH; +Cc: Linux Edac Mailing List, Linux Kernel Mailing List

Em 14-03-2012 20:32, Greg KH escreveu:
> On Wed, Mar 14, 2012 at 07:20:01PM -0300, Mauro Carvalho Chehab wrote:
>>> Easy rule to remember, never mix "raw" kobjects and 'struct device',
>>> which is what you would be doing here, right?  We can handle many
>>> hundreds of thousands of files and devices in a single directory, no
>>> problem.
>>
>> No. They're all generated with raw kobjects, using kobject_init_and_add() or
>> sysfs_create_file() calls.
> 
> Ick, no.
> 
> Please, never use "raw" kobjects.  Why doesn't 'struct device' work for
> you properly here?  You just messed userspace all up in that it can not
> properly account for the parts of sysfs here in the device tree.
> 
> Anything below /sys/devices/ should be using 'struct device' unless
> there is a _very_ good reason not to.

The raw kobjects logic is there since the beginning of the edac. I've no
idea why.

I remember a few years ago I tried to create a virtual class in order to
create the EDAC devices, in order to use struct devices, but the sysfs
nodes were created outside /sys/devices/system, breaking the ABI.

Not sure what changed on sysfs since then, but, on the time I looked
on it, I was unable to find an easy way to solve it.

So, I gave up and just kept it as-is.

> 
> thanks,
> 
> greg k-h


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-15  1:44         ` Mauro Carvalho Chehab
@ 2012-03-15 11:31           ` Borislav Petkov
  2012-03-15 12:40             ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 41+ messages in thread
From: Borislav Petkov @ 2012-03-15 11:31 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Borislav Petkov, Greg KH, Linux Edac Mailing List,
	Linux Kernel Mailing List

On Wed, Mar 14, 2012 at 10:44:13PM -0300, Mauro Carvalho Chehab wrote:
> As I said, that is easy to implement. The hard part would be what to do with
> the per-csrow/per-branch error counters that exist currently at EDAC.
> 
> From my side, I'm OK to remove them, but, as I said before, existing user tools
> use them,

What are you talking about? Those per-rank counters should be the same
as the per-csrow ch0 and ch1 counters...

> especially because UE errors aren't per-rank/per-dimm on the
> typical case (128 bits cacheline).

It depends - if the 128 bit word comes from a single DIMM (unganged
mode) then you have a per-rank UE.

> Of course, the EDAC logic could increment multiple UE error counters
> in such case, (meaning that an error happened on either one of the
> affected DIMMs/Ranks) but this is a different behavior than the
> current API.

Well, the API should be changed to accomodate such configurations.

-- 
Regards/Gruss,
Boris.

Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
GM: Alberto Bozzo
Reg: Dornach, Landkreis Muenchen
HRB Nr. 43632 WEEE Registernr: 129 19551

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-15 11:31           ` Borislav Petkov
@ 2012-03-15 12:40             ` Mauro Carvalho Chehab
  2012-03-15 21:38               ` Borislav Petkov
  0 siblings, 1 reply; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-15 12:40 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Greg KH, Linux Edac Mailing List, Linux Kernel Mailing List

Em 15-03-2012 08:31, Borislav Petkov escreveu:
> On Wed, Mar 14, 2012 at 10:44:13PM -0300, Mauro Carvalho Chehab wrote:
>> As I said, that is easy to implement. The hard part would be what to do with
>> the per-csrow/per-branch error counters that exist currently at EDAC.
>>
>> From my side, I'm OK to remove them, but, as I said before, existing user tools
>> use them,
> 
> What are you talking about? Those per-rank counters should be the same
> as the per-csrow ch0 and ch1 counters...

Yes, but with your proposal, the per-csrow counters will not be added
(the equivalent of):
	/sys/devices/system/edac/mc/mc0/csrow0/ue_count
	/sys/devices/system/edac/mc/mc0/csrow0/ce_count

>> especially because UE errors aren't per-rank/per-dimm on the
>> typical case (128 bits cacheline).
> 
> It depends - if the 128 bit word comes from a single DIMM (unganged
> mode) then you have a per-rank UE.

True, and there are other types of ECC logic that would allow to identify
what DIMM/rank produced the error.

Yet, the typical case is to use two DIMMs for a 128-bits cacheline
on separate channels, due to performance improvements, and ECC chipkill
using the 128+16 bits, as it improves the probability of error correction.

>> Of course, the EDAC logic could increment multiple UE error counters
>> in such case, (meaning that an error happened on either one of the
>> affected DIMMs/Ranks) but this is a different behavior than the
>> current API.
> 
> Well, the API should be changed to accomodate such configurations.

True, but changing the propagation logic to propagate the error down
to the several DIMMs from where the error might have occurred is:

	- the opposite of the current propagation logic;

	- the opposite on how ITU-T TMN architecture and all EMS/NMS
	  implementations I'm aware with work.

So, using such propagation logic doesn't sound right to me. What I'm
saying is that, if all the driver can be sure is that the error happened
at the csrow level, it should not propagate the errors to the channel
level. 

So, I think that csrow-level counter is needed (and the equivalent
"group" counters for non-rank-based memory controllers).

Regards,
Mauro.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-15  2:22           ` Mauro Carvalho Chehab
@ 2012-03-15 15:00             ` Greg KH
  0 siblings, 0 replies; 41+ messages in thread
From: Greg KH @ 2012-03-15 15:00 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: Linux Edac Mailing List, Linux Kernel Mailing List

On Wed, Mar 14, 2012 at 11:22:21PM -0300, Mauro Carvalho Chehab wrote:
> Em 14-03-2012 20:32, Greg KH escreveu:
> > On Wed, Mar 14, 2012 at 07:20:01PM -0300, Mauro Carvalho Chehab wrote:
> >>> Easy rule to remember, never mix "raw" kobjects and 'struct device',
> >>> which is what you would be doing here, right?  We can handle many
> >>> hundreds of thousands of files and devices in a single directory, no
> >>> problem.
> >>
> >> No. They're all generated with raw kobjects, using kobject_init_and_add() or
> >> sysfs_create_file() calls.
> > 
> > Ick, no.
> > 
> > Please, never use "raw" kobjects.  Why doesn't 'struct device' work for
> > you properly here?  You just messed userspace all up in that it can not
> > properly account for the parts of sysfs here in the device tree.
> > 
> > Anything below /sys/devices/ should be using 'struct device' unless
> > there is a _very_ good reason not to.
> 
> The raw kobjects logic is there since the beginning of the edac. I've no
> idea why.

Probably because 'struct sysdev' was what /sys/devices/system/ used to
have, and it wasn't the easiest thing to work with.

I'd recommend converting the whole thing to struct device, if you are
going to be doing any work to this code.  I had figured this code was
not really needed anymore, but you've proved that wrong :)

good luck,

greg k-h

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-15 12:40             ` Mauro Carvalho Chehab
@ 2012-03-15 21:38               ` Borislav Petkov
  2012-03-16  8:47                 ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 41+ messages in thread
From: Borislav Petkov @ 2012-03-15 21:38 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Borislav Petkov, Greg KH, Linux Edac Mailing List,
	Linux Kernel Mailing List

On Thu, Mar 15, 2012 at 09:40:59AM -0300, Mauro Carvalho Chehab wrote:
> > What are you talking about? Those per-rank counters should be the same
> > as the per-csrow ch0 and ch1 counters...
> 
> Yes, but with your proposal, the per-csrow counters will not be added
> (the equivalent of):
> 	/sys/devices/system/edac/mc/mc0/csrow0/ue_count
> 	/sys/devices/system/edac/mc/mc0/csrow0/ce_count

What the hell? Those are already there:

/sys/devices/system/edac/mc/mc0/csrow0/
|-- ce_count
|-- ch0_ce_count
|-- ch0_dimm_label
|-- ch1_ce_count
|-- ch1_dimm_label
|-- dev_type
|-- edac_mode
|-- mem_type
|-- size_mb
`-- ue_count

and since userspace uses them, they cannot be removed.

> > It depends - if the 128 bit word comes from a single DIMM (unganged
> > mode) then you have a per-rank UE.
> 
> True, and there are other types of ECC logic that would allow to identify
> what DIMM/rank produced the error.
> 
> Yet, the typical case is to use two DIMMs for a 128-bits cacheline
> on separate channels, due to performance improvements, and ECC chipkill
> using the 128+16 bits, as it improves the probability of error correction.

... and in this typical case, on smart hardware you can get the rank
too. If one cannot discern between the two DIMMs, then there should be
one counter and the other one should be a symlink to that counter, or
something to that effect.

> >> Of course, the EDAC logic could increment multiple UE error counters
> >> in such case, (meaning that an error happened on either one of the
> >> affected DIMMs/Ranks) but this is a different behavior than the
> >> current API.
> > 
> > Well, the API should be changed to accomodate such configurations.
> 
> True, but changing the propagation logic to propagate the error down
> to the several DIMMs from where the error might have occurred is:
> 
> 	- the opposite of the current propagation logic;
> 
> 	- the opposite on how ITU-T TMN architecture and all EMS/NMS
> 	  implementations I'm aware with work.
> 
> So, using such propagation logic doesn't sound right to me. What I'm
> saying is that, if all the driver can be sure is that the error happened
> at the csrow level, it should not propagate the errors to the channel
> level. 
> 
> So, I think that csrow-level counter is needed (and the equivalent
> "group" counters for non-rank-based memory controllers).

See above, we already have 'ce_count' and 'ue_count' and those are
csrow-level counters.

> 
> Regards,
> Mauro.
> 

-- 
Regards/Gruss,
Boris.

Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
GM: Alberto Bozzo
Reg: Dornach, Landkreis Muenchen
HRB Nr. 43632 WEEE Registernr: 129 19551

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-15 21:38               ` Borislav Petkov
@ 2012-03-16  8:47                 ` Mauro Carvalho Chehab
  2012-03-16 11:15                   ` Borislav Petkov
  2012-03-16 15:30                   ` Greg KH
  0 siblings, 2 replies; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-16  8:47 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Greg KH, Linux Edac Mailing List, Linux Kernel Mailing List

Em 15-03-2012 18:38, Borislav Petkov escreveu:
> On Thu, Mar 15, 2012 at 09:40:59AM -0300, Mauro Carvalho Chehab wrote:
>>> What are you talking about? Those per-rank counters should be the same
>>> as the per-csrow ch0 and ch1 counters...
>>
>> Yes, but with your proposal, the per-csrow counters will not be added
>> (the equivalent of):
>> 	/sys/devices/system/edac/mc/mc0/csrow0/ue_count
>> 	/sys/devices/system/edac/mc/mc0/csrow0/ce_count
> 
> What the hell? Those are already there:
> 
> /sys/devices/system/edac/mc/mc0/csrow0/
> |-- ce_count
> |-- ch0_ce_count
> |-- ch0_dimm_label
> |-- ch1_ce_count
> |-- ch1_dimm_label
> |-- dev_type
> |-- edac_mode
> |-- mem_type
> |-- size_mb
> `-- ue_count
> 
> and since userspace uses them, they cannot be removed.

Two reasons:

1) a per-csrow counter only works properly for amd64 and the Intel drivers for
   hardware shipped 5+ years ago;

2) per Greg's request, we should not use struct sysdev, using instead,
   struct device. That means that everything under
	/sys/devices/system/edac/mc/
   will move to another place, likely:
	/sys/devices/ras/

and everything at /sys/devices/system/edac/mc/ will need to be provided by a
legacy compat API code, to be dropped on some future.

>>> It depends - if the 128 bit word comes from a single DIMM (unganged
>>> mode) then you have a per-rank UE.
>>
>> True, and there are other types of ECC logic that would allow to identify
>> what DIMM/rank produced the error.
>>
>> Yet, the typical case is to use two DIMMs for a 128-bits cacheline
>> on separate channels, due to performance improvements, and ECC chipkill
>> using the 128+16 bits, as it improves the probability of error correction.
> 
> ... and in this typical case, on smart hardware you can get the rank
> too. 

I've analyzed all edac drivers. Almost all points to a single
DIMM or rank on UE errors. They can only blame two dimms/ranks. The
very few ones that will point to a single DIMM seem to be due to a
the way they cheat with the EDAC API in order to implement support
for FB-DIMM (or RAMBUS), telling the EDAC core that there's just one
channel.

> If one cannot discern between the two DIMMs, then there should be
> one counter and the other one should be a symlink to that counter, or
> something to that effect.

Doesn't sound easy to implement it, nor for userspace to convert back
from a series of symlinks into a set of locations.

>>>> Of course, the EDAC logic could increment multiple UE error counters
>>>> in such case, (meaning that an error happened on either one of the
>>>> affected DIMMs/Ranks) but this is a different behavior than the
>>>> current API.
>>>
>>> Well, the API should be changed to accomodate such configurations.
>>
>> True, but changing the propagation logic to propagate the error down
>> to the several DIMMs from where the error might have occurred is:
>>
>> 	- the opposite of the current propagation logic;
>>
>> 	- the opposite on how ITU-T TMN architecture and all EMS/NMS
>> 	  implementations I'm aware with work.
>>
>> So, using such propagation logic doesn't sound right to me. What I'm
>> saying is that, if all the driver can be sure is that the error happened
>> at the csrow level, it should not propagate the errors to the channel
>> level. 
>>
>> So, I think that csrow-level counter is needed (and the equivalent
>> "group" counters for non-rank-based memory controllers).
> 
> See above, we already have 'ce_count' and 'ue_count' and those are
> csrow-level counters.

They're not branch-level counters (a requirement for FB-DIMM drivers).

The role target of this patchset is to fix the core of the reporting system.
The old sysfs nodes don't work properly with Intel's systems
manufactured after 2005 (and with a few others manufactured before).

All Xeon 3xxx/5xxx/7xxx/E3 chipsets[1] use advanced memory controllers that 
abstract csrows, either because they're for FB-DIMMs (where the memory ranks and 
chip select rows aren't visible by the memory controller), or, more recently,
because their registers are per DIMM slots, instead of per ranks, and users
can't replace a damaged rank on a dual-ranked DIMM in field.

[1] The Intel's CPU/chipset series of Servers since 2005. Desktop processors
aren't relevant for EDAC, as they're either not supported, or they're 
supported by the same driver that provides support for servers.

Regards,
Mauro

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-16  8:47                 ` Mauro Carvalho Chehab
@ 2012-03-16 11:15                   ` Borislav Petkov
  2012-03-16 12:07                     ` Mauro Carvalho Chehab
  2012-03-16 15:30                   ` Greg KH
  1 sibling, 1 reply; 41+ messages in thread
From: Borislav Petkov @ 2012-03-16 11:15 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Borislav Petkov, Greg KH, Linux Edac Mailing List,
	Linux Kernel Mailing List

Lemme save you all the trouble:

There will be no breaking of userspace, no matter how wrong it is
implemented in the kernel. All sysfs nodes which are there now will
remain there until edac-ctl or something else uses them. So simply cut
the bullshit bingo and add only the changes needed to accomodate the
FBDIMM drivers _without_ _rewriting_ anything etc., and all the churn to
the whole EDAC core for no good reason.

As I've told you before, add a second edac_mc_alloc() function which
does everything needed for the FBDIMM drivers and is only called by
them. The other drivers don't need to know of any change.

On Fri, Mar 16, 2012 at 05:47:52AM -0300, Mauro Carvalho Chehab wrote:
> Em 15-03-2012 18:38, Borislav Petkov escreveu:
> > On Thu, Mar 15, 2012 at 09:40:59AM -0300, Mauro Carvalho Chehab wrote:
> >>> What are you talking about? Those per-rank counters should be the same
> >>> as the per-csrow ch0 and ch1 counters...
> >>
> >> Yes, but with your proposal, the per-csrow counters will not be added
> >> (the equivalent of):
> >> 	/sys/devices/system/edac/mc/mc0/csrow0/ue_count
> >> 	/sys/devices/system/edac/mc/mc0/csrow0/ce_count
> > 
> > What the hell? Those are already there:
> > 
> > /sys/devices/system/edac/mc/mc0/csrow0/
> > |-- ce_count
> > |-- ch0_ce_count
> > |-- ch0_dimm_label
> > |-- ch1_ce_count
> > |-- ch1_dimm_label
> > |-- dev_type
> > |-- edac_mode
> > |-- mem_type
> > |-- size_mb
> > `-- ue_count
> > 
> > and since userspace uses them, they cannot be removed.
> 
> Two reasons:
> 
> 1) a per-csrow counter only works properly for amd64 and the Intel drivers for
>    hardware shipped 5+ years ago;
> 
> 2) per Greg's request, we should not use struct sysdev, using instead,
>    struct device. That means that everything under
> 	/sys/devices/system/edac/mc/
>    will move to another place, likely:
> 	/sys/devices/ras/
> 
> and everything at /sys/devices/system/edac/mc/ will need to be provided by a
> legacy compat API code, to be dropped on some future.
> 
> >>> It depends - if the 128 bit word comes from a single DIMM (unganged
> >>> mode) then you have a per-rank UE.
> >>
> >> True, and there are other types of ECC logic that would allow to identify
> >> what DIMM/rank produced the error.
> >>
> >> Yet, the typical case is to use two DIMMs for a 128-bits cacheline
> >> on separate channels, due to performance improvements, and ECC chipkill
> >> using the 128+16 bits, as it improves the probability of error correction.
> > 
> > ... and in this typical case, on smart hardware you can get the rank
> > too. 
> 
> I've analyzed all edac drivers. Almost all points to a single
> DIMM or rank on UE errors. They can only blame two dimms/ranks. The
> very few ones that will point to a single DIMM seem to be due to a
> the way they cheat with the EDAC API in order to implement support
> for FB-DIMM (or RAMBUS), telling the EDAC core that there's just one
> channel.
> 
> > If one cannot discern between the two DIMMs, then there should be
> > one counter and the other one should be a symlink to that counter, or
> > something to that effect.
> 
> Doesn't sound easy to implement it, nor for userspace to convert back
> from a series of symlinks into a set of locations.
> 
> >>>> Of course, the EDAC logic could increment multiple UE error counters
> >>>> in such case, (meaning that an error happened on either one of the
> >>>> affected DIMMs/Ranks) but this is a different behavior than the
> >>>> current API.
> >>>
> >>> Well, the API should be changed to accomodate such configurations.
> >>
> >> True, but changing the propagation logic to propagate the error down
> >> to the several DIMMs from where the error might have occurred is:
> >>
> >> 	- the opposite of the current propagation logic;
> >>
> >> 	- the opposite on how ITU-T TMN architecture and all EMS/NMS
> >> 	  implementations I'm aware with work.
> >>
> >> So, using such propagation logic doesn't sound right to me. What I'm
> >> saying is that, if all the driver can be sure is that the error happened
> >> at the csrow level, it should not propagate the errors to the channel
> >> level. 
> >>
> >> So, I think that csrow-level counter is needed (and the equivalent
> >> "group" counters for non-rank-based memory controllers).
> > 
> > See above, we already have 'ce_count' and 'ue_count' and those are
> > csrow-level counters.
> 
> They're not branch-level counters (a requirement for FB-DIMM drivers).
> 
> The role target of this patchset is to fix the core of the reporting system.
> The old sysfs nodes don't work properly with Intel's systems
> manufactured after 2005 (and with a few others manufactured before).
> 
> All Xeon 3xxx/5xxx/7xxx/E3 chipsets[1] use advanced memory controllers that 
> abstract csrows, either because they're for FB-DIMMs (where the memory ranks and 
> chip select rows aren't visible by the memory controller), or, more recently,
> because their registers are per DIMM slots, instead of per ranks, and users
> can't replace a damaged rank on a dual-ranked DIMM in field.
> 
> [1] The Intel's CPU/chipset series of Servers since 2005. Desktop processors
> aren't relevant for EDAC, as they're either not supported, or they're 
> supported by the same driver that provides support for servers.
> 
> Regards,
> Mauro
> 

-- 
Regards/Gruss,
Boris.

Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
GM: Alberto Bozzo
Reg: Dornach, Landkreis Muenchen
HRB Nr. 43632 WEEE Registernr: 129 19551

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-16 11:15                   ` Borislav Petkov
@ 2012-03-16 12:07                     ` Mauro Carvalho Chehab
  2012-03-16 14:07                       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-16 12:07 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Greg KH, Linux Edac Mailing List, Linux Kernel Mailing List

Em 16-03-2012 08:15, Borislav Petkov escreveu:
> Lemme save you all the trouble:
> 
> There will be no breaking of userspace, no matter how wrong it is
> implemented in the kernel.

There is no breaking on userspace, as the old API will remain there,
provided by a legacy-compatibility code.

I would really prefer to not need to port it to system device, as this
is an additional work, but, on the other hand, the patches for it are
simple, and, as a new API is being proposed, Greg is right: it should
be done using the struct device using the current best practices, 
instead of a deprecated structure.

Anyway, I wrote the patches for it already after Greg's email.
I'm just about to test them.

> All sysfs nodes which are there now will
> remain there until edac-ctl or something else uses them. 

I'll write the patches for it, as soon as the patches got merged.

> So simply cut
> the bullshit bingo and add only the changes needed to accomodate the
> FBDIMM drivers _without_ _rewriting_ anything etc., and all the churn to
> the whole EDAC core for no good reason.
> 
> As I've told you before, add a second edac_mc_alloc() function which
> does everything needed for the FBDIMM drivers and is only called by
> them. The other drivers don't need to know of any change.

As I told you before, and as the patches showed, adding a second edac_mc_alloc
won't fix the broken API.

> 
> On Fri, Mar 16, 2012 at 05:47:52AM -0300, Mauro Carvalho Chehab wrote:
>> Em 15-03-2012 18:38, Borislav Petkov escreveu:
>>> On Thu, Mar 15, 2012 at 09:40:59AM -0300, Mauro Carvalho Chehab wrote:
>>>>> What are you talking about? Those per-rank counters should be the same
>>>>> as the per-csrow ch0 and ch1 counters...
>>>>
>>>> Yes, but with your proposal, the per-csrow counters will not be added
>>>> (the equivalent of):
>>>> 	/sys/devices/system/edac/mc/mc0/csrow0/ue_count
>>>> 	/sys/devices/system/edac/mc/mc0/csrow0/ce_count
>>>
>>> What the hell? Those are already there:
>>>
>>> /sys/devices/system/edac/mc/mc0/csrow0/
>>> |-- ce_count
>>> |-- ch0_ce_count
>>> |-- ch0_dimm_label
>>> |-- ch1_ce_count
>>> |-- ch1_dimm_label
>>> |-- dev_type
>>> |-- edac_mode
>>> |-- mem_type
>>> |-- size_mb
>>> `-- ue_count
>>>
>>> and since userspace uses them, they cannot be removed.
>>
>> Two reasons:
>>
>> 1) a per-csrow counter only works properly for amd64 and the Intel drivers for
>>    hardware shipped 5+ years ago;
>>
>> 2) per Greg's request, we should not use struct sysdev, using instead,
>>    struct device. That means that everything under
>> 	/sys/devices/system/edac/mc/
>>    will move to another place, likely:
>> 	/sys/devices/ras/
>>
>> and everything at /sys/devices/system/edac/mc/ will need to be provided by a
>> legacy compat API code, to be dropped on some future.
>>
>>>>> It depends - if the 128 bit word comes from a single DIMM (unganged
>>>>> mode) then you have a per-rank UE.
>>>>
>>>> True, and there are other types of ECC logic that would allow to identify
>>>> what DIMM/rank produced the error.
>>>>
>>>> Yet, the typical case is to use two DIMMs for a 128-bits cacheline
>>>> on separate channels, due to performance improvements, and ECC chipkill
>>>> using the 128+16 bits, as it improves the probability of error correction.
>>>
>>> ... and in this typical case, on smart hardware you can get the rank
>>> too. 
>>
>> I've analyzed all edac drivers. Almost all points to a single
>> DIMM or rank on UE errors. They can only blame two dimms/ranks. The
>> very few ones that will point to a single DIMM seem to be due to a
>> the way they cheat with the EDAC API in order to implement support
>> for FB-DIMM (or RAMBUS), telling the EDAC core that there's just one
>> channel.
>>
>>> If one cannot discern between the two DIMMs, then there should be
>>> one counter and the other one should be a symlink to that counter, or
>>> something to that effect.
>>
>> Doesn't sound easy to implement it, nor for userspace to convert back
>> from a series of symlinks into a set of locations.
>>
>>>>>> Of course, the EDAC logic could increment multiple UE error counters
>>>>>> in such case, (meaning that an error happened on either one of the
>>>>>> affected DIMMs/Ranks) but this is a different behavior than the
>>>>>> current API.
>>>>>
>>>>> Well, the API should be changed to accomodate such configurations.
>>>>
>>>> True, but changing the propagation logic to propagate the error down
>>>> to the several DIMMs from where the error might have occurred is:
>>>>
>>>> 	- the opposite of the current propagation logic;
>>>>
>>>> 	- the opposite on how ITU-T TMN architecture and all EMS/NMS
>>>> 	  implementations I'm aware with work.
>>>>
>>>> So, using such propagation logic doesn't sound right to me. What I'm
>>>> saying is that, if all the driver can be sure is that the error happened
>>>> at the csrow level, it should not propagate the errors to the channel
>>>> level. 
>>>>
>>>> So, I think that csrow-level counter is needed (and the equivalent
>>>> "group" counters for non-rank-based memory controllers).
>>>
>>> See above, we already have 'ce_count' and 'ue_count' and those are
>>> csrow-level counters.
>>
>> They're not branch-level counters (a requirement for FB-DIMM drivers).
>>
>> The role target of this patchset is to fix the core of the reporting system.
>> The old sysfs nodes don't work properly with Intel's systems
>> manufactured after 2005 (and with a few others manufactured before).
>>
>> All Xeon 3xxx/5xxx/7xxx/E3 chipsets[1] use advanced memory controllers that 
>> abstract csrows, either because they're for FB-DIMMs (where the memory ranks and 
>> chip select rows aren't visible by the memory controller), or, more recently,
>> because their registers are per DIMM slots, instead of per ranks, and users
>> can't replace a damaged rank on a dual-ranked DIMM in field.
>>
>> [1] The Intel's CPU/chipset series of Servers since 2005. Desktop processors
>> aren't relevant for EDAC, as they're either not supported, or they're 
>> supported by the same driver that provides support for servers.
>>
>> Regards,
>> Mauro
>>
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-16 12:07                     ` Mauro Carvalho Chehab
@ 2012-03-16 14:07                       ` Mauro Carvalho Chehab
  2012-03-16 15:31                         ` Greg KH
  2012-03-16 16:54                         ` Borislav Petkov
  0 siblings, 2 replies; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-16 14:07 UTC (permalink / raw)
  To: Borislav Petkov, Greg KH
  Cc: Linux Edac Mailing List, Linux Kernel Mailing List

Em 16-03-2012 09:07, Mauro Carvalho Chehab escreveu:
> Em 16-03-2012 08:15, Borislav Petkov escreveu:
>> Lemme save you all the trouble:
...
> Anyway, I wrote the patches for it already after Greg's email.
> I'm just about to test them.

And they're already working. 

The patches are (will be, after mirror sync) at:
	git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac.git hw_events_v8

There are just two new patches on it (and some bug fix patches got fold
to simplify their review):

c316427 edac: preserve the current EDAC sysfs API on a separate file

	- Just copies the edac_mc_sysfs.c as edac_mc_sysfs_legacy.c,
	  in order to preserve the ABI before the changes;

22790f1 edac: Convert edac mc to use struct device

	- Fixes edac_mc_sysfs_legacy.c with the changes at the struct,
	  and converts edac_mc_sysfs to use struct device.

There are still a few adjustments to do, so this is not a final version,
but the patch is already doing what's expected.

I decided to code the ras functions it as a new class, under:
	/sys/class/ras

I didn't convert yet the code that creates the CE/UE error counters
per rank/dimm/branch/csrow, as it will need some changes. The code is
also creating a "power" subdir, of course, this is uneeded/unwanted,
but the basic stuff is already there:

$ tree /sys/class/ras
/sys/class/ras
├── mc0 -> ../../devices/pci0000:3f/0000:3f:03.0/ras/mc0
├── rank0 -> ../../devices/pci0000:3f/0000:3f:03.0/ras/mc0/rank0
├── rank1 -> ../../devices/pci0000:3f/0000:3f:03.0/ras/mc0/rank1
├── rank3 -> ../../devices/pci0000:3f/0000:3f:03.0/ras/mc0/rank3
└── rank6 -> ../../devices/pci0000:3f/0000:3f:03.0/ras/mc0/rank6

$ tree /sys/class/ras/mc0
/sys/class/ras/mc0
├── ce_count
├── ce_noinfo_count
├── device -> ../../../0000:3f:03.0
├── fake_inject
├── max_location
├── mc_name
├── power
│   ├── async
│   ├── autosuspend_delay_ms
│   ├── control
│   ├── runtime_active_kids
│   ├── runtime_active_time
│   ├── runtime_enabled
│   ├── runtime_status
│   ├── runtime_suspended_time
│   └── runtime_usage
├── rank0
│   ├── device -> ../../mc0
│   ├── dimm_dev_type
│   ├── dimm_edac_mode
│   ├── dimm_label
│   ├── dimm_location
│   ├── dimm_mem_type
│   ├── power
│   │   ├── async
│   │   ├── autosuspend_delay_ms
│   │   ├── control
│   │   ├── runtime_active_kids
│   │   ├── runtime_active_time
│   │   ├── runtime_enabled
│   │   ├── runtime_status
│   │   ├── runtime_suspended_time
│   │   └── runtime_usage
│   ├── size
│   ├── subsystem -> ../../../../../../class/ras
│   └── uevent
├── rank1
│   ├── device -> ../../mc0
│   ├── dimm_dev_type
│   ├── dimm_edac_mode
│   ├── dimm_label
│   ├── dimm_location
│   ├── dimm_mem_type
│   ├── power
│   │   ├── async
│   │   ├── autosuspend_delay_ms
│   │   ├── control
│   │   ├── runtime_active_kids
│   │   ├── runtime_active_time
│   │   ├── runtime_enabled
│   │   ├── runtime_status
│   │   ├── runtime_suspended_time
│   │   └── runtime_usage
│   ├── size
│   ├── subsystem -> ../../../../../../class/ras
│   └── uevent
├── rank3
│   ├── device -> ../../mc0
│   ├── dimm_dev_type
│   ├── dimm_edac_mode
│   ├── dimm_label
│   ├── dimm_location
│   ├── dimm_mem_type
│   ├── power
│   │   ├── async
│   │   ├── autosuspend_delay_ms
│   │   ├── control
│   │   ├── runtime_active_kids
│   │   ├── runtime_active_time
│   │   ├── runtime_enabled
│   │   ├── runtime_status
│   │   ├── runtime_suspended_time
│   │   └── runtime_usage
│   ├── size
│   ├── subsystem -> ../../../../../../class/ras
│   └── uevent
├── rank6
│   ├── device -> ../../mc0
│   ├── dimm_dev_type
│   ├── dimm_edac_mode
│   ├── dimm_label
│   ├── dimm_location
│   ├── dimm_mem_type
│   ├── power
│   │   ├── async
│   │   ├── autosuspend_delay_ms
│   │   ├── control
│   │   ├── runtime_active_kids
│   │   ├── runtime_active_time
│   │   ├── runtime_enabled
│   │   ├── runtime_status
│   │   ├── runtime_suspended_time
│   │   └── runtime_usage
│   ├── size
│   ├── subsystem -> ../../../../../../class/ras
│   └── uevent
├── reset_counters
├── sdram_scrub_rate
├── seconds_since_reset
├── size_mb
├── subsystem -> ../../../../../class/ras
├── ue_count
├── ue_noinfo_count
└── uevent

19 directories, 85 files

The legacy API is also there, just like it was
before the entire patchset:

$ tree /sys/devices/system/edac/
/sys/devices/system/edac/
├── mc
│   └── mc0
│       ├── all_channel_counts
│       │   ├── udimm0
│       │   ├── udimm1
│       │   └── udimm2
│       ├── ce_count
│       ├── ce_noinfo_count
│       ├── csrow0
│       │   ├── ce_count
│       │   ├── ch0_ce_count
│       │   ├── ch0_dimm_label
│       │   ├── ch1_ce_count
│       │   ├── ch1_dimm_label
│       │   ├── ch2_ce_count
│       │   ├── ch2_dimm_label
│       │   ├── dev_type
│       │   ├── edac_mode
│       │   ├── mem_type
│       │   ├── size_mb
│       │   └── ue_count
│       ├── csrow1
│       │   ├── ce_count
│       │   ├── ch0_ce_count
│       │   ├── ch0_dimm_label
│       │   ├── ch1_ce_count
│       │   ├── ch1_dimm_label
│       │   ├── ch2_ce_count
│       │   ├── ch2_dimm_label
│       │   ├── dev_type
│       │   ├── edac_mode
│       │   ├── mem_type
│       │   ├── size_mb
│       │   └── ue_count
│       ├── csrow2
│       │   ├── ce_count
│       │   ├── ch0_ce_count
│       │   ├── ch0_dimm_label
│       │   ├── ch1_ce_count
│       │   ├── ch1_dimm_label
│       │   ├── ch2_ce_count
│       │   ├── ch2_dimm_label
│       │   ├── dev_type
│       │   ├── edac_mode
│       │   ├── mem_type
│       │   ├── size_mb
│       │   └── ue_count
│       ├── device -> ../../../../pci0000:3f/0000:3f:03.0
│       ├── inject_addrmatch
│       │   ├── bank
│       │   ├── channel
│       │   ├── col
│       │   ├── dimm
│       │   ├── page
│       │   └── rank
│       ├── inject_eccmask
│       ├── inject_enable
│       ├── inject_section
│       ├── inject_type
│       ├── mc_name
│       ├── reset_counters
│       ├── sdram_scrub_rate
│       ├── seconds_since_reset
│       ├── size_mb
│       ├── ue_count
│       └── ue_noinfo_count
└── pci
    ├── check_pci_errors
    ├── edac_pci_log_npe
    ├── edac_pci_log_pe
    ├── edac_pci_panic_on_pe
    ├── pci0
    │   ├── device -> ../../../../pci0000:3f/0000:3f:03.0
    │   ├── npe_count
    │   └── pe_count
    ├── pci_nonparity_count
    └── pci_parity_count

11 directories, 66 files

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-16  8:47                 ` Mauro Carvalho Chehab
  2012-03-16 11:15                   ` Borislav Petkov
@ 2012-03-16 15:30                   ` Greg KH
  2012-03-16 15:44                     ` Mauro Carvalho Chehab
  1 sibling, 1 reply; 41+ messages in thread
From: Greg KH @ 2012-03-16 15:30 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Borislav Petkov, Linux Edac Mailing List,
	Linux Kernel Mailing List

On Fri, Mar 16, 2012 at 05:47:52AM -0300, Mauro Carvalho Chehab wrote:
> 2) per Greg's request, we should not use struct sysdev, using instead,
>    struct device. That means that everything under
> 	/sys/devices/system/edac/mc/
>    will move to another place, likely:
> 	/sys/devices/ras/

No, you can keep things under /sys/devices/system/edac/ if you use a
struct device, no need to move things and break userspace tools if you
don't have to.

And I don't think you have to.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-16 14:07                       ` Mauro Carvalho Chehab
@ 2012-03-16 15:31                         ` Greg KH
  2012-03-16 16:54                         ` Borislav Petkov
  1 sibling, 0 replies; 41+ messages in thread
From: Greg KH @ 2012-03-16 15:31 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Borislav Petkov, Linux Edac Mailing List,
	Linux Kernel Mailing List

On Fri, Mar 16, 2012 at 11:07:10AM -0300, Mauro Carvalho Chehab wrote:
> Em 16-03-2012 09:07, Mauro Carvalho Chehab escreveu:
> > Em 16-03-2012 08:15, Borislav Petkov escreveu:
> >> Lemme save you all the trouble:
> ...
> > Anyway, I wrote the patches for it already after Greg's email.
> > I'm just about to test them.
> 
> And they're already working. 
> 
> The patches are (will be, after mirror sync) at:
> 	git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac.git hw_events_v8
> 
> There are just two new patches on it (and some bug fix patches got fold
> to simplify their review):
> 
> c316427 edac: preserve the current EDAC sysfs API on a separate file
> 
> 	- Just copies the edac_mc_sysfs.c as edac_mc_sysfs_legacy.c,
> 	  in order to preserve the ABI before the changes;
> 
> 22790f1 edac: Convert edac mc to use struct device
> 
> 	- Fixes edac_mc_sysfs_legacy.c with the changes at the struct,
> 	  and converts edac_mc_sysfs to use struct device.
> 
> There are still a few adjustments to do, so this is not a final version,
> but the patch is already doing what's expected.
> 
> I decided to code the ras functions it as a new class, under:
> 	/sys/class/ras

Ah, you really shouldn't be creating new classes in the kernel either,
sorry.  Why is this needed, and you can't just use a bus and struct
device in the place where the kobjects were in the tree?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-16 15:30                   ` Greg KH
@ 2012-03-16 15:44                     ` Mauro Carvalho Chehab
  2012-03-16 16:01                       ` Greg KH
  0 siblings, 1 reply; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2012-03-16 15:44 UTC (permalink / raw)
  To: Greg KH; +Cc: Borislav Petkov, Linux Edac Mailing List,
	Linux Kernel Mailing List

Em 16-03-2012 12:30, Greg KH escreveu:
> On Fri, Mar 16, 2012 at 05:47:52AM -0300, Mauro Carvalho Chehab wrote:
>> 2) per Greg's request, we should not use struct sysdev, using instead,
>>    struct device. That means that everything under
>> 	/sys/devices/system/edac/mc/
>>    will move to another place, likely:
>> 	/sys/devices/ras/
> 
> No, you can keep things under /sys/devices/system/edac/ if you use a
> struct device, no need to move things and break userspace tools if you
> don't have to.

Hmm... How?

> 
> And I don't think you have to.
> 
> thanks,
> 
> greg k-h


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-16 15:44                     ` Mauro Carvalho Chehab
@ 2012-03-16 16:01                       ` Greg KH
  0 siblings, 0 replies; 41+ messages in thread
From: Greg KH @ 2012-03-16 16:01 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Borislav Petkov, Linux Edac Mailing List,
	Linux Kernel Mailing List

On Fri, Mar 16, 2012 at 12:44:01PM -0300, Mauro Carvalho Chehab wrote:
> Em 16-03-2012 12:30, Greg KH escreveu:
> > On Fri, Mar 16, 2012 at 05:47:52AM -0300, Mauro Carvalho Chehab wrote:
> >> 2) per Greg's request, we should not use struct sysdev, using instead,
> >>    struct device. That means that everything under
> >> 	/sys/devices/system/edac/mc/
> >>    will move to another place, likely:
> >> 	/sys/devices/ras/
> > 
> > No, you can keep things under /sys/devices/system/edac/ if you use a
> > struct device, no need to move things and break userspace tools if you
> > don't have to.
> 
> Hmm... How?

Just point your parent of your device to the same place.  Converting
from using 'struct sysdev' is easy, as the conversion is already done
in-tree to real 'struct device' so there shouldn't be any problems.

Or am I missing something here?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 0/6] Add a per-dimm structure
  2012-03-16 14:07                       ` Mauro Carvalho Chehab
  2012-03-16 15:31                         ` Greg KH
@ 2012-03-16 16:54                         ` Borislav Petkov
  1 sibling, 0 replies; 41+ messages in thread
From: Borislav Petkov @ 2012-03-16 16:54 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Borislav Petkov, Greg KH, Linux Edac Mailing List,
	Linux Kernel Mailing List

On Fri, Mar 16, 2012 at 11:07:10AM -0300, Mauro Carvalho Chehab wrote:
> The patches are (will be, after mirror sync) at:
> 	git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac.git hw_events_v8

Please send them for a normal review after you've changed them as per
Greg's request.

..

> I decided to code the ras functions it as a new class, under:
> 	/sys/class/ras
> 
> I didn't convert yet the code that creates the CE/UE error counters
> per rank/dimm/branch/csrow, as it will need some changes. The code is
> also creating a "power" subdir, of course, this is uneeded/unwanted,
> but the basic stuff is already there:
> 
> $ tree /sys/class/ras
> /sys/class/ras
> ├── mc0 -> ../../devices/pci0000:3f/0000:3f:03.0/ras/mc0
> ├── rank0 -> ../../devices/pci0000:3f/0000:3f:03.0/ras/mc0/rank0
> ├── rank1 -> ../../devices/pci0000:3f/0000:3f:03.0/ras/mc0/rank1
> ├── rank3 -> ../../devices/pci0000:3f/0000:3f:03.0/ras/mc0/rank3
> └── rank6 -> ../../devices/pci0000:3f/0000:3f:03.0/ras/mc0/rank6
> 
> $ tree /sys/class/ras/mc0
> /sys/class/ras/mc0
> ├── ce_count
> ├── ce_noinfo_count
> ├── device -> ../../../0000:3f:03.0
> ├── fake_inject

This node needs to go to debugfs.

Thanks.

-- 
Regards/Gruss,
Boris.

Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
GM: Alberto Bozzo
Reg: Dornach, Landkreis Muenchen
HRB Nr. 43632 WEEE Registernr: 129 19551

^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2012-03-16 16:55 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-03-07 11:40 [PATCH 0/6] Add a per-dimm structure Mauro Carvalho Chehab
2012-03-07 11:40 ` [PATCH 1/6] edac: Create a dimm struct and move the labels into it Mauro Carvalho Chehab
2012-03-07 11:40 ` [PATCH 2/6] edac: Add per dimm's sysfs nodes Mauro Carvalho Chehab
2012-03-07 11:40 ` [PATCH 3/6] edac: move dimm properties to struct memset_info Mauro Carvalho Chehab
2012-03-07 11:40 ` [PATCH 4/6] edac: Don't initialize csrow's first_page & friends when not needed Mauro Carvalho Chehab
2012-03-07 11:40 ` [PATCH 5/6] edac: move nr_pages to dimm struct Mauro Carvalho Chehab
2012-03-07 11:40 ` [PATCH 6/6] edac: Add per-dimm sysfs show nodes Mauro Carvalho Chehab
2012-03-08 21:57 ` [PATCH 0/6] Add a per-dimm structure Borislav Petkov
2012-03-09 10:32   ` Mauro Carvalho Chehab
2012-03-09 14:38     ` Borislav Petkov
2012-03-09 16:40       ` Mauro Carvalho Chehab
2012-03-09 18:47         ` Borislav Petkov
2012-03-09 19:46           ` Mauro Carvalho Chehab
2012-03-11 11:34             ` Borislav Petkov
2012-03-11 12:32               ` Mauro Carvalho Chehab
2012-03-12 16:39                 ` Borislav Petkov
2012-03-12 17:03                   ` Luck, Tony
2012-03-12 18:10                     ` Borislav Petkov
2012-03-13 23:32 ` Greg KH
2012-03-14 19:35   ` Mauro Carvalho Chehab
2012-03-14 20:43     ` Greg KH
2012-03-14 22:20       ` Mauro Carvalho Chehab
2012-03-14 23:32         ` Greg KH
2012-03-15  2:22           ` Mauro Carvalho Chehab
2012-03-15 15:00             ` Greg KH
2012-03-14 22:31       ` Borislav Petkov
2012-03-14 22:40         ` Greg KH
2012-03-15  1:37           ` Mauro Carvalho Chehab
2012-03-15  1:44         ` Mauro Carvalho Chehab
2012-03-15 11:31           ` Borislav Petkov
2012-03-15 12:40             ` Mauro Carvalho Chehab
2012-03-15 21:38               ` Borislav Petkov
2012-03-16  8:47                 ` Mauro Carvalho Chehab
2012-03-16 11:15                   ` Borislav Petkov
2012-03-16 12:07                     ` Mauro Carvalho Chehab
2012-03-16 14:07                       ` Mauro Carvalho Chehab
2012-03-16 15:31                         ` Greg KH
2012-03-16 16:54                         ` Borislav Petkov
2012-03-16 15:30                   ` Greg KH
2012-03-16 15:44                     ` Mauro Carvalho Chehab
2012-03-16 16:01                       ` Greg KH

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox