linux-stable/drivers/edac/ghes_edac.c

344 lines
8.2 KiB
C
Raw Normal View History

/*
* GHES/EDAC Linux driver
*
* This file may be distributed under the terms of the GNU General Public
* License version 2.
*
* Copyright (c) 2013 by Mauro Carvalho Chehab <mchehab@redhat.com>
*
* Red Hat Inc. http://www.redhat.com
*/
#include <acpi/ghes.h>
#include <linux/edac.h>
#include <linux/dmi.h>
#include "edac_core.h"
#define GHES_PFX "ghes_edac: "
#define GHES_EDAC_REVISION " Ver: 1.0.0"
struct ghes_edac_pvt {
struct list_head list;
struct ghes *ghes;
struct mem_ctl_info *mci;
};
static LIST_HEAD(ghes_reglist);
static DEFINE_MUTEX(ghes_edac_lock);
static int ghes_edac_mc_num;
/* Memory Device - Type 17 of SMBIOS spec */
struct memdev_dmi_entry {
u8 type;
u8 length;
u16 handle;
u16 phys_mem_array_handle;
u16 mem_err_info_handle;
u16 total_width;
u16 data_width;
u16 size;
u8 form_factor;
u8 device_set;
u8 device_locator;
u8 bank_locator;
u8 memory_type;
u16 type_detail;
u16 speed;
u8 manufacturer;
u8 serial_number;
u8 asset_tag;
u8 part_number;
u8 attributes;
u32 extended_size;
u16 conf_mem_clk_speed;
} __attribute__((__packed__));
struct ghes_edac_dimm_fill {
struct mem_ctl_info *mci;
unsigned count;
};
char *memory_type[] = {
[MEM_EMPTY] = "EMPTY",
[MEM_RESERVED] = "RESERVED",
[MEM_UNKNOWN] = "UNKNOWN",
[MEM_FPM] = "FPM",
[MEM_EDO] = "EDO",
[MEM_BEDO] = "BEDO",
[MEM_SDR] = "SDR",
[MEM_RDR] = "RDR",
[MEM_DDR] = "DDR",
[MEM_RDDR] = "RDDR",
[MEM_RMBS] = "RMBS",
[MEM_DDR2] = "DDR2",
[MEM_FB_DDR2] = "FB_DDR2",
[MEM_RDDR2] = "RDDR2",
[MEM_XDR] = "XDR",
[MEM_DDR3] = "DDR3",
[MEM_RDDR3] = "RDDR3",
};
static void ghes_edac_count_dimms(const struct dmi_header *dh, void *arg)
{
int *num_dimm = arg;
if (dh->type == DMI_ENTRY_MEM_DEVICE)
(*num_dimm)++;
}
static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg)
{
struct ghes_edac_dimm_fill *dimm_fill = arg;
struct mem_ctl_info *mci = dimm_fill->mci;
if (dh->type == DMI_ENTRY_MEM_DEVICE) {
struct memdev_dmi_entry *entry = (struct memdev_dmi_entry *)dh;
struct dimm_info *dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
mci->n_layers,
dimm_fill->count, 0, 0);
if (entry->size == 0xffff) {
pr_info(GHES_PFX "Can't get dimm size\n");
dimm->nr_pages = MiB_TO_PAGES(32);/* Unknown */
} else if (entry->size == 0x7fff) {
dimm->nr_pages = MiB_TO_PAGES(entry->extended_size);
} else {
if (entry->size & 1 << 15)
dimm->nr_pages = MiB_TO_PAGES((entry->size &
0x7fff) << 10);
else
dimm->nr_pages = MiB_TO_PAGES(entry->size);
}
switch (entry->memory_type) {
case 0x12:
if (entry->type_detail & 1 << 13)
dimm->mtype = MEM_RDDR;
else
dimm->mtype = MEM_DDR;
break;
case 0x13:
if (entry->type_detail & 1 << 13)
dimm->mtype = MEM_RDDR2;
else
dimm->mtype = MEM_DDR2;
break;
case 0x14:
dimm->mtype = MEM_FB_DDR2;
break;
case 0x18:
if (entry->type_detail & 1 << 13)
dimm->mtype = MEM_RDDR3;
else
dimm->mtype = MEM_DDR3;
break;
default:
if (entry->type_detail & 1 << 6)
dimm->mtype = MEM_RMBS;
else if ((entry->type_detail & ((1 << 7) | (1 << 13)))
== ((1 << 7) | (1 << 13)))
dimm->mtype = MEM_RDR;
else if (entry->type_detail & 1 << 7)
dimm->mtype = MEM_SDR;
else if (entry->type_detail & 1 << 9)
dimm->mtype = MEM_EDO;
else
dimm->mtype = MEM_UNKNOWN;
}
/*
* Actually, we can only detect if the memory has bits for
* checksum or not
*/
if (entry->total_width == entry->data_width)
dimm->edac_mode = EDAC_NONE;
else
dimm->edac_mode = EDAC_SECDED;
dimm->dtype = DEV_UNKNOWN;
dimm->grain = 128; /* Likely, worse case */
/*
* FIXME: It shouldn't be hard to also fill the DIMM labels
*/
if (dimm->nr_pages) {
pr_info(GHES_PFX "DIMM%i: %s size = %d MB%s\n",
dimm_fill->count, memory_type[dimm->mtype],
PAGES_TO_MiB(dimm->nr_pages),
(dimm->edac_mode != EDAC_NONE) ? "(ECC)" : "");
pr_info(GHES_PFX "\ttype %d, detail 0x%02x, width %d(total %d)\n",
entry->memory_type, entry->type_detail,
entry->total_width, entry->data_width);
}
dimm_fill->count++;
}
}
void ghes_edac_report_mem_error(struct ghes *ghes, int sev,
struct cper_sec_mem_err *mem_err)
{
enum hw_event_mc_err_type type;
struct edac_raw_error_desc *e;
struct mem_ctl_info *mci;
struct ghes_edac_pvt *pvt = NULL;
list_for_each_entry(pvt, &ghes_reglist, list) {
if (ghes == pvt->ghes)
break;
}
if (!pvt) {
pr_err("Internal error: Can't find EDAC structure\n");
return;
}
mci = pvt->mci;
e = &mci->error_desc;
/* Cleans the error report buffer */
memset(e, 0, sizeof (*e));
e->error_count = 1;
e->msg = "APEI";
strcpy(e->label, "unknown");
e->other_detail = "";
if (mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) {
e->page_frame_number = mem_err->physical_addr >> PAGE_SHIFT;
e->offset_in_page = mem_err->physical_addr & ~PAGE_MASK;
e->grain = ~(mem_err->physical_addr_mask & ~PAGE_MASK);
}
switch (sev) {
case GHES_SEV_CORRECTED:
type = HW_EVENT_ERR_CORRECTED;
break;
case GHES_SEV_RECOVERABLE:
type = HW_EVENT_ERR_UNCORRECTED;
break;
case GHES_SEV_PANIC:
type = HW_EVENT_ERR_FATAL;
break;
default:
case GHES_SEV_NO:
type = HW_EVENT_ERR_INFO;
}
sprintf(e->location,
"node:%d card:%d module:%d bank:%d device:%d row: %d column:%d bit_pos:%d",
mem_err->node, mem_err->card, mem_err->module,
mem_err->bank, mem_err->device, mem_err->row, mem_err->column,
mem_err->bit_pos);
edac_dbg(3, "error at location %s\n", e->location);
edac_raw_mc_handle_error(type, mci, e);
}
EXPORT_SYMBOL_GPL(ghes_edac_report_mem_error);
int ghes_edac_register(struct ghes *ghes, struct device *dev)
{
bool fake = false;
int rc, num_dimm = 0;
struct mem_ctl_info *mci;
struct edac_mc_layer layers[1];
struct ghes_edac_pvt *pvt;
struct ghes_edac_dimm_fill dimm_fill;
/* Get the number of DIMMs */
dmi_walk(ghes_edac_count_dimms, &num_dimm);
/* Check if we've got a bogus BIOS */
if (num_dimm == 0) {
fake = true;
num_dimm = 1;
}
layers[0].type = EDAC_MC_LAYER_ALL_MEM;
layers[0].size = num_dimm;
layers[0].is_virt_csrow = true;
/*
* We need to serialize edac_mc_alloc() and edac_mc_add_mc(),
* to avoid duplicated memory controller numbers
*/
mutex_lock(&ghes_edac_lock);
pr_info("ghes_edac#%d: allocating space for %d dimms\n",
ghes_edac_mc_num, num_dimm);
mci = edac_mc_alloc(ghes_edac_mc_num, ARRAY_SIZE(layers), layers,
sizeof(*pvt));
if (!mci) {
pr_info(GHES_PFX "Can't allocate memory for EDAC data\n");
mutex_unlock(&ghes_edac_lock);
return -ENOMEM;
}
pvt = mci->pvt_info;
memset(pvt, 0, sizeof(*pvt));
list_add_tail(&pvt->list, &ghes_reglist);
pvt->ghes = ghes;
pvt->mci = mci;
mci->pdev = dev;
mci->mtype_cap = MEM_FLAG_EMPTY;
mci->edac_ctl_cap = EDAC_FLAG_NONE;
mci->edac_cap = EDAC_FLAG_NONE;
mci->mod_name = "ghes_edac.c";
mci->mod_ver = GHES_EDAC_REVISION;
mci->ctl_name = "ghes_edac";
mci->dev_name = "ghes";
if (!fake) {
ghes_edac: Don't credit the same memory dimm twice On my tests on a 4xE5-4650 CPU's system, the GHES EDAC driver is called twice. As the SMBIOS DMI enumeration call will seek for the entire DIMM sockets in the system, on this machine, equipped with 128 GB of RAM, the memory is displayed twice: +-----------------------+ | mc0 | mc1 | ----------+-----------------------+ memory45: | 8192 MB | 8192 MB | memory44: | 0 MB | 0 MB | ----------+-----------------------+ memory43: | 0 MB | 0 MB | memory42: | 8192 MB | 8192 MB | ----------+-----------------------+ memory41: | 0 MB | 0 MB | memory40: | 0 MB | 0 MB | ----------+-----------------------+ memory39: | 8192 MB | 8192 MB | memory38: | 0 MB | 0 MB | ----------+-----------------------+ memory37: | 0 MB | 0 MB | memory36: | 8192 MB | 8192 MB | ----------+-----------------------+ memory35: | 0 MB | 0 MB | memory34: | 0 MB | 0 MB | ----------+-----------------------+ memory33: | 8192 MB | 8192 MB | memory32: | 0 MB | 0 MB | ----------+-----------------------+ memory31: | 0 MB | 0 MB | memory30: | 8192 MB | 8192 MB | ----------+-----------------------+ memory29: | 0 MB | 0 MB | memory28: | 0 MB | 0 MB | ----------+-----------------------+ memory27: | 8192 MB | 8192 MB | memory26: | 0 MB | 0 MB | ----------+-----------------------+ memory25: | 0 MB | 0 MB | memory24: | 8192 MB | 8192 MB | ----------+-----------------------+ memory23: | 0 MB | 0 MB | memory22: | 0 MB | 0 MB | ----------+-----------------------+ memory21: | 8192 MB | 8192 MB | memory20: | 0 MB | 0 MB | ----------+-----------------------+ memory19: | 0 MB | 0 MB | memory18: | 8192 MB | 8192 MB | ----------+-----------------------+ memory17: | 0 MB | 0 MB | memory16: | 0 MB | 0 MB | ----------+-----------------------+ memory15: | 8192 MB | 8192 MB | memory14: | 0 MB | 0 MB | ----------+-----------------------+ memory13: | 0 MB | 0 MB | memory12: | 8192 MB | 8192 MB | ----------+-----------------------+ memory11: | 0 MB | 0 MB | memory10: | 0 MB | 0 MB | ----------+-----------------------+ memory9: | 8192 MB | 8192 MB | memory8: | 0 MB | 0 MB | ----------+-----------------------+ memory7: | 0 MB | 0 MB | memory6: | 8192 MB | 8192 MB | ----------+-----------------------+ memory5: | 0 MB | 0 MB | memory4: | 0 MB | 0 MB | ----------+-----------------------+ memory3: | 8192 MB | 8192 MB | memory2: | 0 MB | 0 MB | ----------+-----------------------+ memory1: | 0 MB | 0 MB | memory0: | 8192 MB | 8192 MB | ----------+-----------------------+ Total sum of 256 GB. As there's no reliable way to credit DIMMS to the right memory controller, just put everything on memory controller 0 (with should always exist). Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2013-02-15 11:45:00 +00:00
/*
* Fill DIMM info from DMI for the memory controller #0
*
* Keep it in blank for the other memory controllers, as
* there's no reliable way to properly credit each DIMM to
* the memory controller, as different BIOSes fill the
* DMI bank location fields on different ways
*/
if (!ghes_edac_mc_num) {
dimm_fill.count = 0;
dimm_fill.mci = mci;
dmi_walk(ghes_edac_dmidecode, &dimm_fill);
}
} else {
struct dimm_info *dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
mci->n_layers, 0, 0, 0);
pr_info(GHES_PFX "Crappy BIOS detected. Faking DIMM EDAC data\n");
dimm->nr_pages = 1000;
dimm->grain = 128;
dimm->mtype = MEM_UNKNOWN;
dimm->dtype = DEV_UNKNOWN;
dimm->edac_mode = EDAC_SECDED;
}
rc = edac_mc_add_mc(mci);
if (rc < 0) {
pr_info(GHES_PFX "Can't register at EDAC core\n");
edac_mc_free(mci);
mutex_unlock(&ghes_edac_lock);
return -ENODEV;
}
ghes_edac_mc_num++;
mutex_unlock(&ghes_edac_lock);
return 0;
}
EXPORT_SYMBOL_GPL(ghes_edac_register);
void ghes_edac_unregister(struct ghes *ghes)
{
struct mem_ctl_info *mci;
struct ghes_edac_pvt *pvt;
list_for_each_entry(pvt, &ghes_reglist, list) {
if (ghes == pvt->ghes) {
mci = pvt->mci;
edac_mc_del_mc(mci->pdev);
edac_mc_free(mci);
list_del(&pvt->list);
}
}
}
EXPORT_SYMBOL_GPL(ghes_edac_unregister);