i3200_edac: Fix the logic that detects filled memories
Mauro Carvalho Chehab [Thu, 10 Jan 2013 16:31:47 +0000 (13:31 -0300)]
After running a series of tests on an HP DL320, filled with different
memory sizes, it was noticed that, when filled with just one DIMM
on such hardware, the driver wrongly detects twice the memory, and
thinks that both channels 0 and 1 are filled.

It seems to be partially caused by the BIOS and partially by the driver.

The i3200_edac current logic would be working fine if the BIOS were
disabling the unused second channel when just one DIMM is connected,
in order to do power-saving, as recommended on this chipset's datasheet.

However, the BIOS on this particular machine doesn't do it:

[   16.741421] EDAC DEBUG: how_many_channels: In dual channel mode
[   16.741424] EDAC DEBUG: how_many_channels: 2 DIMMS per channel enabled

So, the driver were assuming that 2 channels are enabled (well, they are,
but the second is unused).

Combined with that, I found two issues at the logic that creates the
EDAC data, that were failing when the two channels are not equally
filled (AFAICT, that happens only when just 1 DIMM is plugged).

The first one is that a 0 at DRB means that nothing is filled. The
driver's logic, however, do some calculation with that.

The second one is that the logic that fills the DIMM data currently
assumes that both channels are equally filled.

I tested the system already with the current configuration and my
patch and it is now working fine. So, for a 2R single DIMM 2Gb memory
at dimm slot 01 (channel 0), it is now displaying:

[   16.741406] EDAC DEBUG: i3200_get_drbs: drb[0][0] = 16, drb[1][0] = 0
[   16.741410] EDAC DEBUG: i3200_get_drbs: drb[0][1] = 32, drb[1][1] = 0
[   16.741413] EDAC DEBUG: i3200_get_drbs: drb[0][2] = 32, drb[1][2] = 0
[   16.741416] EDAC DEBUG: i3200_get_drbs: drb[0][3] = 32, drb[1][3] = 0
...
[   16.741896] EDAC DEBUG: i3200_probe1: csrow 0, channel 0, size = 1024 Mb
[   16.741899] EDAC DEBUG: i3200_probe1: csrow 1, channel 0, size = 1024 Mb

and the corresponding sysfs nodes are now properly filled.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>

drivers/edac/i3200_edac.c

index 2064192..aa44c17 100644 (file)
@@ -323,6 +323,9 @@ static unsigned long drb_to_nr_pages(
        int n;
 
        n = drbs[channel][rank];
+       if (!n)
+               return 0;
+
        if (rank > 0)
                n -= drbs[channel][rank - 1];
        if (stacked && (channel == 1) &&
@@ -389,19 +392,19 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
         * cumulative; the last one will contain the total memory
         * contained in all ranks.
         */
-       for (i = 0; i < mci->nr_csrows; i++) {
+       for (i = 0; i < I3200_DIMMS; i++) {
                unsigned long nr_pages;
-               struct csrow_info *csrow = mci->csrows[i];
 
-               nr_pages = drb_to_nr_pages(drbs, stacked,
-                       i / I3200_RANKS_PER_CHANNEL,
-                       i % I3200_RANKS_PER_CHANNEL);
+               for (j = 0; j < nr_channels; j++) {
+                       struct dimm_info *dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
+                                                              mci->n_layers, i, j, 0);
 
-               if (nr_pages == 0)
-                       continue;
+                       nr_pages = drb_to_nr_pages(drbs, stacked, j, i);
+                       if (nr_pages == 0)
+                               continue;
 
-               for (j = 0; j < nr_channels; j++) {
-                       struct dimm_info *dimm = csrow->channels[j]->dimm;
+                       edac_dbg(0, "csrow %d, channel %d%s, size = %ld Mb\n", i, j,
+                                stacked ? " (stacked)" : "", PAGES_TO_MiB(nr_pages));
 
                        dimm->nr_pages = nr_pages;
                        dimm->grain = nr_pages << PAGE_SHIFT;