From 819a4145a85f05c465899d5777d8f94dc3619052 Mon Sep 17 00:00:00 2001 From: Christophe Lombard Date: Tue, 29 Aug 2023 11:23:26 +0200 Subject: core/pldm: Implement PLDM requester Implement a way for sending PLDM requests for specific PLDM commands. Send a PLDM request message. Wait for corresponding response message, which once received, is returned to the caller. If there's data available, return success only if data is a PLDM response message that matches instance, pldm_type and command code. Reviewed-by: Abhishek Singh Tomar Signed-off-by: Christophe Lombard Signed-off-by: Reza Arbab --- core/pldm/Makefile.inc | 2 +- core/pldm/pldm-mctp.c | 7 +- core/pldm/pldm-requester.c | 335 +++++++++++++++++++++++++++++++++++++++++++++ core/pldm/pldm.h | 9 ++ 4 files changed, 351 insertions(+), 2 deletions(-) create mode 100644 core/pldm/pldm-requester.c diff --git a/core/pldm/Makefile.inc b/core/pldm/Makefile.inc index 9aa72a7..f2328a4 100644 --- a/core/pldm/Makefile.inc +++ b/core/pldm/Makefile.inc @@ -7,7 +7,7 @@ SUBDIRS += $(PLDM_DIR) CPPFLAGS += -I$(SRC)/pldm/include/ CPPFLAGS += -I$(SRC)/pldm/include/libpldm/oem/ibm/ -PLDM_OBJS = pldm-mctp.o pldm-responder.o +PLDM_OBJS = pldm-mctp.o pldm-responder.o pldm-requester.o PLDM = $(PLDM_DIR)/built-in.a $(PLDM): $(PLDM_OBJS:%=$(PLDM_DIR)/%) diff --git a/core/pldm/pldm-mctp.c b/core/pldm/pldm-mctp.c index 0e6a7e3..569fe25 100644 --- a/core/pldm/pldm-mctp.c +++ b/core/pldm/pldm-mctp.c @@ -54,6 +54,9 @@ int pldm_mctp_message_rx(uint8_t eid, bool tag_owner, uint8_t msg_tag, } switch (rx->hdrinf.msg_type) { + case PLDM_RESPONSE: + rc = pldm_requester_handle_response(rx); + break; case PLDM_REQUEST: rc = pldm_responder_handle_request(rx); break; @@ -71,16 +74,18 @@ out: int pldm_mctp_init(void) { - int nbr_elt = 2, rc = OPAL_SUCCESS; + int nbr_elt = 3, rc = OPAL_SUCCESS; int (*pldm_config[])(void) = { ast_mctp_init, /* MCTP Binding */ pldm_responder_init, /* Register mandatory commands we'll respond to */ + pldm_requester_init, /* Requester implementation */ }; const char *pldm_config_error[] = { "Failed to bind MCTP", "Failed to register mandatory commands", + "Failed to configure requister", }; prlog(PR_NOTICE, "%s - Getting PLDM data\n", __func__); diff --git a/core/pldm/pldm-requester.c b/core/pldm/pldm-requester.c new file mode 100644 index 0000000..9830cda --- /dev/null +++ b/core/pldm/pldm-requester.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +// Copyright 2022 IBM Corp. + +#define pr_fmt(fmt) "PLDM: " fmt + +#include +#include +#include +#include +#include +#include +#include "pldm.h" + +#define TIMEOUT_MS 8000 + +struct pldm_request { + struct list_node link; + + /* originating request params */ + struct pldm_header_info hdrinf; + + /* messages requested */ + struct pldm_tx_data *tx; + + /* timeout handling */ + struct timer timeout; + uint64_t timeout_ms; + uint64_t start_time; + + /* completion callback */ + void (*complete)(struct pldm_rx_data *rx, void *data); + void *complete_data; +}; + +struct pldm_response { + void **msg; + size_t *msg_size; + bool done; + int rc; +}; + +/* pldm requests queue */ +static struct lock pldm_requests_lock = LOCK_UNLOCKED; +static LIST_HEAD(list_pldm_requests); + +static struct pldm_request *active_request; + +static bool matches_request(const struct pldm_rx_data *rx, + const struct pldm_request *req) +{ + if (req->hdrinf.instance != rx->hdrinf.instance) + return false; + if (req->hdrinf.pldm_type != rx->hdrinf.pldm_type) + return false; + if (req->hdrinf.command != rx->hdrinf.command) + return false; + + return true; +} + +static void send_and_wait_complete(struct pldm_rx_data *rx, void *data) +{ + struct pldm_response *resp = (struct pldm_response *)data; + int len; + + if (rx != NULL) { + len = rx->msg_len; + *resp->msg_size = len; + *resp->msg = zalloc(len); + memcpy(*resp->msg, rx->msg, len); + + resp->rc = OPAL_SUCCESS; + } else { + *resp->msg_size = 0; + *resp->msg = NULL; + resp->rc = OPAL_TIMEOUT; + } + + resp->done = true; +} + +static void handle_response(struct pldm_rx_data *rx) +{ + uint64_t now; + + if (active_request == NULL) { + prlog(PR_ERR, "%s: No active request\n", __func__); + return; + } + + /* unactivate the timer */ + if (rx != NULL) + cancel_timer(&active_request->timeout); + + if (active_request->complete) + active_request->complete(rx, active_request->complete_data); + + now = mftb(); + prlog(PR_TRACE, "%s: Finished after %ldms, t:%d c:%d i:%d\n", + __func__, + tb_to_msecs(now - active_request->start_time), + active_request->hdrinf.pldm_type, + active_request->hdrinf.command, + active_request->hdrinf.instance); + + free(active_request->tx); + free(active_request); + active_request = NULL; +} + +/* + * Timeout :( + */ +static void expiry(struct timer *t __unused, void *data, uint64_t now __unused) +{ + struct pldm_request *req = (struct pldm_request *)data; + + if (active_request == NULL) { + prlog(PR_ERR, "request timedout! (active request NULL)\n"); + return; + } + + prlog(PR_ERR, "PLDM: request timedout! (active request: t:0x%x c:0x%x i:%d)\n", + active_request->hdrinf.pldm_type, + active_request->hdrinf.command, + active_request->hdrinf.instance); + + prlog(PR_ERR, "PLDM: Original request t:0x%x c:0x%x i:%d -----\n", + req->hdrinf.pldm_type, + req->hdrinf.command, req->hdrinf.instance); + + /* no data received. Finish the procedure */ + handle_response(NULL); +} + +/* + * Handle PLDM message received from the PLDM terminus over MCTP + */ +int pldm_requester_handle_response(struct pldm_rx_data *rx) +{ + /* check the message received */ + if (active_request == NULL) { + prlog(PR_ERR, "%s: No active request. " + "Response received t:%d c:%d i:%d\n", + __func__, + rx->hdrinf.pldm_type, + rx->hdrinf.command, + rx->hdrinf.instance); + return OPAL_WRONG_STATE; + } + + if (!matches_request(rx, active_request)) { + prlog(PR_ERR, "%s: Unexpected response! t:%d c:%d i:%d want %d,%d,%d\n", + __func__, + rx->hdrinf.pldm_type, + rx->hdrinf.command, + rx->hdrinf.instance, + active_request->hdrinf.pldm_type, + active_request->hdrinf.command, + active_request->hdrinf.instance); + return OPAL_WRONG_STATE; + } + + /* The expected message seems correct */ + handle_response(rx); + + return OPAL_SUCCESS; +} + +/* + * Send the PLDM request + */ +static void requests_poller(void *data __unused) +{ + int rc = OPAL_SUCCESS; + + lock(&pldm_requests_lock); + + /* wait for the end of the processing of the current request */ + if (active_request) { + unlock(&pldm_requests_lock); + return; + } + + /* no new request to handle */ + if (list_empty(&list_pldm_requests)) { + unlock(&pldm_requests_lock); + return; + } + + /* remove the first entry in a list */ + active_request = list_pop(&list_pldm_requests, + struct pldm_request, + link); + + unlock(&pldm_requests_lock); + + /* Start timer to control a timeout from the PLDM terminus */ + init_timer(&active_request->timeout, expiry, active_request); + schedule_timer(&active_request->timeout, + msecs_to_tb(active_request->timeout_ms)); + active_request->start_time = mftb(); + + /* Send PLDM message over MCTP */ + prlog(PR_TRACE, "%s: Sending request to BMC t:%d c:%d i:%d -----\n", + __func__, + active_request->hdrinf.pldm_type, + active_request->hdrinf.command, + active_request->hdrinf.instance); + + rc = pldm_mctp_message_tx(active_request->tx); + if (rc) + prlog(PR_ERR, "%s: Error %d while sending request\n", + __func__, rc); +} + +/* + * Add PLDM request in the queue + */ +static int queue_request(struct pldm_tx_data *tx, + uint64_t timeout_ms, + void (*complete)(struct pldm_rx_data *rx, void *data), + void *complete_data) +{ + struct pldm_request *pending; + struct pldm_msg *pldm_msg; + size_t tx_size; + + tx_size = sizeof(struct pldm_tx_data) + tx->data_size; + + pending = zalloc(sizeof(struct pldm_request)); + if (!pending) { + prlog(PR_ERR, "%s: failed to allocate request\n", __func__); + return OPAL_NO_MEM; + } + + pending->timeout_ms = timeout_ms; + pending->complete = complete; + pending->complete_data = complete_data; + pending->tx = zalloc(tx_size); + if (!pending->tx) { + free(pending); + prlog(PR_ERR, "%s: failed to allocate pldm packet (size: 0x%lx)\n", + __func__, tx_size); + return OPAL_NO_MEM; + } + + memcpy(pending->tx, tx, tx_size); + + pldm_msg = (struct pldm_msg *)tx->data; + if (unpack_pldm_header(&pldm_msg->hdr, &pending->hdrinf)) { + free(pending->tx); + free(pending); + prlog(PR_ERR, "%s: error parsing pldm header\n", __func__); + return OPAL_PARAMETER; + } + + /* add an entry at the end of a linked list */ + prlog(PR_TRACE, "%s: Add request t:%d c:%d i:%d -----\n", + __func__, + pending->hdrinf.pldm_type, + pending->hdrinf.command, + pending->hdrinf.instance); + + lock(&pldm_requests_lock); + list_add_tail(&list_pldm_requests, &pending->link); + unlock(&pldm_requests_lock); + + return OPAL_SUCCESS; +} + +/* + * Queue a PLDM request and don't wait. + * When a response is received, call the associated callback. + */ +int pldm_requester_queue(struct pldm_tx_data *tx, + void (*complete)(struct pldm_rx_data *rx, void *data), + void *complete_data) +{ + int rc = OPAL_SUCCESS; + + /* Queue PLDM request */ + rc = queue_request(tx, TIMEOUT_MS, complete, complete_data); + if (rc) { + prlog(PR_ERR, "%s: error %d while queuing request\n", + __func__, rc); + return rc; + } + + return rc; +} + +/* + * Queue a PLDM request and spin until we get a response. + */ +int pldm_requester_queue_and_wait(struct pldm_tx_data *tx, + void **msg, size_t *msg_size) +{ + struct pldm_response *resp; + int rc = OPAL_SUCCESS; + + resp = zalloc(sizeof(struct pldm_response)); + if (!resp) { + prlog(PR_ERR, "%s: failed to allocate response\n", __func__); + return OPAL_NO_MEM; + } + + resp->msg = msg; + resp->msg_size = msg_size; + + rc = pldm_requester_queue(tx, send_and_wait_complete, resp); + if (rc) + goto out; + + /* wait for a response from the BMC */ + for (;;) { + if (resp->done) + break; + + time_wait_ms(5); + } + rc = resp->rc; + +out: + free(resp); + return rc; +} + +int pldm_requester_init(void) +{ + /* requests poller */ + opal_add_poller(requests_poller, NULL); + + return OPAL_SUCCESS; +} diff --git a/core/pldm/pldm.h b/core/pldm/pldm.h index d6eda42..7c7da9f 100644 --- a/core/pldm/pldm.h +++ b/core/pldm/pldm.h @@ -47,4 +47,13 @@ int pldm_mctp_message_rx(uint8_t eid, bool tag_owner, uint8_t msg_tag, int pldm_responder_handle_request(struct pldm_rx_data *rx); int pldm_responder_init(void); +/* Requester support */ +int pldm_requester_handle_response(struct pldm_rx_data *rx); +int pldm_requester_queue(struct pldm_tx_data *tx, + void (*complete)(struct pldm_rx_data *rx, void *data), + void *complete_data); +int pldm_requester_queue_and_wait(struct pldm_tx_data *tx, + void **msg, size_t *msg_size); +int pldm_requester_init(void); + #endif /* __COREPLDM_H__ */ -- cgit v1.1