[Skiboot] [PATCH 9/9] ipmi: improve handling of time errors

Nicholas Piggin npiggin at gmail.com
Mon Mar 31 23:46:35 AEDT 2025


When BMC is in NTP mode, SET_SEL_TIME returns IPMI wrong state errors.

This better tracks and returns IPMI errors from OPAL_RTC_WRITE, which
prevents Linux from continuing to retry this non-transient failure.

Could the BMC be switched to non-NTP mode some time after the OS is up?
The host could see the OPAL_WRONG_STATE return and deal with this if
necessary.

Signed-off-by: Nicholas Piggin <npiggin at gmail.com>
---
 hw/ipmi/ipmi-rtc.c | 117 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 90 insertions(+), 27 deletions(-)

diff --git a/hw/ipmi/ipmi-rtc.c b/hw/ipmi/ipmi-rtc.c
index 52da2946c..3ecd68dce 100644
--- a/hw/ipmi/ipmi-rtc.c
+++ b/hw/ipmi/ipmi-rtc.c
@@ -8,17 +8,29 @@
 #include <stdlib.h>
 #include <string.h>
 #include <ipmi.h>
+#include <lock.h>
 #include <time.h>
 #include <time-utils.h>
 #include <device.h>
 #include <opal.h>
 #include <rtc.h>
 
-static enum {idle, waiting, updated, error} time_status;
+static struct lock time_lock = LOCK_UNLOCKED;
+static enum {
+	idle,
+	waiting,
+	read_updated,
+	write_success,
+	read_error,
+	write_error,
+	write_wrong_state,
+} time_status;
 
 static void get_sel_time_error(struct ipmi_msg *msg)
 {
-	time_status = error;
+	lock(&time_lock);
+	time_status = read_error;
+	unlock(&time_lock);
 	ipmi_free_msg(msg);
 }
 
@@ -31,8 +43,31 @@ static void get_sel_time_complete(struct ipmi_msg *msg)
 	memcpy(&result, msg->data, 4);
 	time = le32_to_cpu(result);
 	gmtime_r(&time, &tm);
+	lock(&time_lock);
 	rtc_cache_update(&tm);
-	time_status = updated;
+	time_status = read_updated;
+	unlock(&time_lock);
+	ipmi_free_msg(msg);
+}
+
+static void set_sel_time_error(struct ipmi_msg *msg)
+{
+	lock(&time_lock);
+	if (msg->cc == IPMI_NOT_IN_MY_STATE_ERR) {
+		/* BMC in NTP mode does not allow this */
+		time_status = write_wrong_state;
+	} else {
+		time_status = write_error;
+	}
+	unlock(&time_lock);
+	ipmi_free_msg(msg);
+}
+
+static void set_sel_time_complete(struct ipmi_msg *msg)
+{
+	lock(&time_lock);
+	time_status = write_success;
+	unlock(&time_lock);
 	ipmi_free_msg(msg);
 }
 
@@ -55,49 +90,52 @@ static int64_t ipmi_set_sel_time(uint32_t _tv)
 	struct ipmi_msg *msg;
 	const le32 tv = cpu_to_le32(_tv);
 
-	msg = ipmi_mkmsg_simple(IPMI_SET_SEL_TIME, (void*)&tv, sizeof(tv));
+	msg = ipmi_mkmsg(IPMI_DEFAULT_INTERFACE, IPMI_SET_SEL_TIME,
+			 set_sel_time_complete, NULL, (void*)&tv, sizeof(tv), 0);
 	if (!msg)
 		return OPAL_HARDWARE;
 
+	msg->error = set_sel_time_error;
+
 	return ipmi_queue_msg(msg);
 }
 
 static int64_t ipmi_opal_rtc_read(__be32 *__ymd, __be64 *__hmsm)
 {
-	int ret = 0;
 	uint32_t ymd;
 	uint64_t hmsm;
 
 	if (!__ymd || !__hmsm)
 		return OPAL_PARAMETER;
 
-	switch(time_status) {
+	lock(&time_lock);
+	switch (time_status) {
 	case idle:
-		if (ipmi_get_sel_time() < 0)
-			return OPAL_HARDWARE;
 		time_status = waiting;
-		ret = OPAL_BUSY_EVENT;
-		break;
-
-	case waiting:
-		ret = OPAL_BUSY_EVENT;
-		break;
-
-	case updated:
+		unlock(&time_lock);
+		return ipmi_get_sel_time();
+	case read_updated:
 		rtc_cache_get_datetime(&ymd, &hmsm);
 		*__ymd = cpu_to_be32(ymd);
 		*__hmsm = cpu_to_be64(hmsm);
 		time_status = idle;
-		ret = OPAL_SUCCESS;
-		break;
-
-	case error:
+		unlock(&time_lock);
+		return OPAL_SUCCESS;
+	case waiting:
+		unlock(&time_lock);
+		return OPAL_BUSY_EVENT;
+	case read_error:
+		time_status = idle;
+		unlock(&time_lock);
+		return OPAL_HARDWARE;
+	default:
+		/* Clear out stale write status */
 		time_status = idle;
-		ret = OPAL_HARDWARE;
-		break;
+		unlock(&time_lock);
+		return OPAL_BUSY;
 	}
 
-	return ret;
+	return OPAL_INTERNAL_ERROR;
 }
 
 static int64_t ipmi_opal_rtc_write(uint32_t year_month_day,
@@ -106,12 +144,37 @@ static int64_t ipmi_opal_rtc_write(uint32_t year_month_day,
 	time_t t;
 	struct tm tm;
 
-	datetime_to_tm(year_month_day, hour_minute_second_millisecond, &tm);
-	t = mktime(&tm);
-	if (ipmi_set_sel_time(t))
+	lock(&time_lock);
+	switch (time_status) {
+	case idle:
+		time_status = waiting;
+		unlock(&time_lock);
+		datetime_to_tm(year_month_day, hour_minute_second_millisecond, &tm);
+		t = mktime(&tm);
+		return ipmi_set_sel_time(t);
+	case write_success:
+		time_status = idle;
+		unlock(&time_lock);
+		return OPAL_SUCCESS;
+	case waiting:
+		unlock(&time_lock);
+		return OPAL_BUSY_EVENT;
+	case write_error:
+		time_status = idle;
+		unlock(&time_lock);
 		return OPAL_HARDWARE;
+	case write_wrong_state:
+		time_status = idle;
+		unlock(&time_lock);
+		return OPAL_WRONG_STATE;
+	default:
+		/* Clear out stale read status */
+		time_status = idle;
+		unlock(&time_lock);
+		return OPAL_BUSY;
+	}
 
-	return OPAL_SUCCESS;
+	return OPAL_INTERNAL_ERROR;
 }
 
 void ipmi_rtc_init(void)
-- 
2.47.1



More information about the Skiboot mailing list