EDAC, skx_edac: Add address translation for non-volatile DIMMs.
Subsystems (2)
Luck, Tony
maintainerPATCH 5/5 EDAC, skx_edac: Add address translation for non-volatile DIMMs.
From: Qiuxu Zhuo <[email protected]>
Current skx_edac driver doesn't support address translation for non-volatile DIMMs.
The wrapped ACPI DSM methods support address translation for both volatile DIMMs and non-volatile DIMMs. So switch skx_edac to use the wrapped ACPI DSM methods, if they are supported and there are non-volatile DIMMs populated on the system.
Signed-off-by Qiuxu Zhuo <[email protected]>
Signed-off-by Tony Luck <[email protected]>
--- drivers/edac/skx_edac.c | 142 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 129 insertions(+), 13 deletions(-)
drivers/edac/skx_edac.c +129 -13
@@ -35,6 +35,8 @@#include "edac_module.h" #define EDAC_MOD_STR "skx_edac" +#define DSM_MOD_STR "dsm_edac" +#define MSG_SIZE 1024/* * Debug macros
@@ -54,6 +56,17 @@static LIST_HEAD (skx_edac_list);static u64 skx_tolm, skx_tohm; +static int nvdimm_count; +static const struct edac_dsm_operations *dsm_ops; +static char component_names[][32 ] = { +"ProcessorSocketId" , +"MemoryControllerId" , +"ChannelId" , +"DimmSlotId" , +"" +}; + +static int component_indices[ARRAY_SIZE (component_names)];#define NUM_IMC 2/* memory controllers per socket */ #define NUM_CHANNELS 3/* channels per memory controller */
@@ -102,11 +115,11 @@ struct decoded_addr {u64 addr;int socket;int imc; -int channel; +u64 channel;u64 chan_addr;int sktways;int chanways; -int dimm; +u64 dimm;int rank;int channel_rank;u64 rank_address;
@@ -393,6 +406,8 @@ static int get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc,u16 flags;u64 size =0 ; + nvdimm_count++; + dev_handle =ACPI_NFIT_BUILD_DEVICE_HANDLE (dimmno, chan, imc->lmc, imc->src_id,0 );
@@ -682,7 +697,7 @@ static bool skx_sad_decode(struct decoded_addr *res) res->imc =GET_BITFIELD (d->mcroute, lchan *3 , lchan *3 +2 ); res->channel =GET_BITFIELD (d->mcroute, lchan *2 +18 , lchan *2 +19 ); -edac_dbg (2 ,"%llx: socket=%d imc=%d channel=%d\n" , +edac_dbg (2 ,"%llx: socket=%d imc=%d channel=%llu\n" , res->addr, res->socket, res->imc, res->channel);return true; }
@@ -818,7 +833,7 @@ static bool skx_rir_decode(struct decoded_addr *res) res->dimm = chan_rank /4 ; res->rank = chan_rank %4 ; -edac_dbg (2 ,"%llx: dimm=%d rank=%d chan_rank=%d rank_addr=%llx\n" , +edac_dbg (2 ,"%llx: dimm=%llu rank=%d chan_rank=%d rank_addr=%llx\n" , res->addr, res->dimm, res->rank, res->channel_rank, res->rank_address);return true;
@@ -894,12 +909,32 @@ static bool skx_decode(struct decoded_addr *res)skx_rir_decode (res) &&skx_mad_decode (res); } +static bool skx_dsm_decode (u64 addr,char *msg,int msglen, +int indices[],u64 *sock,u64 *imc, +u64 *chan,u64 *dimm) + +{ +if (addr >= skx_tohm || (addr >= skx_tolm && addr <BIT_ULL (32 ))) { +edac_dbg (0 ,"Address %llx out of range\n" , addr); +return false; + } + +if (dsm_ops->decode (addr, msg, msglen, indices, + sock, imc, chan, dimm)) { +edac_dbg (0 ,"Failed to decode 0x%llx\n" , addr); +return false; + } + +return true; +} +static void skx_mce_output_error (struct mem_ctl_info *mci,const struct mce *m, -struct decoded_addr *res) +struct decoded_addr *res, +char *msg) {enum hw_event_mc_err_type tp_event; -char *type, *optype, msg[256 ]; +char *type, *optype;bool ripv =GET_BITFIELD (m->mcgstatus,0 ,0 );bool overflow =GET_BITFIELD (m->status,62 ,62 );bool uncorrected_error =GET_BITFIELD (m->status,61 ,61 );
@@ -960,13 +995,11 @@ static void skx_mce_output_error(struct mem_ctl_info *mci, } } -snprintf (msg,sizeof (msg), -"%s%s err_code:%04x:%04x socket:%d imc:%d rank:%d bg:%d ba:%d row:%x col:%x" , +snprintf (msg,MSG_SIZE , +"%s%s err_code:%04x:%04x %s" , overflow ?" OVERFLOW" :"" , (uncorrected_error && recoverable) ?" recoverable" :"" , - mscod, errcode, - res->socket, res->imc, res->rank, - res->bank_group, res->bank_address, res->row, res->column); + mscod, errcode, msg +MSG_SIZE );edac_dbg (0 ,"%s\n" , msg);
@@ -977,13 +1010,33 @@ static void skx_mce_output_error(struct mem_ctl_info *mci, optype, msg); } +static struct mem_ctl_info *get_mci (u64 src_id,u64 lmc) +{ +struct skx_dev *d; + +if (lmc >NUM_IMC -1 ) { +skx_printk (KERN_ERR ,"Bad mc# 0x%llx\n" , lmc); +return NULL ; + } + +list_for_each_entry (d, &skx_edac_list, list) { +if (d->imc[0 ].src_id == src_id) +return d->imc[lmc].mci; + } + +skx_printk (KERN_ERR ,"No mci for src_id %llx lmc %llx\n" , src_id, lmc); + +return NULL ; +} +static int skx_mce_check_error (struct notifier_block *nb,unsigned long val,void *data) {struct mce *mce = (struct mce *)data;struct decoded_addr res;struct mem_ctl_info *mci; -char *type; +u64 socket, memctrl; +char *type, *msg;if (edac_get_report_status () ==EDAC_REPORTING_DISABLED )return NOTIFY_DONE ;
@@ -992,11 +1045,36 @@ static int skx_mce_check_error(struct notifier_block *nb, unsigned long val,if ((mce->status &0xefff ) >>7 !=1 || !(mce->status &MCI_STATUS_ADDRV ))return NOTIFY_DONE ; + msg =kzalloc (MSG_SIZE *2 ,GFP_KERNEL ); +if (!msg) +return NOTIFY_DONE ; + +if (dsm_ops) + goto dsm_decoding; + res.addr = mce->addr;if (!skx_decode (&res))return NOTIFY_DONE ; mci = res.dev->imc[res.imc].mci; +snprintf (msg +MSG_SIZE ,MSG_SIZE , +"socket:%d imc:%d chan:%llu dimm:%llu rank:%d bg:%d ba:%d row:%x col:%x" , + res.socket, res.imc, res.channel, res.dimm, res.rank, + res.bank_group, res.bank_address, res.row, res.column); + + goto decoded; + +dsm_decoding: +if (!skx_dsm_decode (mce->addr, msg +MSG_SIZE ,MSG_SIZE , + component_indices, &socket, &memctrl, + &res.channel, &res.dimm)) + goto out; + + mci =get_mci (socket, memctrl); +if (!mci) + goto out; + +decoded:if (mce->mcgstatus &MCG_STATUS_MCIP ) type ="Exception" ;else
@@ -1015,8 +1093,10 @@ static int skx_mce_check_error(struct notifier_block *nb, unsigned long val,"%u APIC %x\n" , mce->cpuvendor, mce->cpuid, mce->time, mce->socketid, mce->apicid); -skx_mce_output_error (mci, mce, &res); +skx_mce_output_error (mci, mce, &res, msg); +out: +kfree (msg);return NOTIFY_DONE ; }
@@ -1088,6 +1168,29 @@ static void skx_remove(void) } } +static void __init skx_dsm_get (void ) +{ + dsm_ops =edac_dsm_get_ops (); +if (!dsm_ops && !request_module (DSM_MOD_STR )) + dsm_ops =edac_dsm_get_ops (); + +if (!dsm_ops) { +skx_printk (KERN_NOTICE ,"No firmware supports for address translation." ); +skx_printk (KERN_CONT ," Only decoding DDR4 address!\n" ); +return ; + } + +if (dsm_ops->check (component_names, component_indices)) { +edac_dsm_put_ops (dsm_ops); + dsm_ops =NULL ; + } +} + +static void skx_dsm_put (void ) +{ +edac_dsm_put_ops (dsm_ops); +} +/* * skx_init: * make sure we are running on the correct cpu model
@@ -1152,6 +1255,9 @@ static int __init skx_init(void) } } +if (nvdimm_count) +skx_dsm_get (); +/* Ensure that the OPSTATE is set correctly for POLL or NMI */ opstate_init ();
@@ -1171,6 +1277,8 @@ static void __exit skx_exit(void)mce_unregister_decode_chain (&skx_mce_dec);skx_remove ();teardown_skx_debug (); +if (dsm_ops) +skx_dsm_put (); }module_init (skx_init);
@@ -1178,6 +1286,14 @@ module_exit(skx_exit);module_param (edac_op_state, int,0444 );MODULE_PARM_DESC (edac_op_state,"EDAC Error Reporting state: 0=Poll,1=NMI" ); +module_param_string (sock, component_names[0 ],sizeof (component_names[0 ]),0644 ); +MODULE_PARM_DESC (sock,"String to get socket ID" ); +module_param_string (imc, component_names[1 ],sizeof (component_names[1 ]),0644 ); +MODULE_PARM_DESC (imc,"String to get IMC ID" ); +module_param_string (chan, component_names[2 ],sizeof (component_names[2 ]),0644 ); +MODULE_PARM_DESC (chan,"String to get channel ID" ); +module_param_string (dimm, component_names[3 ],sizeof (component_names[3 ]),0644 ); +MODULE_PARM_DESC (dimm,"String to get DIMM ID" );MODULE_LICENSE ("GPL v2" );MODULE_AUTHOR ("Tony Luck" );