aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/powernv
diff options
context:
space:
mode:
authorMahesh Salgaonkar <mahesh@linux.vnet.ibm.com>2013-10-30 20:05:40 +0530
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2013-12-05 16:05:20 +1100
commit36df96f8acaf51992177645eb2d781f766ce97dc (patch)
tree0d7563425e6a848379e60b7a4b65d9239cc7e51e /arch/powerpc/platforms/powernv
parentae744f3432d3872c51298d922728e13c24ccc068 (diff)
powerpc/book3s: Decode and save machine check event.
Now that we handle machine check in linux, the MCE decoding should also take place in linux host. This info is crucial to log before we go down in case we can not handle the machine check errors. This patch decodes and populates a machine check event which contain high level meaning full MCE information. We do this in real mode C code with ME bit on. The MCE information is still available on emergency stack (in pt_regs structure format). Even if we take another exception at this point the MCE early handler will allocate a new stack frame on top of current one. So when we return back here we still have our MCE information safe on current stack. We use per cpu buffer to save high level MCE information. Each per cpu buffer is an array of machine check event structure indexed by per cpu counter mce_nest_count. The mce_nest_count is incremented every time we enter machine check early handler in real mode to get the current free slot (index = mce_nest_count - 1). The mce_nest_count is decremented once the MCE info is consumed by virtual mode machine exception handler. This patch provides save_mce_event(), get_mce_event() and release_mce_event() generic routines that can be used by machine check handlers to populate and retrieve the event. The routine release_mce_event() will free the event slot so that it can be reused. Caller can invoke get_mce_event() with a release flag either to release the event slot immediately OR keep it so that it can be fetched again. The event slot can be also released anytime by invoking release_mce_event(). This patch also updates kvm code to invoke get_mce_event to retrieve generic mce event rather than paca->opal_mce_evt. The KVM code always calls get_mce_event() with release flags set to false so that event is available for linus host machine If machine check occurs while we are in guest, KVM tries to handle the error. If KVM is able to handle MC error successfully, it enters the guest and delivers the machine check to guest. If KVM is not able to handle MC error, it exists the guest and passes the control to linux host machine check handler which then logs MC event and decides how to handle it in linux host. In failure case, KVM needs to make sure that the MC event is available for linux host to consume. Hence KVM always calls get_mce_event() with release flags set to false and later it invokes release_mce_event() only if it succeeds to handle error. Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/platforms/powernv')
-rw-r--r--arch/powerpc/platforms/powernv/opal.c35
1 files changed, 17 insertions, 18 deletions
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 1c798cd55372..c5e71d773f47 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -21,6 +21,7 @@
#include <linux/kobject.h>
#include <asm/opal.h>
#include <asm/firmware.h>
+#include <asm/mce.h>
#include "powernv.h"
@@ -256,8 +257,7 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
int opal_machine_check(struct pt_regs *regs)
{
- struct opal_machine_check_event *opal_evt = get_paca()->opal_mc_evt;
- struct opal_machine_check_event evt;
+ struct machine_check_event evt;
const char *level, *sevstr, *subtype;
static const char *opal_mc_ue_types[] = {
"Indeterminate",
@@ -282,30 +282,29 @@ int opal_machine_check(struct pt_regs *regs)
"Multihit",
};
- /* Copy the event structure and release the original */
- evt = *opal_evt;
- opal_evt->in_use = 0;
+ if (!get_mce_event(&evt, MCE_EVENT_RELEASE))
+ return 0;
/* Print things out */
- if (evt.version != OpalMCE_V1) {
+ if (evt.version != MCE_V1) {
pr_err("Machine Check Exception, Unknown event version %d !\n",
evt.version);
return 0;
}
switch(evt.severity) {
- case OpalMCE_SEV_NO_ERROR:
+ case MCE_SEV_NO_ERROR:
level = KERN_INFO;
sevstr = "Harmless";
break;
- case OpalMCE_SEV_WARNING:
+ case MCE_SEV_WARNING:
level = KERN_WARNING;
sevstr = "";
break;
- case OpalMCE_SEV_ERROR_SYNC:
+ case MCE_SEV_ERROR_SYNC:
level = KERN_ERR;
sevstr = "Severe";
break;
- case OpalMCE_SEV_FATAL:
+ case MCE_SEV_FATAL:
default:
level = KERN_ERR;
sevstr = "Fatal";
@@ -313,12 +312,12 @@ int opal_machine_check(struct pt_regs *regs)
}
printk("%s%s Machine check interrupt [%s]\n", level, sevstr,
- evt.disposition == OpalMCE_DISPOSITION_RECOVERED ?
+ evt.disposition == MCE_DISPOSITION_RECOVERED ?
"Recovered" : "[Not recovered");
printk("%s Initiator: %s\n", level,
- evt.initiator == OpalMCE_INITIATOR_CPU ? "CPU" : "Unknown");
+ evt.initiator == MCE_INITIATOR_CPU ? "CPU" : "Unknown");
switch(evt.error_type) {
- case OpalMCE_ERROR_TYPE_UE:
+ case MCE_ERROR_TYPE_UE:
subtype = evt.u.ue_error.ue_error_type <
ARRAY_SIZE(opal_mc_ue_types) ?
opal_mc_ue_types[evt.u.ue_error.ue_error_type]
@@ -331,7 +330,7 @@ int opal_machine_check(struct pt_regs *regs)
printk("%s Physial address: %016llx\n",
level, evt.u.ue_error.physical_address);
break;
- case OpalMCE_ERROR_TYPE_SLB:
+ case MCE_ERROR_TYPE_SLB:
subtype = evt.u.slb_error.slb_error_type <
ARRAY_SIZE(opal_mc_slb_types) ?
opal_mc_slb_types[evt.u.slb_error.slb_error_type]
@@ -341,7 +340,7 @@ int opal_machine_check(struct pt_regs *regs)
printk("%s Effective address: %016llx\n",
level, evt.u.slb_error.effective_address);
break;
- case OpalMCE_ERROR_TYPE_ERAT:
+ case MCE_ERROR_TYPE_ERAT:
subtype = evt.u.erat_error.erat_error_type <
ARRAY_SIZE(opal_mc_erat_types) ?
opal_mc_erat_types[evt.u.erat_error.erat_error_type]
@@ -351,7 +350,7 @@ int opal_machine_check(struct pt_regs *regs)
printk("%s Effective address: %016llx\n",
level, evt.u.erat_error.effective_address);
break;
- case OpalMCE_ERROR_TYPE_TLB:
+ case MCE_ERROR_TYPE_TLB:
subtype = evt.u.tlb_error.tlb_error_type <
ARRAY_SIZE(opal_mc_tlb_types) ?
opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type]
@@ -362,11 +361,11 @@ int opal_machine_check(struct pt_regs *regs)
level, evt.u.tlb_error.effective_address);
break;
default:
- case OpalMCE_ERROR_TYPE_UNKNOWN:
+ case MCE_ERROR_TYPE_UNKNOWN:
printk("%s Error type: Unknown\n", level);
break;
}
- return evt.severity == OpalMCE_SEV_FATAL ? 0 : 1;
+ return evt.severity == MCE_SEV_FATAL ? 0 : 1;
}
static irqreturn_t opal_interrupt(int irq, void *data)