diff options
author | William A. Kennington III <wak@google.com> | 2018-05-23 17:13:35 -0700 |
---|---|---|
committer | Stewart Smith <stewart@linux.ibm.com> | 2018-06-04 00:02:26 -0500 |
commit | e6e74c53ed64eb029cb669fbb6715ee4077cf0b2 (patch) | |
tree | 7f4db5a5d6d28072736dde70e2a2cfde6ec90ce3 /hw/ipmi | |
parent | 2d2916409639507c0357a9fe22493e1379eb1115 (diff) | |
download | skiboot-e6e74c53ed64eb029cb669fbb6715ee4077cf0b2.zip skiboot-e6e74c53ed64eb029cb669fbb6715ee4077cf0b2.tar.gz skiboot-e6e74c53ed64eb029cb669fbb6715ee4077cf0b2.tar.bz2 |
ipmi-watchdog: Support handling re-initialization
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@google.com>
Signed-off-by: Stewart Smith <stewart@linux.ibm.com>
Diffstat (limited to 'hw/ipmi')
-rw-r--r-- | hw/ipmi/ipmi-watchdog.c | 47 |
1 files changed, 42 insertions, 5 deletions
diff --git a/hw/ipmi/ipmi-watchdog.c b/hw/ipmi/ipmi-watchdog.c index 0de5877..ce97f61 100644 --- a/hw/ipmi/ipmi-watchdog.c +++ b/hw/ipmi/ipmi-watchdog.c @@ -33,8 +33,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 @@ -51,14 +56,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); } @@ -72,6 +86,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) { @@ -93,7 +113,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. */ @@ -113,6 +147,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; } @@ -125,13 +160,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) |