| /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ |
| /* NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ |
| #define _GNU_SOURCE |
| #include "array.h" |
| #include "container-of.h" |
| #include "transport.h" |
| #include "test.h" |
| |
| #include <errno.h> |
| #include <poll.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| struct pldm_transport_test { |
| struct pldm_transport transport; |
| const struct pldm_transport_test_descriptor *seq; |
| size_t count; |
| size_t cursor; |
| int timerfd; |
| }; |
| |
| #define transport_to_test(ptr) \ |
| container_of(ptr, struct pldm_transport_test, transport) |
| |
| LIBPLDM_ABI_TESTING |
| struct pldm_transport *pldm_transport_test_core(struct pldm_transport_test *ctx) |
| { |
| return &ctx->transport; |
| } |
| |
| #ifdef PLDM_HAS_POLL |
| #include <poll.h> |
| LIBPLDM_ABI_TESTING |
| int pldm_transport_test_init_pollfd(struct pldm_transport *ctx, |
| struct pollfd *pollfd) |
| { |
| static const struct itimerspec disable = { |
| .it_value = { 0, 0 }, |
| .it_interval = { 0, 0 }, |
| }; |
| struct pldm_transport_test *test = transport_to_test(ctx); |
| const struct pldm_transport_test_descriptor *desc; |
| int rc; |
| |
| rc = timerfd_settime(test->timerfd, 0, &disable, NULL); |
| if (rc < 0) { |
| return PLDM_REQUESTER_POLL_FAIL; |
| } |
| |
| if (test->cursor >= test->count) { |
| return PLDM_REQUESTER_POLL_FAIL; |
| } |
| |
| desc = &test->seq[test->cursor]; |
| |
| if (desc->type == PLDM_TRANSPORT_TEST_ELEMENT_LATENCY) { |
| rc = timerfd_settime(test->timerfd, 0, &desc->latency, NULL); |
| if (rc < 0) { |
| return PLDM_REQUESTER_POLL_FAIL; |
| } |
| |
| /* This was an explicit latency element, so now move beyond it for recv */ |
| test->cursor++; |
| } else if (desc->type == PLDM_TRANSPORT_TEST_ELEMENT_MSG_RECV) { |
| /* Expire the timer immediately so it appears ready */ |
| static const struct timespec ensure_ready = { |
| .tv_sec = 0, |
| .tv_nsec = 2, |
| }; |
| static const struct itimerspec ready = { |
| .it_value = { 0, 1 }, |
| .it_interval = { 0, 0 }, |
| }; |
| struct pollfd pfds[] = { |
| { .fd = test->timerfd, .events = POLLIN }, |
| }; |
| |
| rc = timerfd_settime(test->timerfd, 0, &ready, NULL); |
| if (rc < 0) { |
| return PLDM_REQUESTER_POLL_FAIL; |
| } |
| |
| rc = ppoll(pfds, ARRAY_SIZE(pfds), &ensure_ready, NULL); |
| if (rc < 1) { |
| return PLDM_REQUESTER_POLL_FAIL; |
| } |
| |
| /* Don't increment test->cursor as recv needs to consume the current test element */ |
| } else { |
| return PLDM_REQUESTER_POLL_FAIL; |
| } |
| |
| pollfd->fd = test->timerfd; |
| pollfd->events = POLLIN; |
| |
| return 0; |
| } |
| #endif |
| |
| static pldm_requester_rc_t pldm_transport_test_recv(struct pldm_transport *ctx, |
| pldm_tid_t *tid, |
| void **pldm_resp_msg, |
| size_t *resp_msg_len) |
| { |
| struct pldm_transport_test *test = transport_to_test(ctx); |
| const struct pldm_transport_test_descriptor *desc; |
| void *msg; |
| |
| if (test->cursor >= test->count) { |
| return PLDM_REQUESTER_RECV_FAIL; |
| } |
| |
| desc = &test->seq[test->cursor]; |
| |
| if (desc->type != PLDM_TRANSPORT_TEST_ELEMENT_MSG_RECV) { |
| return PLDM_REQUESTER_RECV_FAIL; |
| } |
| |
| msg = malloc(desc->recv_msg.len); |
| if (!msg) { |
| return PLDM_REQUESTER_RECV_FAIL; |
| } |
| |
| memcpy(msg, desc->recv_msg.msg, desc->recv_msg.len); |
| *pldm_resp_msg = msg; |
| *resp_msg_len = desc->recv_msg.len; |
| *tid = desc->recv_msg.src; |
| |
| test->cursor++; |
| |
| return PLDM_REQUESTER_SUCCESS; |
| } |
| |
| static pldm_requester_rc_t pldm_transport_test_send(struct pldm_transport *ctx, |
| pldm_tid_t tid, |
| const void *pldm_req_msg, |
| size_t req_msg_len) |
| { |
| struct pldm_transport_test *test = transport_to_test(ctx); |
| const struct pldm_transport_test_descriptor *desc; |
| |
| if (test->cursor > test->count) { |
| return PLDM_REQUESTER_SEND_FAIL; |
| } |
| |
| desc = &test->seq[test->cursor]; |
| |
| if (desc->type != PLDM_TRANSPORT_TEST_ELEMENT_MSG_SEND) { |
| return PLDM_REQUESTER_SEND_FAIL; |
| } |
| |
| if (desc->send_msg.dst != tid) { |
| return PLDM_REQUESTER_SEND_FAIL; |
| } |
| |
| if (desc->send_msg.len != req_msg_len) { |
| return PLDM_REQUESTER_SEND_FAIL; |
| } |
| |
| if (memcmp(desc->send_msg.msg, pldm_req_msg, req_msg_len) != 0) { |
| return PLDM_REQUESTER_SEND_FAIL; |
| } |
| |
| test->cursor++; |
| |
| return PLDM_REQUESTER_SUCCESS; |
| } |
| |
| LIBPLDM_ABI_TESTING |
| int pldm_transport_test_init(struct pldm_transport_test **ctx, |
| const struct pldm_transport_test_descriptor *seq, |
| size_t count) |
| { |
| int rc; |
| |
| if (!ctx || *ctx) { |
| return -EINVAL; |
| } |
| |
| struct pldm_transport_test *test = malloc(sizeof(*test)); |
| if (!test) { |
| return -ENOMEM; |
| } |
| |
| test->transport.name = "TEST"; |
| test->transport.version = 1; |
| test->transport.recv = pldm_transport_test_recv; |
| test->transport.send = pldm_transport_test_send; |
| test->transport.init_pollfd = pldm_transport_test_init_pollfd; |
| test->seq = seq; |
| test->count = count; |
| test->cursor = 0; |
| test->timerfd = timerfd_create(CLOCK_MONOTONIC, 0); |
| if (test->timerfd < 0) { |
| rc = -errno; |
| goto cleanup_test; |
| } |
| |
| *ctx = test; |
| |
| return 0; |
| |
| cleanup_test: |
| free(test); |
| return rc; |
| } |
| |
| LIBPLDM_ABI_TESTING |
| void pldm_transport_test_destroy(struct pldm_transport_test *ctx) |
| { |
| close(ctx->timerfd); |
| free(ctx); |
| } |