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