Message ID | 20220913102705.65506-10-clombard@linux.vnet.ibm.com |
---|---|
State | Superseded |
Headers | show |
Series | Implement MCTP and PLDM features | expand |
On Tue, Sep 13, 2022 at 12:26:53PM +0200, Christophe Lombard wrote: > 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. > > Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com> Reviewed-by: Abhishek Singh Tomar <abhishek@linux.ibm.com> > --- > core/pldm/Makefile.inc | 2 +- > core/pldm/pldm-mctp.c | 9 +- > core/pldm/pldm-requester.c | 331 +++++++++++++++++++++++++++++++++++++ > core/pldm/pldm.h | 9 + > 4 files changed, 349 insertions(+), 2 deletions(-) > create mode 100644 core/pldm/pldm-requester.c > > diff --git a/core/pldm/Makefile.inc b/core/pldm/Makefile.inc > index dbca102a..a9b3b7e6 100644 > --- a/core/pldm/Makefile.inc > +++ b/core/pldm/Makefile.inc > @@ -7,7 +7,7 @@ SUBDIRS += $(PLDM_DIR) > CPPFLAGS += -I$(SRC)/pldm/libpldm/ > CPPFLAGS += -I$(SRC)/pldm/ibm/libpldm/ > > -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 51dbad0a..c7d7bb3b 100644 > --- a/core/pldm/pldm-mctp.c > +++ b/core/pldm/pldm-mctp.c > @@ -76,6 +76,8 @@ static int handle_message_rx(uint8_t eid, const uint8_t *buf, int len) > } > > switch (rx.hdrinf.msg_type) { > + case PLDM_RESPONSE: > + return pldm_requester_handle_response(&rx); > case PLDM_REQUEST: > return pldm_responder_handle_request(&rx); > break; > @@ -120,8 +122,13 @@ int pldm_mctp_init(uint8_t mode) > > /* Register mandatory commands we'll respond to */ > rc = pldm_responder_init(); > - if (rc) > + if (rc) { > prlog(PR_ERR, "Failed to register mandatory commands\n"); > + goto out; > + } > + > + /* Requester implementation */ > + pldm_requester_init(); > > out: > prlog(PR_NOTICE, "%s - done, rc: %d\n", __func__, rc); > diff --git a/core/pldm/pldm-requester.c b/core/pldm/pldm-requester.c > new file mode 100644 > index 00000000..412d495a > --- /dev/null > +++ b/core/pldm/pldm-requester.c > @@ -0,0 +1,331 @@ > +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later > +// Copyright 2022 IBM Corp. > + > +#define pr_fmt(fmt) "PLDM: " fmt > + > +#include <cpu.h> > +#include <stdio.h> > +#include <string.h> > +#include <timebase.h> > +#include <ast.h> > +#include <pldm/libpldm/utils.h> > +#include "pldm.h" > + > +struct pldm_request { > + struct list_node link; > + > + /* originating request params */ > + int dest; > + int instance; > + int cmd_code; > + int type; > + > + /* messages requested */ > + void *request_msg; > + int request_len; > + > + /* 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 **response_msg; > + size_t *response_len; > + bool done; > + int rc; > +}; > + > +/* pldm requests queue */ > +static LIST_HEAD(list_pldm_requests); > +struct lock list_lock; > +struct timer requests_timer; > + > +static struct pldm_request *active_request; > + > +static bool matches_request(const struct pldm_rx_data *rx, > + const struct pldm_request *req) > +{ > + if (req->instance != rx->hdrinf.instance) > + return false; > + if (req->type != rx->hdrinf.pldm_type) > + return false; > + if (req->cmd_code != 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->response_len = len; > + *resp->response_msg = zalloc(len); > + memcpy(*resp->response_msg, rx->msg, len); > + > + resp->rc = OPAL_SUCCESS; > + } else { > + *resp->response_len = 0; > + *resp->response_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->type, > + active_request->cmd_code, > + active_request->instance); > + > + free(active_request->request_msg); > + 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, "request timedout! (active request: t:%d c:%d i:%d)\n", > + active_request->type, > + active_request->cmd_code, > + active_request->instance); > + > + prlog(PR_TRACE, "%s: Original request t:%d c:%d i:%d -----\n", > + __func__, req->type, req->cmd_code, req->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->type, > + active_request->cmd_code, > + active_request->instance > + ); > + return OPAL_WRONG_STATE; > + } > + > + /* The expected message seems correct */ > + handle_response(rx); > + > + return OPAL_SUCCESS; > +} > + > +/* > + * Send the PLDM request > + */ > +static void requests_poller(struct timer *t __unused, > + void *data __unused, > + uint64_t now __unused) > +{ > + int rc = OPAL_SUCCESS; > + > + lock(&list_lock); > + > + /* no new request to handle */ > + if (list_empty(&list_pldm_requests)) > + goto out; > + > + /* wait for the end of the processing of the current request */ > + if (active_request) > + goto out; > + > + /* remove the first entry in a list */ > + active_request = list_pop(&list_pldm_requests, > + struct pldm_request, > + link); > + > + /* 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->type, > + active_request->cmd_code, > + active_request->instance); > + > + rc = pldm_mctp_message_tx(active_request->dest, > + active_request->request_msg, > + active_request->request_len); > + if (rc) > + prlog(PR_ERR, "%s: Error %d while sending request\n", > + __func__, rc); > + > +out: > + unlock(&list_lock); > + schedule_timer(&requests_timer, msecs_to_tb(5)); > +} > + > +/* > + * Add PLDM request in the queue > + */ > +static int queue_request(void *request_msg, int request_len, > + uint64_t timeout_ms, > + void (*complete)(struct pldm_rx_data *rx, void *data), > + void *complete_data) > +{ > + struct pldm_header_info hdrinf; > + struct pldm_request *pending; > + > + if (unpack_pldm_header(request_msg, &hdrinf)) { > + prlog(PR_ERR, "%s: error parsing pldm header\n", > + __func__); > + return OPAL_PARAMETER; > + } > + > + pending = zalloc(sizeof(struct pldm_request)); > + > + pending->dest = BMC_EID; > + pending->instance = hdrinf.instance; > + pending->type = hdrinf.pldm_type; > + pending->cmd_code = hdrinf.command; > + pending->timeout_ms = timeout_ms; > + pending->request_msg = zalloc(request_len); > + memcpy(pending->request_msg, request_msg, request_len); > + > + pending->request_len = request_len; > + pending->complete = complete; > + pending->complete_data = complete_data; > + > + /* add an entry at the end of a linked list */ > + lock(&list_lock); > + prlog(PR_TRACE, "%s: Add request t:%d c:%d i:%d -----\n", > + __func__, > + pending->type, > + pending->cmd_code, > + pending->instance); > + list_add_tail(&list_pldm_requests, &pending->link); > + unlock(&list_lock); > + > + return OPAL_SUCCESS; > +} > + > +#define TIMEOUT_MS 8000 > + > +/* > + * Queue a PLDM request and don't wait. > + * When a response is received, call the associated callback. > + */ > +int pldm_requester_queue(void *request_msg, size_t request_len, > + void (*complete)(struct pldm_rx_data *rx, void *data), > + void *complete_data) > +{ > + int rc = OPAL_SUCCESS; > + > + rc = ast_mctp_ready(); > + if (rc) { > + prlog(PR_ERR, "%s: BMC is not listening (rc: %d)\n", > + __func__, rc); > + return rc; > + } > + > + /* Queue PLDM request */ > + rc = queue_request(request_msg, request_len, > + 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(void *request_msg, size_t request_len, > + void **response_msg, size_t *response_len) > +{ > + struct pldm_response resp; > + int rc; > + > + memset(&resp, 0, sizeof(resp)); > + resp.response_msg = response_msg; > + resp.response_len = response_len; > + > + rc = pldm_requester_queue(request_msg, request_len, > + send_and_wait_complete, &resp); > + if (rc) > + return rc; > + > + /* wait for a response from the BMC */ > + do { > + time_wait_ms(5); > + } while (!resp.done); > + > + return resp.rc; > +} > + > +void pldm_requester_init(void) > +{ > + /* lock used to queue and unqueue pldm requests */ > + init_lock(&list_lock); > + > + init_timer(&requests_timer, requests_poller, NULL); > + schedule_timer(&requests_timer, msecs_to_tb(5)); > +} > diff --git a/core/pldm/pldm.h b/core/pldm/pldm.h > index 7df413da..fa7a80de 100644 > --- a/core/pldm/pldm.h > +++ b/core/pldm/pldm.h > @@ -42,4 +42,13 @@ int pldm_mctp_message_tx(uint8_t dest_id, uint8_t *buf, int len); > 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(void *request_msg, size_t request_len, > + void (*complete)(struct pldm_rx_data *rx, void *data), > + void *complete_data); > +int pldm_requester_queue_and_wait(void *request_msg, size_t request_len, > + void **response_msg, size_t *response_len); > +void pldm_requester_init(void); > + > #endif /* __COREPLDM_H__ */ > -- > 2.37.3 > > _______________________________________________ > Skiboot mailing list > Skiboot@lists.ozlabs.org > https://lists.ozlabs.org/listinfo/skiboot
On 13/09/2022 12:26, Christophe Lombard wrote: > diff --git a/core/pldm/pldm-requester.c b/core/pldm/pldm-requester.c > new file mode 100644 > index 00000000..412d495a > --- /dev/null > +++ b/core/pldm/pldm-requester.c > @@ -0,0 +1,331 @@ > +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later > +// Copyright 2022 IBM Corp. > + > +#define pr_fmt(fmt) "PLDM: " fmt > + > +#include <cpu.h> > +#include <stdio.h> > +#include <string.h> > +#include <timebase.h> > +#include <ast.h> > +#include <pldm/libpldm/utils.h> > +#include "pldm.h" > + > +struct pldm_request { > + struct list_node link; > + > + /* originating request params */ > + int dest; > + int instance; > + int cmd_code; > + int type; > + > + /* messages requested */ > + void *request_msg; > + int request_len; > + > + /* 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 **response_msg; > + size_t *response_len; > + bool done; > + int rc; > +}; > + > +/* pldm requests queue */ > +static LIST_HEAD(list_pldm_requests); > +struct lock list_lock; > +struct timer requests_timer; Make those 2 struct static. 'list_lock' is pretty generic. Can we rename or at least have a comment to say what it's protecting? > +/* > + * 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->type, > + active_request->cmd_code, > + active_request->instance > + ); > + return OPAL_WRONG_STATE; > + } > + > + /* The expected message seems correct */ > + handle_response(rx); > + > + return OPAL_SUCCESS; > +} > + > +/* > + * Send the PLDM request > + */ > +static void requests_poller(struct timer *t __unused, > + void *data __unused, > + uint64_t now __unused) > +{ Could probably also be converted to an opal poller and avoid the timer management. > + int rc = OPAL_SUCCESS; > + > + lock(&list_lock); > + > + /* no new request to handle */ > + if (list_empty(&list_pldm_requests)) > + goto out; > + > + /* wait for the end of the processing of the current request */ > + if (active_request) > + goto out; > + > + /* remove the first entry in a list */ > + active_request = list_pop(&list_pldm_requests, > + struct pldm_request, > + link); I think we can shorten the duration of holding the 'list_lock' and release it here. Fred
Le 12/04/2023 à 17:11, Frederic Barrat a écrit : > > > On 13/09/2022 12:26, Christophe Lombard wrote: > >> diff --git a/core/pldm/pldm-requester.c b/core/pldm/pldm-requester.c >> new file mode 100644 >> index 00000000..412d495a >> --- /dev/null >> +++ b/core/pldm/pldm-requester.c >> @@ -0,0 +1,331 @@ >> +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later >> +// Copyright 2022 IBM Corp. >> + >> +#define pr_fmt(fmt) "PLDM: " fmt >> + >> +#include <cpu.h> >> +#include <stdio.h> >> +#include <string.h> >> +#include <timebase.h> >> +#include <ast.h> >> +#include <pldm/libpldm/utils.h> >> +#include "pldm.h" >> + >> +struct pldm_request { >> + struct list_node link; >> + >> + /* originating request params */ >> + int dest; >> + int instance; >> + int cmd_code; >> + int type; >> + >> + /* messages requested */ >> + void *request_msg; >> + int request_len; >> + >> + /* 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 **response_msg; >> + size_t *response_len; >> + bool done; >> + int rc; >> +}; >> + >> +/* pldm requests queue */ >> +static LIST_HEAD(list_pldm_requests); >> +struct lock list_lock; >> +struct timer requests_timer; > > > Make those 2 struct static. > 'list_lock' is pretty generic. Can we rename or at least have a > comment to say what it's protecting? > > ok, will do. Thanks > >> +/* >> + * 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->type, >> + active_request->cmd_code, >> + active_request->instance >> + ); >> + return OPAL_WRONG_STATE; >> + } >> + >> + /* The expected message seems correct */ >> + handle_response(rx); >> + >> + return OPAL_SUCCESS; >> +} >> + >> +/* >> + * Send the PLDM request >> + */ >> +static void requests_poller(struct timer *t __unused, >> + void *data __unused, >> + uint64_t now __unused) >> +{ > > > Could probably also be converted to an opal poller and avoid the timer > management. yep. Thanks for this info > > >> + int rc = OPAL_SUCCESS; >> + >> + lock(&list_lock); >> + >> + /* no new request to handle */ >> + if (list_empty(&list_pldm_requests)) >> + goto out; >> + >> + /* wait for the end of the processing of the current request */ >> + if (active_request) >> + goto out; >> + >> + /* remove the first entry in a list */ >> + active_request = list_pop(&list_pldm_requests, >> + struct pldm_request, >> + link); > > > I think we can shorten the duration of holding the 'list_lock' and > release it here. > > Fred >
diff --git a/core/pldm/Makefile.inc b/core/pldm/Makefile.inc index dbca102a..a9b3b7e6 100644 --- a/core/pldm/Makefile.inc +++ b/core/pldm/Makefile.inc @@ -7,7 +7,7 @@ SUBDIRS += $(PLDM_DIR) CPPFLAGS += -I$(SRC)/pldm/libpldm/ CPPFLAGS += -I$(SRC)/pldm/ibm/libpldm/ -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 51dbad0a..c7d7bb3b 100644 --- a/core/pldm/pldm-mctp.c +++ b/core/pldm/pldm-mctp.c @@ -76,6 +76,8 @@ static int handle_message_rx(uint8_t eid, const uint8_t *buf, int len) } switch (rx.hdrinf.msg_type) { + case PLDM_RESPONSE: + return pldm_requester_handle_response(&rx); case PLDM_REQUEST: return pldm_responder_handle_request(&rx); break; @@ -120,8 +122,13 @@ int pldm_mctp_init(uint8_t mode) /* Register mandatory commands we'll respond to */ rc = pldm_responder_init(); - if (rc) + if (rc) { prlog(PR_ERR, "Failed to register mandatory commands\n"); + goto out; + } + + /* Requester implementation */ + pldm_requester_init(); out: prlog(PR_NOTICE, "%s - done, rc: %d\n", __func__, rc); diff --git a/core/pldm/pldm-requester.c b/core/pldm/pldm-requester.c new file mode 100644 index 00000000..412d495a --- /dev/null +++ b/core/pldm/pldm-requester.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +// Copyright 2022 IBM Corp. + +#define pr_fmt(fmt) "PLDM: " fmt + +#include <cpu.h> +#include <stdio.h> +#include <string.h> +#include <timebase.h> +#include <ast.h> +#include <pldm/libpldm/utils.h> +#include "pldm.h" + +struct pldm_request { + struct list_node link; + + /* originating request params */ + int dest; + int instance; + int cmd_code; + int type; + + /* messages requested */ + void *request_msg; + int request_len; + + /* 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 **response_msg; + size_t *response_len; + bool done; + int rc; +}; + +/* pldm requests queue */ +static LIST_HEAD(list_pldm_requests); +struct lock list_lock; +struct timer requests_timer; + +static struct pldm_request *active_request; + +static bool matches_request(const struct pldm_rx_data *rx, + const struct pldm_request *req) +{ + if (req->instance != rx->hdrinf.instance) + return false; + if (req->type != rx->hdrinf.pldm_type) + return false; + if (req->cmd_code != 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->response_len = len; + *resp->response_msg = zalloc(len); + memcpy(*resp->response_msg, rx->msg, len); + + resp->rc = OPAL_SUCCESS; + } else { + *resp->response_len = 0; + *resp->response_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->type, + active_request->cmd_code, + active_request->instance); + + free(active_request->request_msg); + 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, "request timedout! (active request: t:%d c:%d i:%d)\n", + active_request->type, + active_request->cmd_code, + active_request->instance); + + prlog(PR_TRACE, "%s: Original request t:%d c:%d i:%d -----\n", + __func__, req->type, req->cmd_code, req->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->type, + active_request->cmd_code, + active_request->instance + ); + return OPAL_WRONG_STATE; + } + + /* The expected message seems correct */ + handle_response(rx); + + return OPAL_SUCCESS; +} + +/* + * Send the PLDM request + */ +static void requests_poller(struct timer *t __unused, + void *data __unused, + uint64_t now __unused) +{ + int rc = OPAL_SUCCESS; + + lock(&list_lock); + + /* no new request to handle */ + if (list_empty(&list_pldm_requests)) + goto out; + + /* wait for the end of the processing of the current request */ + if (active_request) + goto out; + + /* remove the first entry in a list */ + active_request = list_pop(&list_pldm_requests, + struct pldm_request, + link); + + /* 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->type, + active_request->cmd_code, + active_request->instance); + + rc = pldm_mctp_message_tx(active_request->dest, + active_request->request_msg, + active_request->request_len); + if (rc) + prlog(PR_ERR, "%s: Error %d while sending request\n", + __func__, rc); + +out: + unlock(&list_lock); + schedule_timer(&requests_timer, msecs_to_tb(5)); +} + +/* + * Add PLDM request in the queue + */ +static int queue_request(void *request_msg, int request_len, + uint64_t timeout_ms, + void (*complete)(struct pldm_rx_data *rx, void *data), + void *complete_data) +{ + struct pldm_header_info hdrinf; + struct pldm_request *pending; + + if (unpack_pldm_header(request_msg, &hdrinf)) { + prlog(PR_ERR, "%s: error parsing pldm header\n", + __func__); + return OPAL_PARAMETER; + } + + pending = zalloc(sizeof(struct pldm_request)); + + pending->dest = BMC_EID; + pending->instance = hdrinf.instance; + pending->type = hdrinf.pldm_type; + pending->cmd_code = hdrinf.command; + pending->timeout_ms = timeout_ms; + pending->request_msg = zalloc(request_len); + memcpy(pending->request_msg, request_msg, request_len); + + pending->request_len = request_len; + pending->complete = complete; + pending->complete_data = complete_data; + + /* add an entry at the end of a linked list */ + lock(&list_lock); + prlog(PR_TRACE, "%s: Add request t:%d c:%d i:%d -----\n", + __func__, + pending->type, + pending->cmd_code, + pending->instance); + list_add_tail(&list_pldm_requests, &pending->link); + unlock(&list_lock); + + return OPAL_SUCCESS; +} + +#define TIMEOUT_MS 8000 + +/* + * Queue a PLDM request and don't wait. + * When a response is received, call the associated callback. + */ +int pldm_requester_queue(void *request_msg, size_t request_len, + void (*complete)(struct pldm_rx_data *rx, void *data), + void *complete_data) +{ + int rc = OPAL_SUCCESS; + + rc = ast_mctp_ready(); + if (rc) { + prlog(PR_ERR, "%s: BMC is not listening (rc: %d)\n", + __func__, rc); + return rc; + } + + /* Queue PLDM request */ + rc = queue_request(request_msg, request_len, + 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(void *request_msg, size_t request_len, + void **response_msg, size_t *response_len) +{ + struct pldm_response resp; + int rc; + + memset(&resp, 0, sizeof(resp)); + resp.response_msg = response_msg; + resp.response_len = response_len; + + rc = pldm_requester_queue(request_msg, request_len, + send_and_wait_complete, &resp); + if (rc) + return rc; + + /* wait for a response from the BMC */ + do { + time_wait_ms(5); + } while (!resp.done); + + return resp.rc; +} + +void pldm_requester_init(void) +{ + /* lock used to queue and unqueue pldm requests */ + init_lock(&list_lock); + + init_timer(&requests_timer, requests_poller, NULL); + schedule_timer(&requests_timer, msecs_to_tb(5)); +} diff --git a/core/pldm/pldm.h b/core/pldm/pldm.h index 7df413da..fa7a80de 100644 --- a/core/pldm/pldm.h +++ b/core/pldm/pldm.h @@ -42,4 +42,13 @@ int pldm_mctp_message_tx(uint8_t dest_id, uint8_t *buf, int len); 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(void *request_msg, size_t request_len, + void (*complete)(struct pldm_rx_data *rx, void *data), + void *complete_data); +int pldm_requester_queue_and_wait(void *request_msg, size_t request_len, + void **response_msg, size_t *response_len); +void pldm_requester_init(void); + #endif /* __COREPLDM_H__ */
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. Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com> --- core/pldm/Makefile.inc | 2 +- core/pldm/pldm-mctp.c | 9 +- core/pldm/pldm-requester.c | 331 +++++++++++++++++++++++++++++++++++++ core/pldm/pldm.h | 9 + 4 files changed, 349 insertions(+), 2 deletions(-) create mode 100644 core/pldm/pldm-requester.c