Implement page offline recovery action for AMD
authorChristoph Egger <Christoph.Egger@amd.com>
Tue, 9 Oct 2012 11:51:37 +0000 (12:51 +0100)
committerChristoph Egger <Christoph.Egger@amd.com>
Tue, 9 Oct 2012 11:51:37 +0000 (12:51 +0100)
Signed-off-by: Christoph Egger <Christoph.Egger@amd.com>
Committed-by: Keir Fraser <keir@xen.org>
xen/arch/x86/cpu/mcheck/Makefile
xen/arch/x86/cpu/mcheck/amd_f10.c
xen/arch/x86/cpu/mcheck/mcaction.c [new file with mode: 0644]
xen/arch/x86/cpu/mcheck/mcaction.h [new file with mode: 0644]
xen/arch/x86/cpu/mcheck/mce.c
xen/arch/x86/cpu/mcheck/mce_amd.c
xen/arch/x86/cpu/mcheck/mce_amd.h
xen/arch/x86/cpu/mcheck/mce_intel.c

index 7138b2358f3d91ce370c825ce1dd1e6404f4c3b5..e0b3186ef6cae599f432fc8d847ba8d8e8d5cf9e 100644 (file)
@@ -3,6 +3,7 @@ obj-y += k7.o
 obj-y += amd_k8.o
 obj-y += amd_f10.o
 obj-y += mce_amd.o
+obj-y += mcaction.o
 obj-y += barrier.o
 obj-y += mctelem.o
 obj-y += mce.o
index e112f89346d7d2e7aaddc26010561215830442d9..1d9067c3f774019d02c70ad191dd6ba4bb91797d 100644 (file)
@@ -44,6 +44,7 @@
 #include "mce_quirks.h"
 #include "x86_mca.h"
 #include "mce_amd.h"
+#include "mcaction.h"
 
 static struct mcinfo_extended *
 amd_f10_handler(struct mc_info *mi, uint16_t bank, uint64_t status)
@@ -97,6 +98,7 @@ enum mcheck_type amd_f10_mcheck_init(struct cpuinfo_x86 *c)
 
        x86_mce_callback_register(amd_f10_handler);
        mce_recoverable_register(mc_amd_recoverable_scan);
+       mce_register_addrcheck(mc_amd_addrcheck);
 
        return mcheck_amd_famXX;
 }
diff --git a/xen/arch/x86/cpu/mcheck/mcaction.c b/xen/arch/x86/cpu/mcheck/mcaction.c
new file mode 100644 (file)
index 0000000..27e3796
--- /dev/null
@@ -0,0 +1,139 @@
+#include <xen/types.h>
+#include <xen/sched.h>
+#include "mcaction.h"
+#include "vmce.h"
+#include "mce.h"
+
+static struct mcinfo_recovery *
+mci_action_add_pageoffline(int bank, struct mc_info *mi,
+                       uint64_t mfn, uint32_t status)
+{
+    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 = bank;
+    rec->action_types = MC_ACTION_PAGE_OFFLINE;
+    rec->action_info.page_retire.mfn = mfn;
+    rec->action_info.page_retire.status = status;
+    return rec;
+}
+
+mce_check_addr_t mc_check_addr = NULL;
+
+void mce_register_addrcheck(mce_check_addr_t cbfunc)
+{
+    mc_check_addr = cbfunc;
+}
+
+void
+mc_memerr_dhandler(struct mca_binfo *binfo,
+                   enum mce_result *result,
+                   struct cpu_user_regs *regs)
+{
+    struct mcinfo_bank *bank = binfo->mib;
+    struct mcinfo_global *global = binfo->mig;
+    struct domain *d;
+    unsigned long mfn, gfn;
+    uint32_t status;
+    uint16_t vmce_vcpuid;
+
+    if (!mc_check_addr(bank->mc_status, bank->mc_misc, MC_ADDR_PHYSICAL)) {
+        dprintk(XENLOG_WARNING,
+            "No physical address provided for memory error\n");
+        return;
+    }
+
+    mfn = bank->mc_addr >> PAGE_SHIFT;
+    if (offline_page(mfn, 1, &status))
+    {
+        dprintk(XENLOG_WARNING,
+                "Failed to offline page %lx for MCE error\n", mfn);
+        return;
+    }
+
+    mci_action_add_pageoffline(binfo->bank, binfo->mi, mfn, status);
+
+    /* This is free page */
+    if (status & PG_OFFLINE_OFFLINED)
+        *result = MCER_RECOVERED;
+    else if (status & PG_OFFLINE_AGAIN)
+        *result = MCER_CONTINUE;
+    else if (status & PG_OFFLINE_PENDING) {
+        /* This page has owner */
+        if (status & PG_OFFLINE_OWNED) {
+            bank->mc_domid = status >> PG_OFFLINE_OWNER_SHIFT;
+            mce_printk(MCE_QUIET, "MCE: This error page is ownded"
+              " by DOM %d\n", bank->mc_domid);
+            /* XXX: Cannot handle shared pages yet
+             * (this should identify all domains and gfn mapping to
+             *  the mfn in question) */
+            BUG_ON( bank->mc_domid == DOMID_COW );
+            if ( bank->mc_domid != DOMID_XEN ) {
+                d = get_domain_by_id(bank->mc_domid);
+                ASSERT(d);
+                gfn = get_gpfn_from_mfn((bank->mc_addr) >> PAGE_SHIFT);
+
+                if ( !is_vmce_ready(bank, d) )
+                {
+                    printk("DOM%d not ready for vMCE\n", d->domain_id);
+                    goto vmce_failed;
+                }
+
+                if ( unmmap_broken_page(d, _mfn(mfn), gfn) )
+                {
+                    printk("Unmap broken memory %lx for DOM%d failed\n",
+                            mfn, d->domain_id);
+                    goto vmce_failed;
+                }
+
+                bank->mc_addr = gfn << PAGE_SHIFT |
+                  (bank->mc_addr & (PAGE_SIZE -1 ));
+                if ( fill_vmsr_data(bank, d,
+                      global->mc_gstatus) == -1 )
+                {
+                    mce_printk(MCE_QUIET, "Fill vMCE# data for DOM%d "
+                      "failed\n", bank->mc_domid);
+                    goto vmce_failed;
+                }
+
+                if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL )
+                    vmce_vcpuid = VMCE_INJECT_BROADCAST;
+                else
+                    vmce_vcpuid = global->mc_vcpuid;
+
+                /* We will inject vMCE to DOMU*/
+                if ( inject_vmce(d, vmce_vcpuid) < 0 )
+                {
+                    mce_printk(MCE_QUIET, "inject vMCE to DOM%d"
+                      " failed\n", d->domain_id);
+                    goto vmce_failed;
+                }
+
+                /* Impacted domain go on with domain's recovery job
+                 * if the domain has its own MCA handler.
+                 * For xen, it has contained the error and finished
+                 * its own recovery job.
+                 */
+                *result = MCER_RECOVERED;
+                put_domain(d);
+
+                return;
+vmce_failed:
+                put_domain(d);
+                domain_crash(d);
+            }
+        }
+    }
+}
diff --git a/xen/arch/x86/cpu/mcheck/mcaction.h b/xen/arch/x86/cpu/mcheck/mcaction.h
new file mode 100644 (file)
index 0000000..0f70a24
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef _MCHECK_ACTION_H
+#define _MCHECK_ACTION_H
+
+#include <xen/types.h>
+#include "x86_mca.h"
+
+void
+mc_memerr_dhandler(struct mca_binfo *binfo,
+                   enum mce_result *result,
+                   struct cpu_user_regs *regs);
+
+#define MC_ADDR_PHYSICAL  0
+#define MC_ADDR_VIRTUAL   1
+
+typedef int (*mce_check_addr_t)(uint64_t status, uint64_t misc, int addr_type);
+extern void mce_register_addrcheck(mce_check_addr_t);
+
+extern mce_check_addr_t mc_check_addr;
+
+#endif
index 5f0339469985fed211669c308d887bdc873e62e2..6f8c6f94b6bcf380943f175749aab34e8b906b51 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "mce.h"
 #include "barrier.h"
+#include "mcaction.h"
 #include "util.h"
 #include "vmce.h"
 
@@ -216,7 +217,7 @@ static void mca_init_bank(enum mca_source who,
 
     if ((mib->mc_status & MCi_STATUS_MISCV) &&
         (mib->mc_status & MCi_STATUS_ADDRV) &&
-        ((mib->mc_misc & MCi_MISC_ADDRMOD_MASK) == MCi_MISC_PHYSMOD) && 
+        (mc_check_addr(mib->mc_status, mib->mc_misc, MC_ADDR_PHYSICAL)) &&
         (who == MCA_POLLER || who == MCA_CMCI_HANDLER) &&
         (mfn_valid(paddr_to_pfn(mib->mc_addr))))
     {
index 3c64d24f08e014265e74a5374828fe4e602e1dc9..0691f6a5a69d4d00864b4462d4b19bffa52bcb87 100644 (file)
@@ -25,6 +25,7 @@
 #include "mce.h"
 #include "x86_mca.h"
 #include "mce_amd.h"
+#include "mcaction.h"
 
 /* Error Code Types */
 enum mc_ec_type {
@@ -75,3 +76,25 @@ mc_amd_recoverable_scan(uint64_t status)
 
     return ret;
 }
+
+int
+mc_amd_addrcheck(uint64_t status, uint64_t misc, int addrtype)
+{
+    enum mc_ec_type ectype;
+    uint16_t errorcode;
+
+    errorcode = status & (MCi_STATUS_MCA | MCi_STATUS_MSEC);
+    ectype = mc_ec2type(errorcode);
+
+    switch (ectype) {
+    case MC_EC_BUS_TYPE: /* value in addr MSR is physical */
+    case MC_EC_MEM_TYPE: /* value in addr MSR is physical */
+        return (addrtype == MC_ADDR_PHYSICAL);
+    case MC_EC_TLB_TYPE: /* value in addr MSR is virtual */
+        return (addrtype == MC_ADDR_VIRTUAL);
+    }
+
+    /* unreached */
+    BUG();
+    return 0;
+}
index 10e363134dc190ef0aae796c9f726a0fb979b603..de5fc48d91009d09037cf72a8fba7c4b4f46343c 100644 (file)
@@ -2,5 +2,6 @@
 #define _MCHECK_AMD_H
 
 int mc_amd_recoverable_scan(uint64_t status);
+int mc_amd_addrcheck(uint64_t status, uint64_t misc, int addrtype);
 
 #endif
index babab904be392eceb7adcb6963c616e6dcf9f999..e70bd55b1d934be44261b8af6db9709d62174829 100644 (file)
@@ -19,6 +19,7 @@
 #include "barrier.h"
 #include "util.h"
 #include "vmce.h"
+#include "mcaction.h"
 
 DEFINE_PER_CPU(struct mca_banks *, mce_banks_owned);
 DEFINE_PER_CPU(struct mca_banks *, no_cmci_banks);
@@ -257,130 +258,13 @@ static enum intel_mce_type intel_check_mce_type(uint64_t status)
     return intel_mce_fatal;
 }
 
-struct mcinfo_recovery *mci_add_pageoff_action(int bank, struct mc_info *mi,
-                              uint64_t mfn, uint32_t status)
-{
-    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->mc_bank = bank;
-    rec->action_types = MC_ACTION_PAGE_OFFLINE;
-    rec->action_info.page_retire.mfn = mfn;
-    rec->action_info.page_retire.status = status;
-    return rec;
-}
-
 static void intel_memerr_dhandler(
              struct mca_binfo *binfo,
              enum mce_result *result,
              struct cpu_user_regs *regs)
 {
-    struct mcinfo_bank *bank = binfo->mib;
-    struct mcinfo_global *global = binfo->mig;
-    struct domain *d;
-    unsigned long mfn, gfn;
-    uint32_t status;
-    uint64_t mc_status, mc_misc;
-
     mce_printk(MCE_VERBOSE, "MCE: Enter UCR recovery action\n");
-
-    mc_status = bank->mc_status;
-    mc_misc = bank->mc_misc;
-    if (!(mc_status &  MCi_STATUS_ADDRV) ||
-        !(mc_status & MCi_STATUS_MISCV) ||
-        ((mc_misc & MCi_MISC_ADDRMOD_MASK) != MCi_MISC_PHYSMOD) )
-    {
-        dprintk(XENLOG_WARNING,
-            "No physical address provided for memory error\n");
-        return;
-    }
-
-    mfn = bank->mc_addr >> PAGE_SHIFT;
-    if (offline_page(mfn, 1, &status))
-    {
-        dprintk(XENLOG_WARNING,
-                "Failed to offline page %lx for MCE error\n", mfn);
-        return;
-    }
-
-    mci_add_pageoff_action(binfo->bank, binfo->mi, mfn, status);
-
-    /* This is free page */
-    if (status & PG_OFFLINE_OFFLINED)
-        *result = MCER_RECOVERED;
-    else if (status & PG_OFFLINE_AGAIN)
-        *result = MCER_CONTINUE;
-    else if (status & PG_OFFLINE_PENDING) {
-        /* This page has owner */
-        if (status & PG_OFFLINE_OWNED) {
-            bank->mc_domid = status >> PG_OFFLINE_OWNER_SHIFT;
-            mce_printk(MCE_QUIET, "MCE: This error page is ownded"
-              " by DOM %d\n", bank->mc_domid);
-            /* XXX: Cannot handle shared pages yet 
-             * (this should identify all domains and gfn mapping to
-             *  the mfn in question) */
-            BUG_ON( bank->mc_domid == DOMID_COW );
-            if ( bank->mc_domid != DOMID_XEN ) {
-                d = get_domain_by_id(bank->mc_domid);
-                ASSERT(d);
-                gfn = get_gpfn_from_mfn((bank->mc_addr) >> PAGE_SHIFT);
-
-                if ( !is_vmce_ready(bank, d) )
-                {
-                    printk("DOM%d not ready for vMCE\n", d->domain_id);
-                    goto vmce_failed;
-                }
-
-                if ( unmmap_broken_page(d, _mfn(mfn), gfn) )
-                {
-                    printk("Unmap broken memory %lx for DOM%d failed\n",
-                            mfn, d->domain_id);
-                    goto vmce_failed;
-                }
-
-                bank->mc_addr =  gfn << PAGE_SHIFT |
-                  (bank->mc_addr & (PAGE_SIZE -1 ));
-                if ( fill_vmsr_data(bank, d,
-                      global->mc_gstatus) == -1 )
-                {
-                    mce_printk(MCE_QUIET, "Fill vMCE# data for DOM%d "
-                      "failed\n", bank->mc_domid);
-                    goto vmce_failed;
-                }
-
-                /* We will inject vMCE to DOMU*/
-                if ( inject_vmce(d, VMCE_INJECT_BROADCAST) < 0 )
-                {
-                    mce_printk(MCE_QUIET, "inject vMCE to DOM%d"
-                      " failed\n", d->domain_id);
-                    goto vmce_failed;
-                }
-                /* Impacted domain go on with domain's recovery job
-                 * if the domain has its own MCA handler.
-                 * For xen, it has contained the error and finished
-                 * its own recovery job.
-                 */
-                *result = MCER_RECOVERED;
-                put_domain(d);
-
-                return;
-vmce_failed:
-                put_domain(d);
-                domain_crash(d);
-            }
-        }
-    }
+    mc_memerr_dhandler(binfo, result, regs);
 }
 
 static int intel_srar_check(uint64_t status)
@@ -388,6 +272,19 @@ static int intel_srar_check(uint64_t status)
     return ( intel_check_mce_type(status) == intel_mce_ucr_srar );
 }
 
+static int intel_checkaddr(uint64_t status, uint64_t misc, int addrtype)
+{
+    if (!(status & MCi_STATUS_ADDRV) ||
+        !(status & MCi_STATUS_MISCV) ||
+        ((misc & MCi_MISC_ADDRMOD_MASK) != MCi_MISC_PHYSMOD) )
+    {
+        /* addr is virtual */
+        return (addrtype == MC_ADDR_VIRTUAL);
+    }
+
+    return (addrtype == MC_ADDR_PHYSICAL);
+}
+
 static void intel_srar_dhandler(
              struct mca_binfo *binfo,
              enum mce_result *result,
@@ -882,6 +779,7 @@ static void intel_init_mce(void)
     x86_mce_vector_register(intel_machine_check);
     mce_recoverable_register(intel_recoverable_scan);
     mce_need_clearbank_register(intel_need_clearbank_scan);
+    mce_register_addrcheck(intel_checkaddr);
 
     mce_dhandlers = intel_mce_dhandlers;
     mce_dhandler_num = ARRAY_SIZE(intel_mce_dhandlers);