[Skiboot] [PATCH V2 01/15] core/pldm: Handle Watchdog timer.
Frederic Barrat
fbarrat at linux.ibm.com
Wed Apr 26 22:23:34 AEST 2023
On 29/04/2022 11:47, Christophe Lombard wrote:
> Encode a PLDM platform event message to send the heartbeat to the BMC.
> Watchdog is "armed" when a
> PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE is received.
>
> Signed-off-by: Christophe Lombard <clombard at linux.vnet.ibm.com>
> ---
> core/pldm/Makefile.inc | 2 +
> core/pldm/pldm-watchdog.c | 127 ++++++++++++++++++++++++++++++++++++++
> core/pldm/pldm.h | 1 +
> include/pldm.h | 5 ++
> 4 files changed, 135 insertions(+)
> create mode 100644 core/pldm/pldm-watchdog.c
>
> diff --git a/core/pldm/Makefile.inc b/core/pldm/Makefile.inc
> index 506bf5d7..c6f78822 100644
> --- a/core/pldm/Makefile.inc
> +++ b/core/pldm/Makefile.inc
> @@ -9,11 +9,13 @@ CPPFLAGS += -I$(SRC)/pldm/ibm/libpldm/
>
> CFLAGS_$(PLDM_DIR)/pldm-platform-requests.o = -Wno-strict-prototypes
> CFLAGS_$(PLDM_DIR)/pldm-bios-requests.o = -Wno-strict-prototypes
> +CFLAGS_$(PLDM_DIR)/pldm-watchdog.o = -Wno-strict-prototypes
I know you're going to update to latest libpldm, but it seems that
-Wno-strict-prototypes is no longer going to be needed (they fixed the
prototypes with no arguments). Might also be true for the other
file-specific CFLAGS, this is just a reminder that we should check it.
>
> PLDM_OBJS = pldm-common.o pldm-responder.o pldm-requester.o
> PLDM_OBJS += pldm-base-requests.o pldm-platform-requests.o
> PLDM_OBJS += pldm-bios-requests.o pldm-fru-requests.o
> PLDM_OBJS += pldm-file-io-requests.o pldm-lid-files.o
> +PLDM_OBJS += pldm-watchdog.o
>
> PLDM = $(PLDM_DIR)/built-in.a
> $(PLDM): $(PLDM_OBJS:%=$(PLDM_DIR)/%)
> diff --git a/core/pldm/pldm-watchdog.c b/core/pldm/pldm-watchdog.c
> new file mode 100644
> index 00000000..9d2aabe8
> --- /dev/null
> +++ b/core/pldm/pldm-watchdog.c
> @@ -0,0 +1,127 @@
> +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
> +// Copyright 2022 IBM Corp.
> +
> +#define pr_fmt(fmt) "PLDM: " fmt
> +
> +#include <lock.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <opal.h>
> +#include <timebase.h>
> +#include <timer.h>
> +#include <pldm/libpldm/platform.h>
> +#include "pldm.h"
> +
> +#define DEFAULT_WATCHDOG_TIMEOUT_SEC (10 * 60) /* 10 min */
> +
> +/* Whether the watchdog timer is armed and Skiboot should be sending
> + * regular heartbeats.
> + */
> +bool watchdog_armed;
> +
> +/* The period (in seconds) of the PLDM watchdog, as dictated by BMC */
> +int watchdog_period_sec = DEFAULT_WATCHDOG_TIMEOUT_SEC;
> +
> +static struct lock sequence_lock;
> +static uint8_t sequence_number;
> +
> +int pldm_watchdog_reset_timer(void)
> +{
> + uint8_t heartbeat_elapsed_data[2];
> + size_t response_len, payload_len;
> + uint32_t request_length;
> + void *response_msg;
> + char *request_msg;
> + int rc;
> +
> + struct pldm_platform_event_message_req event_message_req = {
> + .format_version = PLDM_PLATFORM_EVENT_MESSAGE_FORMAT_VERSION,
> + .tid = HOST_TID,
> + .event_class = PLDM_HEARTBEAT_TIMER_ELAPSED_EVENT,
> + };
> +
> + struct pldm_platform_event_message_resp response;
> +
> + /* watchdog is not armed, so no need to send the heartbeat */
> + if (!watchdog_armed) {
> + prlog(PR_ERR, "%s - PLDM watchdog is not armed, not sending the heartbeat\n",
> + __func__);
> + return OPAL_PARAMETER;
> + }
> +
> + prlog(PR_INFO, "%s - send the heartbeat to the BMC, sequence: %d, period: %d\n",
> + __func__, sequence_number, watchdog_period_sec);
> +
> + /* Send the event request */
> + heartbeat_elapsed_data[0] = PLDM_PLATFORM_EVENT_MESSAGE_FORMAT_VERSION;
> +
> + /* We need to make sure that we send the BMC the correct
> + * sequence number. To prevent possible race conditions for the
> + * sequence number, lock it while we're incrementing and
> + * sending it down.
> + */
> + lock(&sequence_lock);
> + heartbeat_elapsed_data[1] = sequence_number++;
> +
> + payload_len = PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES + sizeof(heartbeat_elapsed_data);
> +
> + request_length = sizeof(struct pldm_msg_hdr) +
> + sizeof(struct pldm_platform_event_message_req) +
> + sizeof(heartbeat_elapsed_data);
> + request_msg = malloc(request_length);
> +
> + /* Encode the platform event message request */
> + rc = encode_platform_event_message_req(
> + DEFAULT_INSTANCE_ID,
> + event_message_req.format_version,
> + event_message_req.tid,
> + event_message_req.event_class,
> + heartbeat_elapsed_data,
> + sizeof(heartbeat_elapsed_data),
> + (struct pldm_msg *)request_msg,
> + payload_len);
> + if (rc != PLDM_SUCCESS) {
> + prlog(PR_ERR, "Encode PlatformEventMessage Error, rc: %d\n", rc);
> + free(request_msg);
> + sequence_number--;
We don't release the sequence_lock in this error path.
Do we really care about decrementing sequence_number in case of error?
The spec says it allows the BMC to know if it misses one hearbeat. Which
would be the case in case of error, so decrementing is actually
misleading. And it would simplify the code.
> + return OPAL_PARAMETER;
> + }
> + unlock(&sequence_lock);
> +
> + /* Send and get the response message bytes */
> + rc = pldm_do_request(BMC_EID, request_msg, request_length - 1,
> + &response_msg, &response_len);
> + if (rc) {
> + prlog(PR_ERR, "Communication Error, req: PlatformEventMessage, rc: %d\n", rc);
> + free(request_msg);
> + sequence_number--;
sequence_number is decremented with no lock held. But see previous
comment, we may not need to decrement.
> + return rc;
> + }
> + free(request_msg);
> +
> + /* Decode the message */
> + payload_len = response_len - sizeof(struct pldm_msg_hdr);
> + rc = decode_platform_event_message_resp(
> + response_msg,
> + payload_len,
> + &response.completion_code,
> + &response.platform_event_status);
> + if (rc != PLDM_SUCCESS || response.completion_code != PLDM_SUCCESS) {
> + prlog(PR_ERR, "Decode PlatformEventMessage Error, rc: %d, cc: %d, pes: %d\n",
> + rc, response.completion_code,
> + response.platform_event_status);
> + free(response_msg);
> + return OPAL_PARAMETER;
> + }
> +
> + free(response_msg);
> +
So we only send one heartbeat message? Not emitted periodically?
From the spec:
"heartbeatTimer
Amount of time in seconds after *each* elapsing of which the terminus
shall emit a heartbeat event (the heartbeatTimerElapsedEvent) to the
event receiver. If the terminus cannot produce heartbeat events at
the requested rate, it shall return completion code
HEARTBEAT_FREQUENCY_TOO_HIGH."
So it seems to me that once a SetEventReceiver command is received
requesting a heartbeat, we should send one periodically.
> + return OPAL_SUCCESS;
> +}
> +
> +int pldm_watchdog_init(void)
> +{
> + init_lock(&sequence_lock);
pldm_watchdog_reset_timer() is an exported API and a misbehaving caller
might call it and use the lock before it's init. We should use a static
initializer when declaring the global variable:
static struct lock sequence_lock = LOCK_UNLOCKED
> +
> + return pldm_watchdog_reset_timer();
Does it make sense to call pldm_watchdog_reset_timer() during init? Can
watchdog_timer already be set?
Fred
More information about the Skiboot
mailing list