| #pragma once |
| |
| #include "common/flight_recorder.hpp" |
| #include "common/types.hpp" |
| #include "common/utils.hpp" |
| |
| #include <libpldm/base.h> |
| #include <libpldm/pldm.h> |
| #include <sys/socket.h> |
| |
| #include <phosphor-logging/lg2.hpp> |
| #include <sdbusplus/timer.hpp> |
| #include <sdeventplus/event.hpp> |
| |
| #include <chrono> |
| #include <functional> |
| #include <iostream> |
| |
| PHOSPHOR_LOG2_USING; |
| |
| namespace pldm |
| { |
| namespace requester |
| { |
| /** @class RequestRetryTimer |
| * |
| * The abstract base class for implementing the PLDM request retry logic. This |
| * class handles number of times the PLDM request needs to be retried if the |
| * response is not received and the time to wait between each retry. It |
| * provides APIs to start and stop the request flow. |
| */ |
| class RequestRetryTimer |
| { |
| public: |
| RequestRetryTimer() = delete; |
| RequestRetryTimer(const RequestRetryTimer&) = delete; |
| RequestRetryTimer(RequestRetryTimer&&) = delete; |
| RequestRetryTimer& operator=(const RequestRetryTimer&) = delete; |
| RequestRetryTimer& operator=(RequestRetryTimer&&) = delete; |
| virtual ~RequestRetryTimer() = default; |
| |
| /** @brief Constructor |
| * |
| * @param[in] event - reference to PLDM daemon's main event loop |
| * @param[in] numRetries - number of request retries |
| * @param[in] timeout - time to wait between each retry in milliseconds |
| */ |
| explicit RequestRetryTimer(sdeventplus::Event& event, uint8_t numRetries, |
| std::chrono::milliseconds timeout) : |
| |
| event(event), |
| numRetries(numRetries), timeout(timeout), |
| timer(event.get(), std::bind_front(&RequestRetryTimer::callback, this)) |
| {} |
| |
| /** @brief Starts the request flow and arms the timer for request retries |
| * |
| * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise |
| */ |
| int start() |
| { |
| auto rc = send(); |
| if (rc) |
| { |
| return rc; |
| } |
| |
| try |
| { |
| if (numRetries) |
| { |
| timer.start(duration_cast<std::chrono::microseconds>(timeout), |
| true); |
| } |
| } |
| catch (const std::runtime_error& e) |
| { |
| error("Failed to start the request timer. RC = {ERR_EXCEP}", |
| "ERR_EXCEP", e.what()); |
| return PLDM_ERROR; |
| } |
| |
| return PLDM_SUCCESS; |
| } |
| |
| /** @brief Stops the timer and no further request retries happen */ |
| void stop() |
| { |
| auto rc = timer.stop(); |
| if (rc) |
| { |
| error("Failed to stop the request timer. RC = {RC}", "RC", |
| static_cast<int>(rc)); |
| } |
| } |
| |
| protected: |
| sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop |
| uint8_t numRetries; //!< number of request retries |
| std::chrono::milliseconds |
| timeout; //!< time to wait between each retry in milliseconds |
| phosphor::Timer timer; //!< manages starting timers and handling timeouts |
| |
| /** @brief Sends the PLDM request message |
| * |
| * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise |
| */ |
| virtual int send() const = 0; |
| |
| /** @brief Callback function invoked when the timeout happens */ |
| void callback() |
| { |
| if (numRetries--) |
| { |
| send(); |
| } |
| else |
| { |
| stop(); |
| } |
| } |
| }; |
| |
| /** @class Request |
| * |
| * The concrete implementation of RequestIntf. This class implements the send() |
| * to send the PLDM request message over MCTP socket. |
| * This class encapsulates the PLDM request message, the number of times the |
| * request needs to retried if the response is not received and the amount of |
| * time to wait between each retry. It provides APIs to start and stop the |
| * request flow. |
| */ |
| class Request final : public RequestRetryTimer |
| { |
| public: |
| Request() = delete; |
| Request(const Request&) = delete; |
| Request(Request&&) = delete; |
| Request& operator=(const Request&) = delete; |
| Request& operator=(Request&&) = delete; |
| ~Request() = default; |
| |
| /** @brief Constructor |
| * |
| * @param[in] fd - fd of the MCTP communication socket |
| * @param[in] eid - endpoint ID of the remote MCTP endpoint |
| * @param[in] currrentSendbuffSize - the current send buffer size |
| * @param[in] event - reference to PLDM daemon's main event loop |
| * @param[in] requestMsg - PLDM request message |
| * @param[in] numRetries - number of request retries |
| * @param[in] timeout - time to wait between each retry in milliseconds |
| * @param[in] verbose - verbose tracing flag |
| */ |
| explicit Request(int fd, mctp_eid_t eid, sdeventplus::Event& event, |
| pldm::Request&& requestMsg, uint8_t numRetries, |
| std::chrono::milliseconds timeout, int currentSendbuffSize, |
| bool verbose) : |
| RequestRetryTimer(event, numRetries, timeout), |
| fd(fd), eid(eid), requestMsg(std::move(requestMsg)), |
| currentSendbuffSize(currentSendbuffSize), verbose(verbose) |
| {} |
| |
| private: |
| int fd; //!< file descriptor of MCTP communications socket |
| mctp_eid_t eid; //!< endpoint ID of the remote MCTP endpoint |
| pldm::Request requestMsg; //!< PLDM request message |
| mutable int currentSendbuffSize; //!< current Send Buffer size |
| bool verbose; //!< verbose tracing flag |
| |
| /** @brief Sends the PLDM request message on the socket |
| * |
| * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise |
| */ |
| int send() const |
| { |
| if (verbose) |
| { |
| pldm::utils::printBuffer(pldm::utils::Tx, requestMsg); |
| } |
| if (currentSendbuffSize >= 0 && |
| (size_t)currentSendbuffSize < requestMsg.size()) |
| { |
| int oldSendbuffSize = currentSendbuffSize; |
| currentSendbuffSize = requestMsg.size(); |
| int res = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, |
| ¤tSendbuffSize, |
| sizeof(currentSendbuffSize)); |
| if (res == -1) |
| { |
| error( |
| "Requester : Failed to set the new send buffer size [bytes] : {CURR_SND_BUF_SIZE} from current size [bytes]: {OLD_BUF_SIZE} , Error : {ERR}", |
| "CURR_SND_BUF_SIZE", currentSendbuffSize, "OLD_BUF_SIZE", |
| oldSendbuffSize, "ERR", strerror(errno)); |
| return PLDM_ERROR; |
| } |
| } |
| pldm::flightrecorder::FlightRecorder::GetInstance().saveRecord( |
| requestMsg, true); |
| auto rc = pldm_send(eid, fd, requestMsg.data(), requestMsg.size()); |
| if (rc < 0) |
| { |
| error("Failed to send PLDM message. RC = {RC}, errno = {ERR}", "RC", |
| static_cast<int>(rc), "ERR", errno); |
| return PLDM_ERROR; |
| } |
| return PLDM_SUCCESS; |
| } |
| }; |
| |
| } // namespace requester |
| |
| } // namespace pldm |