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:
+137
-79
@@ -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
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user