[Skiboot] [PATCH 9/9] ipmi-watchdog: Support handling re-initialization

William A. Kennington III wak at google.com
Thu May 24 10:13:35 AEST 2018


Watchdog resets can return an error code from the BMC indicating that
the BMC watchdog was not initialized. Currently we abort skiboot due to
a missing error handler. This patch implements handling
re-initialization for the watchdog, automatically saving the last
watchdog set values and re-issuing them if needed.

Signed-off-by: William A. Kennington III <wak at google.com>
---
 hw/ipmi/ipmi-watchdog.c | 47 ++++++++++++++++++++++++++++++++++++-----
 1 file changed, 42 insertions(+), 5 deletions(-)

diff --git a/hw/ipmi/ipmi-watchdog.c b/hw/ipmi/ipmi-watchdog.c
index 1472a8ea1..105270f5e 100644
--- a/hw/ipmi/ipmi-watchdog.c
+++ b/hw/ipmi/ipmi-watchdog.c
@@ -34,8 +34,13 @@
 #define WDT_RESET_ACTION 	0x01
 #define WDT_NO_ACTION		0x00
 
+/* IPMI defined custom completion codes for the watchdog */
+#define WDT_CC_OK		0x00
+#define WDT_CC_NOT_INITIALIZED	0x80
+
 /* Flags used for IPMI callbacks */
 #define WDT_SET_DO_RESET	0x01
+#define WDT_RESET_NO_REINIT	0x01
 
 /* How long to set the overall watchdog timeout for. In units of
  * 100ms. If the timer is not reset within this time the watchdog
@@ -52,14 +57,23 @@ static struct timer wdt_timer;
 static bool wdt_stopped;
 static bool wdt_ticking;
 
+/* Saved values from the last watchdog set action */
+static uint8_t last_action;
+static uint16_t last_count;
+static uint8_t last_pretimeout;
+
 static void reset_wdt(struct timer *t, void *data, uint64_t now);
 
 static void set_wdt_complete(struct ipmi_msg *msg)
 {
 	const uintptr_t flags = (uintptr_t)msg->user_data;
 
-	if (flags & WDT_SET_DO_RESET)
-		reset_wdt(NULL, NULL, 0);
+	if (flags & WDT_SET_DO_RESET) {
+		/* Make sure the reset action does not create a loop and
+		 * perform a reset in the case where the BMC send an
+		 * uninitialized error. */
+		reset_wdt(NULL, (void *)WDT_RESET_NO_REINIT, 0);
+	}
 
 	ipmi_free_msg(msg);
 }
@@ -73,6 +87,12 @@ static void set_wdt(uint8_t action, uint16_t count, uint8_t pretimeout,
 	if (do_reset)
 		completion_flags |= WDT_SET_DO_RESET;
 
+	/* Save the values prior to issuing the set operation so that we can
+	 * re-initialize the watchdog in error cases. */
+	last_action = action;
+	last_count = count;
+	last_pretimeout = pretimeout;
+
 	ipmi_msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_SET_WDT,
 			      set_wdt_complete, NULL, NULL, 6, 0);
 	if (!ipmi_msg) {
@@ -94,7 +114,21 @@ static void set_wdt(uint8_t action, uint16_t count, uint8_t pretimeout,
 
 static void reset_wdt_complete(struct ipmi_msg *msg)
 {
-	const uint64_t reset_delay_ms = (WDT_TIMEOUT - WDT_MARGIN) * 100;
+	const uintptr_t flags = (uintptr_t)msg->user_data;
+	uint64_t reset_delay_ms = (WDT_TIMEOUT - WDT_MARGIN) * 100;
+
+	if (msg->cc == WDT_CC_NOT_INITIALIZED &&
+			!(flags & WDT_RESET_NO_REINIT)) {
+		/* If our timer was not initialized on the BMC side, we should
+		 * perform a single attempt to set it up again. */
+		set_wdt(last_action, last_count, last_pretimeout, true, true);
+	} else if (msg->cc != WDT_CC_OK) {
+		/* Use a short (10s) timeout before performing the next reset
+		 * if we encounter an unknown error. This makes sure that we
+		 * are able to reset and re-initialize the timer since it might
+		 * expire. */
+		reset_delay_ms = 10 * 1000;
+	}
 
 	/* If we are inside of skiboot we need to periodically restart the
 	 * timer. Reschedule a reset so it happens before the timeout. */
@@ -114,6 +148,7 @@ static struct ipmi_msg *wdt_reset_mkmsg(void)
 		prerror("Unable to allocate reset wdt message\n");
 		return NULL;
 	}
+	ipmi_msg->error = reset_wdt_complete;
 
 	return ipmi_msg;
 }
@@ -126,13 +161,15 @@ static void sync_reset_wdt(void)
 		ipmi_queue_msg_sync(ipmi_msg);
 }
 
-static void reset_wdt(struct timer *t __unused, void *data __unused,
+static void reset_wdt(struct timer *t __unused, void *data,
 		      uint64_t now __unused)
 {
 	struct ipmi_msg *ipmi_msg;
 
-	if ((ipmi_msg = wdt_reset_mkmsg()))
+	if ((ipmi_msg = wdt_reset_mkmsg())) {
+		ipmi_msg->user_data = data;
 		ipmi_queue_msg_head(ipmi_msg);
+	}
 }
 
 void ipmi_wdt_stop(void)
-- 
2.17.0.441.gb46fe60e1d-goog



More information about the Skiboot mailing list