Merge git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac

Pull EDAC patches from Mauro Carvalho Chehab:

 - the second part of the EDAC rework:
    - Add the sysfs nodes that exports the real memory layout, instead
      of the fake one (needed to properly represent Intel memory
      controllers since 2002)
    - convert EDAC MC to use "struct device" instead of creating the
      sysfs nodes via the kobj API
    - adds a tracepoint to represent memory errors

 - some cleanup patches

 - some fixes at i5000, i5400 and EDAC core

 - a new EDAC driver for Caldera.

* git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac: (33 commits)
  edac i5000, i5400: fix pointer math in i5000_get_mc_regs()
  edac: allow specifying the error count with fake_inject
  edac: add support for Calxeda highbank L2 cache ecc
  edac: add support for Calxeda highbank memory controller
  edac: create top-level debugfs directory
  sb_edac: properly handle error count
  i7core_edac: properly handle error count
  edac: edac_mc_handle_error(): add an error_count parameter
  edac: remove arch-specific parameter for the error handler
  amd64_edac: Don't pass driver name as an error parameter
  edac_mc: check for allocation failure in edac_mc_alloc()
  edac: Increase version to 3.0.0
  edac_mc: Cleanup per-dimm_info debug messages
  edac: Convert debugfX to edac_dbg(X,
  edac: Use more normal debugging macro style
  edac: Don't add __func__ or __FILE__ for debugf[0-9] msgs
  Edac: Add ABI Documentation for the new device nodes
  edac: move documentation ABI to ABI/testing/sysfs-devices-edac
  i7core_edac: change the mem allocation scheme to make Documentation/kobject.txt happy
  edac: change the mem allocation scheme to make Documentation/kobject.txt happy
  ...
This commit is contained in:
Linus Torvalds
2012-07-30 09:53:50 -07:00
48 changed files with 3506 additions and 2590 deletions
+137 -79
View File
@@ -13,9 +13,11 @@
#define _LINUX_EDAC_H_
#include <linux/atomic.h>
#include <linux/device.h>
#include <linux/kobject.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
#include <linux/debugfs.h>
struct device;
@@ -49,7 +51,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,
@@ -167,18 +181,30 @@ 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)
@@ -191,18 +217,30 @@ 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)
@@ -374,7 +412,44 @@ struct edac_mc_layer {
#define EDAC_MAX_LAYERS 3
/**
* EDAC_DIMM_PTR - Macro responsible to find a pointer inside a pointer array
* EDAC_DIMM_OFF - Macro responsible to get a pointer offset inside a pointer array
* for the element given by [layer0,layer1,layer2] position
*
* @layers: a struct edac_mc_layer array, describing how many elements
* were allocated for each layer
* @n_layers: Number of layers at the @layers array
* @layer0: layer0 position
* @layer1: layer1 position. Unused if n_layers < 2
* @layer2: layer2 position. Unused if n_layers < 3
*
* For 1 layer, this macro returns &var[layer0] - &var
* For 2 layers, this macro is similar to allocate a bi-dimensional array
* and to return "&var[layer0][layer1] - &var"
* For 3 layers, this macro is similar to allocate a tri-dimensional array
* and to return "&var[layer0][layer1][layer2] - &var"
*
* A loop could be used here to make it more generic, but, as we only have
* 3 layers, this is a little faster.
* By design, layers can never be 0 or more than 3. If that ever happens,
* a NULL is returned, causing an OOPS during the memory allocation routine,
* with would point to the developer that he's doing something wrong.
*/
#define EDAC_DIMM_OFF(layers, nlayers, layer0, layer1, layer2) ({ \
int __i; \
if ((nlayers) == 1) \
__i = layer0; \
else if ((nlayers) == 2) \
__i = (layer1) + ((layers[1]).size * (layer0)); \
else if ((nlayers) == 3) \
__i = (layer2) + ((layers[2]).size * ((layer1) + \
((layers[1]).size * (layer0)))); \
else \
__i = -EINVAL; \
__i; \
})
/**
* EDAC_DIMM_PTR - Macro responsible to get a pointer inside a pointer array
* for the element given by [layer0,layer1,layer2] position
*
* @layers: a struct edac_mc_layer array, describing how many elements
@@ -391,30 +466,20 @@ struct edac_mc_layer {
* and to return "&var[layer0][layer1]"
* For 3 layers, this macro is similar to allocate a tri-dimensional array
* and to return "&var[layer0][layer1][layer2]"
*
* A loop could be used here to make it more generic, but, as we only have
* 3 layers, this is a little faster.
* By design, layers can never be 0 or more than 3. If that ever happens,
* a NULL is returned, causing an OOPS during the memory allocation routine,
* with would point to the developer that he's doing something wrong.
*/
#define EDAC_DIMM_PTR(layers, var, nlayers, layer0, layer1, layer2) ({ \
typeof(var) __p; \
if ((nlayers) == 1) \
__p = &var[layer0]; \
else if ((nlayers) == 2) \
__p = &var[(layer1) + ((layers[1]).size * (layer0))]; \
else if ((nlayers) == 3) \
__p = &var[(layer2) + ((layers[2]).size * ((layer1) + \
((layers[1]).size * (layer0))))]; \
else \
typeof(*var) __p; \
int ___i = EDAC_DIMM_OFF(layers, nlayers, layer0, layer1, layer2); \
if (___i < 0) \
__p = NULL; \
else \
__p = (var)[___i]; \
__p; \
})
/* FIXME: add the proper per-location error counts */
struct dimm_info {
struct device dev;
char label[EDAC_MC_LABEL_LEN + 1]; /* DIMM label on motherboard */
/* Memory location data */
@@ -456,6 +521,8 @@ struct rank_info {
};
struct csrow_info {
struct device dev;
/* 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 */
@@ -469,44 +536,26 @@ struct csrow_info {
struct mem_ctl_info *mci; /* the parent */
struct kobject kobj; /* sysfs kobject for this csrow */
/* channel information for this csrow */
u32 nr_channels;
struct rank_info *channels;
struct rank_info **channels;
};
struct mcidev_sysfs_group {
const char *name; /* group name */
const struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */
};
struct mcidev_sysfs_group_kobj {
struct list_head list; /* list for all instances within a mc */
struct kobject kobj; /* kobj for the group */
const struct mcidev_sysfs_group *grp; /* group description table */
struct mem_ctl_info *mci; /* the parent */
};
/* mcidev_sysfs_attribute structure
* used for driver sysfs attributes and in mem_ctl_info
* sysfs top level entries
/*
* struct errcount_attribute - used to store the several error counts
*/
struct mcidev_sysfs_attribute {
/* It should use either attr or grp */
struct attribute attr;
const struct mcidev_sysfs_group *grp; /* Points to a group of attributes */
/* Ops for show/store values at the attribute - not used on group */
ssize_t (*show)(struct mem_ctl_info *,char *);
ssize_t (*store)(struct mem_ctl_info *, const char *,size_t);
struct errcount_attribute_data {
int n_layers;
int pos[EDAC_MAX_LAYERS];
int layer0, layer1, layer2;
};
/* MEMORY controller information structure
*/
struct mem_ctl_info {
struct device dev;
struct bus_type bus;
struct list_head link; /* for global list of mem_ctl_info structs */
struct module *owner; /* Module owner of this control struct */
@@ -548,10 +597,18 @@ struct mem_ctl_info {
unsigned long (*ctl_page_to_phys) (struct mem_ctl_info * mci,
unsigned long page);
int mc_idx;
struct csrow_info *csrows;
struct csrow_info **csrows;
unsigned nr_csrows, num_cschannel;
/* Memory Controller hierarchy */
/*
* Memory Controller hierarchy
*
* There are basically two types of memory controller: the ones that
* sees memory sticks ("dimms"), and the ones that sees memory ranks.
* All old memory controllers enumerate memories per rank, but most
* of the recent drivers enumerate memories per DIMM, instead.
* When the memory controller is per rank, mem_is_per_rank is true.
*/
unsigned n_layers;
struct edac_mc_layer *layers;
bool mem_is_per_rank;
@@ -560,14 +617,14 @@ struct mem_ctl_info {
* DIMM info. Will eventually remove the entire csrows_info some day
*/
unsigned tot_dimms;
struct dimm_info *dimms;
struct dimm_info **dimms;
/*
* FIXME - what about controllers on other busses? - IDs must be
* unique. dev pointer should be sufficiently unique, but
* BUS:SLOT.FUNC numbers may not be unique.
*/
struct device *dev;
struct device *pdev;
const char *mod_name;
const char *mod_ver;
const char *ctl_name;
@@ -586,12 +643,6 @@ struct mem_ctl_info {
struct completion complete;
/* edac sysfs device control */
struct kobject edac_mci_kobj;
/* list for all grp instances within a mc */
struct list_head grp_kobj_list;
/* Additional top controller level attributes, but specified
* by the low level driver.
*
@@ -609,6 +660,13 @@ struct mem_ctl_info {
/* the internal state of this controller instance */
int op_state;
#ifdef CONFIG_EDAC_DEBUG
struct dentry *debugfs;
u8 fake_inject_layer[EDAC_MAX_LAYERS];
u32 fake_inject_ue;
u16 fake_inject_count;
#endif
};
#endif
+102
View File
@@ -0,0 +1,102 @@
#undef TRACE_SYSTEM
#define TRACE_SYSTEM ras
#define TRACE_INCLUDE_FILE ras_event
#if !defined(_TRACE_HW_EVENT_MC_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_HW_EVENT_MC_H
#include <linux/tracepoint.h>
#include <linux/edac.h>
#include <linux/ktime.h>
/*
* Hardware Events Report
*
* Those events are generated when hardware detected a corrected or
* uncorrected event, and are meant to replace the current API to report
* errors defined on both EDAC and MCE subsystems.
*
* FIXME: Add events for handling memory errors originated from the
* MCE subsystem.
*/
/*
* Hardware-independent Memory Controller specific events
*/
/*
* Default error mechanisms for Memory Controller errors (CE and UE)
*/
TRACE_EVENT(mc_event,
TP_PROTO(const unsigned int err_type,
const char *error_msg,
const char *label,
const int error_count,
const u8 mc_index,
const s8 top_layer,
const s8 mid_layer,
const s8 low_layer,
unsigned long address,
const u8 grain_bits,
unsigned long syndrome,
const char *driver_detail),
TP_ARGS(err_type, error_msg, label, error_count, mc_index,
top_layer, mid_layer, low_layer, address, grain_bits,
syndrome, driver_detail),
TP_STRUCT__entry(
__field( unsigned int, error_type )
__string( msg, error_msg )
__string( label, label )
__field( u16, error_count )
__field( u8, mc_index )
__field( s8, top_layer )
__field( s8, middle_layer )
__field( s8, lower_layer )
__field( long, address )
__field( u8, grain_bits )
__field( long, syndrome )
__string( driver_detail, driver_detail )
),
TP_fast_assign(
__entry->error_type = err_type;
__assign_str(msg, error_msg);
__assign_str(label, label);
__entry->error_count = error_count;
__entry->mc_index = mc_index;
__entry->top_layer = top_layer;
__entry->middle_layer = mid_layer;
__entry->lower_layer = low_layer;
__entry->address = address;
__entry->grain_bits = grain_bits;
__entry->syndrome = syndrome;
__assign_str(driver_detail, driver_detail);
),
TP_printk("%d %s error%s:%s%s on %s (mc:%d location:%d:%d:%d address:0x%08lx grain:%d syndrome:0x%08lx%s%s)",
__entry->error_count,
(__entry->error_type == HW_EVENT_ERR_CORRECTED) ? "Corrected" :
((__entry->error_type == HW_EVENT_ERR_FATAL) ?
"Fatal" : "Uncorrected"),
__entry->error_count > 1 ? "s" : "",
((char *)__get_str(msg))[0] ? " " : "",
__get_str(msg),
__get_str(label),
__entry->mc_index,
__entry->top_layer,
__entry->middle_layer,
__entry->lower_layer,
__entry->address,
1 << __entry->grain_bits,
__entry->syndrome,
((char *)__get_str(driver_detail))[0] ? " " : "",
__get_str(driver_detail))
);
#endif /* _TRACE_HW_EVENT_MC_H */
/* This part must be outside protection */
#include <trace/define_trace.h>