diff mbox

[v2,2/3] opal: Check Core FIRs to find the reason for Malfunction Alert.

Message ID 20150205055605.2171.8523.stgit@mars
State Changes Requested
Headers show

Commit Message

Mahesh J Salgaonkar Feb. 5, 2015, 5:57 a.m. UTC
From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>

On Malfunction Alert read all CORE FIRs to identify reason for
core check stop and update a HMI event with relevant error information.

This patch changes the HMI event version to 2 (OpalHMIEvt_V2).

Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
---
 core/hmi.c |  123 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 120 insertions(+), 3 deletions(-)

Comments

Stewart Smith Feb. 12, 2015, 5:54 a.m. UTC | #1
Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com> writes:
> @@ -337,7 +454,7 @@ static int64_t opal_handle_hmi(void)
>  	struct OpalHMIEvent hmi_evt;
>  
>  	memset(&hmi_evt, 0, sizeof(struct OpalHMIEvent));
> -	hmi_evt.version = OpalHMIEvt_V1;
> +	hmi_evt.version = OpalHMIEvt_V2;
>  
>  	lock(&hmi_lock);
>  	hmer = mfspr(SPR_HMER);		/* Get HMER register value */

It would be better if you only sent v2 in the event of something not
supported by V1.

Please also document in doc/opal-api
Mahesh J Salgaonkar Feb. 12, 2015, 9:08 a.m. UTC | #2
On 02/12/2015 11:24 AM, Stewart Smith wrote:
> Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com> writes:
>> @@ -337,7 +454,7 @@ static int64_t opal_handle_hmi(void)
>>  	struct OpalHMIEvent hmi_evt;
>>  
>>  	memset(&hmi_evt, 0, sizeof(struct OpalHMIEvent));
>> -	hmi_evt.version = OpalHMIEvt_V1;
>> +	hmi_evt.version = OpalHMIEvt_V2;
>>  
>>  	lock(&hmi_lock);
>>  	hmer = mfspr(SPR_HMER);		/* Get HMER register value */
> 
> It would be better if you only sent v2 in the event of something not
> supported by V1.

Wouldn't that make code bit messy ? Also, would it be correct for OPAL
layer to send two different versions of HMI event depending on what it
contains ? V2 is a super set of v1, and can contain data pertaining to
v1 and v2 both. V2 is backward compatible. Older Linux kernel receiving
V2 event (containing only v1 data) can still print all the info that
pertains to V1.

Thanks,
-Mahesh.
Stewart Smith Feb. 16, 2015, 4:14 a.m. UTC | #3
Mahesh Jagannath Salgaonkar <mahesh@linux.vnet.ibm.com> writes:
> On 02/12/2015 11:24 AM, Stewart Smith wrote:
>> Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com> writes:
>>> @@ -337,7 +454,7 @@ static int64_t opal_handle_hmi(void)
>>>  	struct OpalHMIEvent hmi_evt;
>>>  
>>>  	memset(&hmi_evt, 0, sizeof(struct OpalHMIEvent));
>>> -	hmi_evt.version = OpalHMIEvt_V1;
>>> +	hmi_evt.version = OpalHMIEvt_V2;
>>>  
>>>  	lock(&hmi_lock);
>>>  	hmer = mfspr(SPR_HMER);		/* Get HMER register value */
>> 
>> It would be better if you only sent v2 in the event of something not
>> supported by V1.
>
> Wouldn't that make code bit messy ? Also, would it be correct for OPAL
> layer to send two different versions of HMI event depending on what it
> contains ? V2 is a super set of v1, and can contain data pertaining to
> v1 and v2 both. V2 is backward compatible. Older Linux kernel receiving
> V2 event (containing only v1 data) can still print all the info that
> pertains to V1.

Okay - so we should *really* document this pretty carefully (in
doc/opal-api/opal-messages.txt ).

I've gone and filled out some of the basics of the forward/backwards
compatibility with struct opal_msg in doc/opal-api/opal-messages.txt,
I'd love it if you could complete the documentation on the HMI message.

I'd love to see BUILD_ASSERT(sizeof(struct opal_msg) >= sizeof(struct
OpalHMIEvent)) somewhere in the code too. I can imagine this getting
hairy to ensure at some point in the future.

Currently it seems that:
1) version cannot be < 1 (linux will bail out). So a version of 0 is
   the way to make a backwards-incompatible change. This should be
   documented.
2) All future versions of OPAL_MSG_HMI_EVT are backwards compatible with
   v1. It'd be great to explicitly document this somewhere.

   Looking carefully at the code, it's "safe" to read the extra words
   out of struct opal_msg as OPAL message is always at least this big.
   (the case for new kernel with a bigger struct OpalHMIEvent than the
   event it receives from an old OPAL)

   IF you decide to have all future versions compatible with v2 as well,
   then please also explicitly document this.


My initial concern with saying we should emit v1 where possible was
around exactly how everything fits together with OPAL_GET_MSG, and
after looking at things fairly closely and documenting some of it
(again, *please* expand the OpalHMIEvent docs) it looks like we'd be
safe - but we should really explicitly work out if being backwards
compatible with v2 as well as v1 *forever* is something we want to
do. I'm pretty sure it's okay - thoughts?

Please also document that when adding a struct to the union, the
version of HMIEvent *MUST* be bumped.
diff mbox

Patch

diff --git a/core/hmi.c b/core/hmi.c
index f21ca50..2a21fee 100644
--- a/core/hmi.c
+++ b/core/hmi.c
@@ -22,6 +22,8 @@ 
 #include <xscom.h>
 #include <capp.h>
 #include <pci.h>
+#include <cpu.h>
+#include <chip.h>
 
 /*
  * HMER register layout:
@@ -144,6 +146,32 @@ 
  * NOTE: Per Dave Larson, never enable 8,9,21-23
  */
 
+/* xscom addresses for core FIR (Fault Isolation Register) */
+#define CORE_FIR		0x10013100
+
+static const struct core_xstop_bit_info {
+	uint8_t bit;		/* CORE FIR bit number */
+	enum OpalHMI_CoreXstopReason reason;
+} xstop_bits[] = {
+	{ 3, CORE_CHECKSTOP_IFU_REGFILE },
+	{ 5, CORE_CHECKSTOP_IFU_LOGIC },
+	{ 8, CORE_CHECKSTOP_PC_DURING_RECOV },
+	{ 10, CORE_CHECKSTOP_ISU_REGFILE },
+	{ 12, CORE_CHECKSTOP_ISU_LOGIC },
+	{ 21, CORE_CHECKSTOP_FXU_LOGIC },
+	{ 25, CORE_CHECKSTOP_VSU_LOGIC },
+	{ 26, CORE_CHECKSTOP_PC_RECOV_IN_MAINT_MODE },
+	{ 32, CORE_CHECKSTOP_LSU_REGFILE },
+	{ 36, CORE_CHECKSTOP_PC_FWD_PROGRESS },
+	{ 38, CORE_CHECKSTOP_LSU_LOGIC },
+	{ 45, CORE_CHECKSTOP_PC_LOGIC },
+	{ 48, CORE_CHECKSTOP_PC_HYP_RESOURCE },
+	{ 52, CORE_CHECKSTOP_PC_HANG_RECOV_FAILED },
+	{ 54, CORE_CHECKSTOP_PC_AMBI_HANG_DETECTED },
+	{ 60, CORE_CHECKSTOP_PC_DEBUG_TRIG_ERR_INJ },
+	{ 63, CORE_CHECKSTOP_PC_SPRD_HYP_ERR_INJ },
+};
+
 static struct lock hmi_lock = LOCK_UNLOCKED;
 
 static int queue_hmi_event(struct OpalHMIEvent *hmi_evt, int recover)
@@ -161,7 +189,7 @@  static int queue_hmi_event(struct OpalHMIEvent *hmi_evt, int recover)
 		hmi_evt->disposition = OpalHMI_DISPOSITION_NOT_RECOVERED;
 
 	/*
-	 * struct OpalHMIEvent is of (3 * 64 bits) size and well packed
+	 * V2 of struct OpalHMIEvent is of (4 * 64 bits) size and well packed
 	 * structure. Hence use uint64_t pointer to pass entire structure
 	 * using 4 params in generic message format.
 	 */
@@ -169,7 +197,8 @@  static int queue_hmi_event(struct OpalHMIEvent *hmi_evt, int recover)
 
 	/* queue up for delivery to host. */
 	return opal_queue_msg(OPAL_MSG_HMI_EVT, NULL, NULL,
-				hmi_data[0], hmi_data[1], hmi_data[2]);
+				hmi_data[0], hmi_data[1], hmi_data[2],
+				hmi_data[3]);
 }
 
 static int is_capp_recoverable(int chip_id)
@@ -220,11 +249,81 @@  static int decode_one_malfunction(int flat_chip_id, struct OpalHMIEvent *hmi_evt
 	return 0;
 }
 
+static bool decode_core_fir(struct cpu_thread *cpu,
+				struct OpalHMIEvent *hmi_evt)
+{
+	uint64_t core_fir;
+	uint32_t core_id;
+	int i;
+	bool found = false;
+
+	/* Sanity check */
+	if (!cpu || !hmi_evt)
+		return false;
+
+	core_id = pir_to_core_id(cpu->pir);
+
+	/* Get CORE FIR register value. */
+	if (xscom_read(cpu->chip_id, XSCOM_ADDR_P8_EX(core_id, CORE_FIR),
+							&core_fir) != 0) {
+		prerror("HMI: XSCOM error reading CORE FIR\n");
+		return false;
+	}
+
+	prlog(PR_INFO, "HMI: CHIP ID: %x, CORE ID: %x, FIR: %016llx\n",
+			cpu->chip_id, core_id, core_fir);
+
+	/* Check CORE FIR bits and populate HMI event with error info. */
+	for (i = 0; i < ARRAY_SIZE(xstop_bits); i++) {
+		if (core_fir & PPC_BIT(xstop_bits[i].bit)) {
+			found = true;
+			hmi_evt->u.xstop_error.xstop_reason
+						|= xstop_bits[i].reason;
+		}
+	}
+	return found;
+}
+
+static void find_core_checkstop_reason(struct OpalHMIEvent *hmi_evt,
+					int *event_generated)
+{
+	struct cpu_thread *cpu;
+
+	/* Initialize HMI event */
+	hmi_evt->severity = OpalHMI_SEV_FATAL;
+	hmi_evt->type = OpalHMI_ERROR_MALFUNC_ALERT;
+	hmi_evt->u.xstop_error.xstop_type = CHECKSTOP_TYPE_CORE;
+
+	/*
+	 * Check CORE FIRs and find the reason for core checkstop.
+	 * Send a separate HMI event for each core that has checkstopped.
+	 */
+	for_each_cpu(cpu) {
+		/* GARDed CPUs are marked unavailable. Skip them.  */
+		if (cpu->state == cpu_state_unavailable)
+			continue;
+
+		/* Only check on primaries (ie. core), not threads */
+		if (cpu->is_secondary)
+			continue;
+
+		/* Initialize xstop_error fields. */
+		hmi_evt->u.xstop_error.xstop_reason = 0;
+		hmi_evt->u.xstop_error.u.pir = cpu->pir;
+
+		if (decode_core_fir(cpu, hmi_evt)) {
+			queue_hmi_event(hmi_evt, 0);
+			*event_generated = 1;
+		}
+	}
+}
+
 static int decode_malfunction(struct OpalHMIEvent *hmi_evt)
 {
 	int i;
 	int recover = -1;
 	uint64_t malf_alert;
+	int event_generated = 0;
 
 	xscom_read(this_cpu()->chip_id, 0x2020011, &malf_alert);
 
@@ -232,8 +331,26 @@  static int decode_malfunction(struct OpalHMIEvent *hmi_evt)
 		if (malf_alert & PPC_BIT(i)) {
 			recover = decode_one_malfunction(i, hmi_evt);
 			xscom_write(this_cpu()->chip_id, 0x02020011, ~PPC_BIT(i));
+			if (recover) {
+				queue_hmi_event(hmi_evt, recover);
+				event_generated = 1;
+			}
 		}
 
+	if (recover != -1) {
+		find_core_checkstop_reason(hmi_evt, &event_generated);
+
+		/*
+		 * In case, if we fail to find checkstop reason send an
+		 * unknown HMI event.
+		 */
+		if (!event_generated) {
+			hmi_evt->u.xstop_error.xstop_type =
+						CHECKSTOP_TYPE_UNKNOWN;
+			hmi_evt->u.xstop_error.xstop_reason = 0;
+		}
+	}
+
 	return recover;
 }
 
@@ -337,7 +454,7 @@  static int64_t opal_handle_hmi(void)
 	struct OpalHMIEvent hmi_evt;
 
 	memset(&hmi_evt, 0, sizeof(struct OpalHMIEvent));
-	hmi_evt.version = OpalHMIEvt_V1;
+	hmi_evt.version = OpalHMIEvt_V2;
 
 	lock(&hmi_lock);
 	hmer = mfspr(SPR_HMER);		/* Get HMER register value */