# HG changeset patch # User Christoph Egger # Date 1351516427 -3600 Implement recovery handlers diff -r 0164b2acce7a -r c7461f8f3e95 xen/arch/x86/cpu/mcheck/amd_f10.c --- a/xen/arch/x86/cpu/mcheck/amd_f10.c +++ b/xen/arch/x86/cpu/mcheck/amd_f10.c @@ -37,6 +37,7 @@ #include #include +#include #include @@ -85,6 +86,133 @@ amd_f10_handler(struct mc_info *mi, uint return mc_ext; } +/* AMD specific family 0x10+ mc actions */ +static void +mc_link_dhandler(struct mca_binfo *binfo, + enum mce_result *result, + struct cpu_user_regs *regs) +{ + struct mcinfo_bank *bank = binfo->mib; + + if (bank->mc_bank != 4) + return; + + if (!(bank->mc_status & MCi_STATUS_UC)) + return; + + /* uncorrectable link errors happens very rarely, + * it isn't really worth to handle them. + */ + *result = MCER_RESET; +} + +static struct mcinfo_recovery * +mci_action_add_cacheshrink(int mc_bank, struct mc_info *mi, + uint32_t mc_l3index, uint32_t mc_l3subcache) +{ + struct mcinfo_recovery *rec; + + if (!mi) + return NULL; + + rec = x86_mcinfo_reserve(mi, sizeof(struct mcinfo_recovery)); + if (!rec) { + mi->flags |= MCINFO_FLAGS_UNCOMPLETE; + return NULL; + } + + memset(rec, 0, sizeof(struct mcinfo_recovery)); + + rec->common.type = MC_TYPE_RECOVERY; + rec->common.size = sizeof(*rec); + rec->mc_bank = mc_bank; + rec->action_types = MC_ACTION_CACHE_SHRINK; + rec->action_flags = REC_ACTION_NONE | REC_ACTION_NEED_RESET; + + rec->action_info.mcr_cache_shrink.cs_cacheindex = mc_l3index; + rec->action_info.mcr_cache_shrink.cs_subcache = mc_l3subcache; + + return rec; +} + +static void +mc_l3cache_dhandler(struct mca_binfo *binfo, + enum mce_result *result, + struct cpu_user_regs *regs) +{ + /* Goal: Recover by disabling l3 cache index */ + struct mcinfo_bank *bank = binfo->mib; + uint32_t l3index, l3subcache; + + if (bank->mc_bank != 4) + return; + + l3subcache = (bank->mc_status & MCi_STATUS_L3SUBCACHE) >> 42; + l3index = (bank->mc_addr >> 6) & 0x7ff; + + mci_action_add_cacheshrink(bank->mc_bank, binfo->mi, + l3index, l3subcache); + + *result = MCER_RECOVERED_BYDOM0; +} + +void +mc_amdf10_northbridge_handler(struct mca_binfo *binfo, enum mce_result *result, + struct cpu_user_regs *regs) +{ + struct mcinfo_bank *bank = binfo->mib; + struct mcinfo_extended *mc_ext; + void *mic; + uint64_t misc0, misc1, misc2; + + if (bank->mc_bank != 4) + return; + + if (!(bank->mc_status & MCi_STATUS_MISCV)) + return; + + x86_mcinfo_lookup(mic, binfo->mi, MC_TYPE_EXTENDED); + mc_ext = mic; + if (mc_ext == NULL) + return; + + /* DRAM */ + misc0 = bank->mc_misc; + if (misc0 & (MC4_MISCj_VALID | MC4_MISCj_CNTP)) { + /* DRAM error happened */ + if (misc0 & MC4_MISCj_ERRCNT) { + mc_memerr_dhandler(binfo, result, regs); + /* Clear error counter */ + mca_wrmsr(mc_ext->mc_msr[0].reg, + misc0 & ~MC4_MISCj_ERRCNT); + } + } + + /* Link */ + misc1 = mc_ext->mc_msr[0].value; + if (misc1 & (MC4_MISCj_VALID | MC4_MISCj_CNTP)) { + /* Link error happened */ + if (misc1 & MC4_MISCj_ERRCNT) { + mc_link_dhandler(binfo, result, regs); + /* Clear error counter */ + mca_wrmsr(mc_ext->mc_msr[1].reg, + misc1 & ~MC4_MISCj_ERRCNT); + } + } + + /* L3 Cache */ + misc2 = mc_ext->mc_msr[1].value; + if (misc2 & (MC4_MISCj_VALID | MC4_MISCj_CNTP)) { + /* L3 Cache error happened */ + mc_l3cache_dhandler(binfo, result, regs); + if (misc2 & MC4_MISCj_ERRCNT) { + /* Clear error counter */ + mca_wrmsr(mc_ext->mc_msr[2].reg, + misc2 & ~MC4_MISCj_ERRCNT); + } + } +} + /* AMD Family10 machine check */ enum mcheck_type amd_f10_mcheck_init(struct cpuinfo_x86 *c) { @@ -100,6 +228,8 @@ enum mcheck_type amd_f10_mcheck_init(str mce_recoverable_register(mc_amd_recoverable_scan); mce_register_addrcheck(mc_amd_addrcheck); + mc_amd_init_recovery_handlers(); + return mcheck_amd_famXX; } diff -r 0164b2acce7a -r c7461f8f3e95 xen/arch/x86/cpu/mcheck/mcaction.c --- a/xen/arch/x86/cpu/mcheck/mcaction.c +++ b/xen/arch/x86/cpu/mcheck/mcaction.c @@ -30,6 +30,36 @@ mci_action_add_pageoffline(int bank, str return rec; } +struct mcinfo_recovery * +mci_action_add_cpuoffline(int mc_bank, struct mc_info *mi, + uint32_t mc_socketid, uint16_t mc_coreid, uint16_t mc_core_threadid, + uint32_t mc_nodeid) +{ + struct mcinfo_recovery *rec; + + if (!mi) + return NULL; + + rec = x86_mcinfo_reserve(mi, sizeof(struct mcinfo_recovery)); + if (!rec) { + mi->flags |= MCINFO_FLAGS_UNCOMPLETE; + return NULL; + } + + memset(rec, 0, sizeof(struct mcinfo_recovery)); + + rec->common.type = MC_TYPE_RECOVERY; + rec->common.size = sizeof(*rec); + rec->mc_bank = mc_bank; + rec->action_types = MC_ACTION_CPU_OFFLINE; + rec->action_info.cpu_offline.mc_socketid = mc_socketid; + rec->action_info.cpu_offline.mc_coreid = mc_coreid; + rec->action_info.cpu_offline.mc_core_threadid = mc_core_threadid; + //rec->action_info.cpu_offline.mc_nodeid = mc_nodeid; + + return rec; +} + mce_check_addr_t mc_check_addr = NULL; void mce_register_addrcheck(mce_check_addr_t cbfunc) diff -r 0164b2acce7a -r c7461f8f3e95 xen/arch/x86/cpu/mcheck/mce.c --- a/xen/arch/x86/cpu/mcheck/mce.c +++ b/xen/arch/x86/cpu/mcheck/mce.c @@ -1116,6 +1116,19 @@ static int x86_mc_msrinject_verify(struc case MSR_IA32_MCG_STATUS: break; + case MSR_F10_MC4_MISC1: + case MSR_F10_MC4_MISC2: + case MSR_F10_MC4_MISC3: + if (c->x86_vendor != X86_VENDOR_AMD) { + reason = "only supported on AMD"; + break; + } + if (c->x86 < 0x10) { + reason = "only supported on AMD Family 10h+"; + break; + } + break; + /* MSRs that the HV will take care of */ case MSR_K8_HWCR: if (c->x86_vendor == X86_VENDOR_AMD) @@ -1265,9 +1278,11 @@ CHECK_mcinfo_extended; # define xen_mcinfo_recovery mcinfo_recovery # define xen_cpu_offline_action cpu_offline_action # define xen_page_offline_action page_offline_action +# define xen_mcr_cache_shrink_action mcr_cache_shrink_action CHECK_mcinfo_recovery; # undef xen_cpu_offline_action # undef xen_page_offline_action +# undef xen_mcr_cache_shrink_action # undef xen_mcinfo_recovery /* Machine Check Architecture Hypercall */ @@ -1642,6 +1657,10 @@ static int mce_delayed_action(mctelem_co dprintk(XENLOG_INFO, "MCE: Error is successfully recovered\n"); ret = 1; break; + case MCER_RECOVERED_BYDOM0: + dprintk(XENLOG_INFO, "MCE: Error will be recovered by Dom0\n"); + ret = 1; + break; case MCER_CONTINUE: dprintk(XENLOG_INFO, "MCE: Error can't be recovered, " "system is tainted\n"); diff -r 0164b2acce7a -r c7461f8f3e95 xen/arch/x86/cpu/mcheck/mce_amd.c --- a/xen/arch/x86/cpu/mcheck/mce_amd.c +++ b/xen/arch/x86/cpu/mcheck/mce_amd.c @@ -55,6 +55,11 @@ mc_ec2type(uint16_t errorcode) return MC_EC_MEM_TYPE; if ( errorcode & MC_EC_TLB_TYPE ) return MC_EC_TLB_TYPE; + + /* Assumption, this is bank 4 */ + if ((errorcode & MCi_STATUS_MCA) == 0x0) + return MC_EC_MEM_TYPE; + /* Unreached */ BUG(); return 0; @@ -111,6 +116,127 @@ mc_amd_addrcheck(uint64_t status, uint64 return 0; } +enum amd_mce_type +{ + amd_mce_invalid, + amd_mce_fatal, + amd_mce_corrected, + amd_mce_recoverable, +}; + +static enum amd_mce_type +amd_check_mce_type(uint64_t status) +{ + if (!(status & MCi_STATUS_VAL)) + return amd_mce_invalid; + + if (status & MCi_STATUS_PCC) + return amd_mce_fatal; + + /* Corrected error? */ + if (!(status & MCi_STATUS_UC)) + return amd_mce_corrected; + + return amd_mce_recoverable; +} + +static int +amd_bus_check(uint64_t status) +{ + enum mc_ec_type ectype; + uint16_t errorcode; + + errorcode = status & (MCi_STATUS_MCA | MCi_STATUS_MSEC); + ectype = mc_ec2type(errorcode); + + return (ectype == MC_EC_BUS_TYPE); +} + +static int +amd_tlb_check(uint64_t status) +{ + enum mc_ec_type ectype; + uint16_t errorcode; + + errorcode = status & (MCi_STATUS_MCA | MCi_STATUS_MSEC); + ectype = mc_ec2type(errorcode); + + return (ectype == MC_EC_TLB_TYPE); +} + +static int +amd_mem_check(uint64_t status) +{ + enum mc_ec_type ectype; + uint16_t errorcode; + + errorcode = status & (MCi_STATUS_MCA | MCi_STATUS_MSEC); + ectype = mc_ec2type(errorcode); + + return (ectype == MC_EC_MEM_TYPE); +} + +static int +amd_default_check(uint64_t status) +{ + return 1; +} + +static void +amd_bus_handler(struct mca_binfo *binfo, enum mce_result *result, + struct cpu_user_regs *regs) +{ +} + +static void +amd_tlb_handler(struct mca_binfo *binfo, enum mce_result *result, + struct cpu_user_regs *regs) +{ +} + +static void +amd_mem_handler(struct mca_binfo *binfo, enum mce_result *result, + struct cpu_user_regs *regs) +{ + mce_printk(MCE_VERBOSE, "%s: MCE memory error\n", __func__); + mc_amdf10_northbridge_handler(binfo, result, regs); +} + +static void +amd_default_handler(struct mca_binfo *binfo, enum mce_result *result, + struct cpu_user_regs *regs) +{ + uint64_t status = binfo->mib->mc_status; + enum amd_mce_type type; + + type = amd_check_mce_type(status); + + if (type == amd_mce_fatal) + *result = MCER_RESET; + else + *result = MCER_CONTINUE; +} + +static const struct mca_error_handler amd_mce_dhandlers[] = { + { amd_bus_check, amd_bus_handler }, + { amd_tlb_check, amd_tlb_handler }, + { amd_mem_check, amd_mem_handler }, + { amd_default_check, amd_default_handler } +}; + +static const struct mca_error_handler amd_mce_uhandlers[] = { + { amd_default_check, amd_default_handler } +}; + +void +mc_amd_init_recovery_handlers(void) +{ + mce_dhandlers = amd_mce_dhandlers; + mce_dhandler_num = ARRAY_SIZE(amd_mce_dhandlers); + mce_uhandlers = amd_mce_uhandlers; + mce_uhandler_num = ARRAY_SIZE(amd_mce_uhandlers); +} + /* MC quirks */ enum mcequirk_amd_flags mcequirk_lookup_amd_quirkdata(struct cpuinfo_x86 *c) diff -r 0164b2acce7a -r c7461f8f3e95 xen/arch/x86/cpu/mcheck/mce_amd.h --- a/xen/arch/x86/cpu/mcheck/mce_amd.h +++ b/xen/arch/x86/cpu/mcheck/mce_amd.h @@ -1,10 +1,18 @@ #ifndef _MCHECK_AMD_H #define _MCHECK_AMD_H +#include "x86_mca.h" + enum mcheck_type amd_k8_mcheck_init(struct cpuinfo_x86 *c); enum mcheck_type amd_f10_mcheck_init(struct cpuinfo_x86 *c); int mc_amd_recoverable_scan(uint64_t status); int mc_amd_addrcheck(uint64_t status, uint64_t misc, int addrtype); +void mc_amd_init_recovery_handlers(void); + +/* amd_f10.c */ +void mc_amdf10_northbridge_handler(struct mca_binfo *, enum mce_result *, + struct cpu_user_regs *); + #endif diff -r 0164b2acce7a -r c7461f8f3e95 xen/arch/x86/cpu/mcheck/mce_intel.c --- a/xen/arch/x86/cpu/mcheck/mce_intel.c +++ b/xen/arch/x86/cpu/mcheck/mce_intel.c @@ -785,7 +785,7 @@ static void intel_init_mce(void) mce_uhandler_num = ARRAY_SIZE(intel_mce_uhandlers); } -static void cpu_mcabank_free(unsigned int cpu) +static void intel_cpu_mcabank_free(unsigned int cpu) { struct mca_banks *cmci = per_cpu(no_cmci_banks, cpu); struct mca_banks *owned = per_cpu(mce_banks_owned, cpu); @@ -794,7 +794,7 @@ static void cpu_mcabank_free(unsigned in mcabanks_free(owned); } -static int cpu_mcabank_alloc(unsigned int cpu) +static int intel_cpu_mcabank_alloc(unsigned int cpu) { struct mca_banks *cmci = mcabanks_alloc(); struct mca_banks *owned = mcabanks_alloc(); @@ -812,7 +812,7 @@ out: return -ENOMEM; } -static int cpu_callback( +static int intel_cpu_callback( struct notifier_block *nfb, unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long)hcpu; @@ -821,7 +821,7 @@ static int cpu_callback( switch ( action ) { case CPU_UP_PREPARE: - rc = cpu_mcabank_alloc(cpu); + rc = intel_cpu_mcabank_alloc(cpu); break; case CPU_DYING: cpu_mcheck_disable(); @@ -829,7 +829,7 @@ static int cpu_callback( case CPU_UP_CANCELED: case CPU_DEAD: cpu_mcheck_distribute_cmci(); - cpu_mcabank_free(cpu); + intel_cpu_mcabank_free(cpu); break; default: break; @@ -839,7 +839,7 @@ static int cpu_callback( } static struct notifier_block cpu_nfb = { - .notifier_call = cpu_callback + .notifier_call = intel_cpu_callback }; /* p4/p6 family have similar MCA initialization process */ @@ -848,7 +848,7 @@ enum mcheck_type intel_mcheck_init(struc if ( bsp ) { /* Early MCE initialisation for BSP. */ - if ( cpu_mcabank_alloc(0) ) + if ( intel_cpu_mcabank_alloc(0) ) BUG(); register_cpu_notifier(&cpu_nfb); mcheck_intel_therm_init(); diff -r 0164b2acce7a -r c7461f8f3e95 xen/arch/x86/cpu/mcheck/x86_mca.h --- a/xen/arch/x86/cpu/mcheck/x86_mca.h +++ b/xen/arch/x86/cpu/mcheck/x86_mca.h @@ -52,6 +52,10 @@ #define MCi_STATUS_MSEC 0x00000000ffff0000ULL /* Other information */ #define MCi_STATUS_OTHER 0x01ffffff00000000ULL +/* McaStatSubCache */ +#define MCi_STATUS_L3SUBCACHE 0x0000020000000000ULL /* AMD specific */ +/* SubLink */ +#define MCi_STATUS_SUBLINK 0x00000c0000000000ULL /* AMD specific */ /* Action Required flag */ #define MCi_STATUS_AR 0x0080000000000000ULL /* Intel specific */ /* Signaling flag */ @@ -83,6 +87,11 @@ /* reserved bits */ #define MCi_STATUS_OTHER_RESERVED2 0x0180000000000000ULL +/* Bitfield of MC4_MISCj MSR register */ +#define MC4_MISCj_VALID 0x8000000000000000ULL +#define MC4_MISCj_CNTP 0x4000000000000000ULL +#define MC4_MISCj_ERRCNT 0x00000fff00000000ULL + /* Bitfield of MSR_K8_HWCR register */ #define K8_HWCR_MCi_STATUS_WREN (1ULL << 18) @@ -138,6 +147,7 @@ enum mce_result { MCER_NOERROR, MCER_RECOVERED, + MCER_RECOVERED_BYDOM0, /* Not recovered, but can continue */ MCER_CONTINUE, MCER_RESET, diff -r 0164b2acce7a -r c7461f8f3e95 xen/include/public/arch-x86/xen-mca.h --- a/xen/include/public/arch-x86/xen-mca.h +++ b/xen/include/public/arch-x86/xen-mca.h @@ -105,6 +105,7 @@ #define MC_TYPE_BANK 1 #define MC_TYPE_EXTENDED 2 #define MC_TYPE_RECOVERY 3 +#define MC_TYPE_GLOBAL2 4 struct mcinfo_common { uint16_t type; /* structure type */ @@ -119,6 +120,7 @@ struct mcinfo_common { #define MC_FLAG_RESET (1 << 4) #define MC_FLAG_CMCI (1 << 5) #define MC_FLAG_MCE (1 << 6) + /* contains global x86 mc information */ struct mcinfo_global { struct mcinfo_common common; @@ -134,6 +136,21 @@ struct mcinfo_global { uint64_t mc_gstatus; /* global status */ }; +struct mcinfo_global2 { + struct mcinfo_common common; + + /* running domain at the time in error (most likely the impacted one) */ + uint16_t mc_domid; + uint16_t mc_vcpuid; /* virtual cpu scheduled for mc_domid */ + uint32_t mc_socketid; /* physical socket of the physical core */ + uint16_t mc_coreid; /* physical impacted core */ + uint16_t mc_core_threadid; /* core thread of physical core */ + uint32_t mc_apicid; + uint32_t mc_flags; + uint64_t mc_gstatus; /* global status */ + uint32_t mc_cpucore_nodeid; /* cpu core node id */ +}; + /* contains bank local x86 mc information */ struct mcinfo_bank { struct mcinfo_common common; @@ -210,11 +227,20 @@ struct page_offline_action struct cpu_offline_action { /* Params for passing the identity of the offlined CPU to DOM0 */ + /* XXX: This information is redundant since the information + * provided by mcinfo_global is enough. */ uint32_t mc_socketid; uint16_t mc_coreid; uint16_t mc_core_threadid; }; +struct mcr_cache_shrink_action +{ + /* Params for passing the identity of the cpu cache to DOM0 */ + uint32_t cs_cacheindex; + uint32_t cs_subcache; +}; + #define MAX_UNION_SIZE 16 struct mcinfo_recovery { @@ -225,6 +251,7 @@ struct mcinfo_recovery union { struct page_offline_action page_retire; struct cpu_offline_action cpu_offline; + struct mcr_cache_shrink_action mcr_cache_shrink; uint8_t pad[MAX_UNION_SIZE]; } action_info; }; diff -r 0164b2acce7a -r c7461f8f3e95 xen/include/xlat.lst --- a/xen/include/xlat.lst +++ b/xen/include/xlat.lst @@ -11,6 +11,7 @@ ! cpu_user_regs arch-x86/xen-@arch@.h ! trap_info arch-x86/xen.h ? cpu_offline_action arch-x86/xen-mca.h +? mcr_cache_shrink_action arch-x86/xen-mca.h ? mc arch-x86/xen-mca.h ? mcinfo_bank arch-x86/xen-mca.h ? mcinfo_common arch-x86/xen-mca.h