1 #pragma once 2 3 #include "common/flight_recorder.hpp" 4 #include "common/transport.hpp" 5 #include "common/types.hpp" 6 #include "common/utils.hpp" 7 8 #include <libpldm/base.h> 9 #include <sys/socket.h> 10 11 #include <phosphor-logging/lg2.hpp> 12 #include <sdbusplus/timer.hpp> 13 #include <sdeventplus/event.hpp> 14 15 #include <chrono> 16 #include <functional> 17 #include <iostream> 18 19 PHOSPHOR_LOG2_USING; 20 21 namespace pldm 22 { 23 namespace requester 24 { 25 /** @class RequestRetryTimer 26 * 27 * The abstract base class for implementing the PLDM request retry logic. This 28 * class handles number of times the PLDM request needs to be retried if the 29 * response is not received and the time to wait between each retry. It 30 * provides APIs to start and stop the request flow. 31 */ 32 class RequestRetryTimer 33 { 34 public: 35 RequestRetryTimer() = delete; 36 RequestRetryTimer(const RequestRetryTimer&) = delete; 37 RequestRetryTimer(RequestRetryTimer&&) = delete; 38 RequestRetryTimer& operator=(const RequestRetryTimer&) = delete; 39 RequestRetryTimer& operator=(RequestRetryTimer&&) = delete; 40 virtual ~RequestRetryTimer() = default; 41 42 /** @brief Constructor 43 * 44 * @param[in] event - reference to PLDM daemon's main event loop 45 * @param[in] numRetries - number of request retries 46 * @param[in] timeout - time to wait between each retry in milliseconds 47 */ 48 explicit RequestRetryTimer(sdeventplus::Event& event, uint8_t numRetries, 49 std::chrono::milliseconds timeout) : 50 51 event(event), 52 numRetries(numRetries), timeout(timeout), 53 timer(event.get(), std::bind_front(&RequestRetryTimer::callback, this)) 54 {} 55 56 /** @brief Starts the request flow and arms the timer for request retries 57 * 58 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise 59 */ 60 int start() 61 { 62 auto rc = send(); 63 if (rc) 64 { 65 return rc; 66 } 67 68 try 69 { 70 if (numRetries) 71 { 72 timer.start(duration_cast<std::chrono::microseconds>(timeout), 73 true); 74 } 75 } 76 catch (const std::runtime_error& e) 77 { 78 error("Failed to start the request timer. RC = {ERR_EXCEP}", 79 "ERR_EXCEP", e.what()); 80 return PLDM_ERROR; 81 } 82 83 return PLDM_SUCCESS; 84 } 85 86 /** @brief Stops the timer and no further request retries happen */ 87 void stop() 88 { 89 auto rc = timer.stop(); 90 if (rc) 91 { 92 error("Failed to stop the request timer. RC = {RC}", "RC", 93 static_cast<int>(rc)); 94 } 95 } 96 97 protected: 98 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop 99 uint8_t numRetries; //!< number of request retries 100 std::chrono::milliseconds 101 timeout; //!< time to wait between each retry in milliseconds 102 sdbusplus::Timer timer; //!< manages starting timers and handling timeouts 103 104 /** @brief Sends the PLDM request message 105 * 106 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise 107 */ 108 virtual int send() const = 0; 109 110 /** @brief Callback function invoked when the timeout happens */ 111 void callback() 112 { 113 if (numRetries--) 114 { 115 send(); 116 } 117 else 118 { 119 stop(); 120 } 121 } 122 }; 123 124 /** @class Request 125 * 126 * The concrete implementation of RequestIntf. This class implements the send() 127 * to send the PLDM request message over MCTP socket. 128 * This class encapsulates the PLDM request message, the number of times the 129 * request needs to retried if the response is not received and the amount of 130 * time to wait between each retry. It provides APIs to start and stop the 131 * request flow. 132 */ 133 class Request final : public RequestRetryTimer 134 { 135 public: 136 Request() = delete; 137 Request(const Request&) = delete; 138 Request(Request&&) = delete; 139 Request& operator=(const Request&) = delete; 140 Request& operator=(Request&&) = delete; 141 ~Request() = default; 142 143 /** @brief Constructor 144 * 145 * @param[in] pldm_transport - PLDM transport object 146 * @param[in] eid - endpoint ID of the remote MCTP endpoint 147 * @param[in] currrentSendbuffSize - the current send buffer size 148 * @param[in] event - reference to PLDM daemon's main event loop 149 * @param[in] requestMsg - PLDM request message 150 * @param[in] numRetries - number of request retries 151 * @param[in] timeout - time to wait between each retry in milliseconds 152 * @param[in] verbose - verbose tracing flag 153 */ 154 explicit Request(PldmTransport* pldmTransport, mctp_eid_t eid, 155 sdeventplus::Event& event, pldm::Request&& requestMsg, 156 uint8_t numRetries, std::chrono::milliseconds timeout, 157 bool verbose) : 158 RequestRetryTimer(event, numRetries, timeout), 159 pldmTransport(pldmTransport), eid(eid), 160 requestMsg(std::move(requestMsg)), verbose(verbose) 161 {} 162 163 private: 164 PldmTransport* pldmTransport; //!< PLDM transport 165 mctp_eid_t eid; //!< endpoint ID of the remote MCTP endpoint 166 pldm::Request requestMsg; //!< PLDM request message 167 bool verbose; //!< verbose tracing flag 168 169 /** @brief Sends the PLDM request message on the socket 170 * 171 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise 172 */ 173 int send() const 174 { 175 if (verbose) 176 { 177 pldm::utils::printBuffer(pldm::utils::Tx, requestMsg); 178 } 179 pldm::flightrecorder::FlightRecorder::GetInstance().saveRecord( 180 requestMsg, true); 181 const struct pldm_msg_hdr* hdr = 182 (struct pldm_msg_hdr*)(requestMsg.data()); 183 if (!hdr->request) 184 { 185 return PLDM_REQUESTER_NOT_REQ_MSG; 186 } 187 188 if (pldmTransport == nullptr) 189 { 190 error("Invalid transport: Unable to send PLDM request"); 191 return PLDM_ERROR; 192 } 193 194 auto rc = pldmTransport->sendMsg(static_cast<pldm_tid_t>(eid), 195 requestMsg.data(), requestMsg.size()); 196 if (rc < 0) 197 { 198 error("Failed to send PLDM message. RC = {RC}, errno = {ERR}", "RC", 199 static_cast<int>(rc), "ERR", errno); 200 return PLDM_ERROR; 201 } 202 return PLDM_SUCCESS; 203 } 204 }; 205 206 } // namespace requester 207 208 } // namespace pldm 209