1 #pragma once 2 3 #include "common/flight_recorder.hpp" 4 #include "common/types.hpp" 5 #include "common/utils.hpp" 6 7 #include <libpldm/base.h> 8 #include <libpldm/pldm.h> 9 #include <sys/socket.h> 10 11 #include <sdbusplus/timer.hpp> 12 #include <sdeventplus/event.hpp> 13 14 #include <chrono> 15 #include <functional> 16 #include <iostream> 17 18 namespace pldm 19 { 20 21 namespace requester 22 { 23 24 /** @class RequestRetryTimer 25 * 26 * The abstract base class for implementing the PLDM request retry logic. This 27 * class handles number of times the PLDM request needs to be retried if the 28 * response is not received and the time to wait between each retry. It 29 * provides APIs to start and stop the request flow. 30 */ 31 class RequestRetryTimer 32 { 33 public: 34 RequestRetryTimer() = delete; 35 RequestRetryTimer(const RequestRetryTimer&) = delete; 36 RequestRetryTimer(RequestRetryTimer&&) = default; 37 RequestRetryTimer& operator=(const RequestRetryTimer&) = delete; 38 RequestRetryTimer& operator=(RequestRetryTimer&&) = default; 39 virtual ~RequestRetryTimer() = default; 40 41 /** @brief Constructor 42 * 43 * @param[in] event - reference to PLDM daemon's main event loop 44 * @param[in] numRetries - number of request retries 45 * @param[in] timeout - time to wait between each retry in milliseconds 46 */ 47 explicit RequestRetryTimer(sdeventplus::Event& event, uint8_t numRetries, 48 std::chrono::milliseconds timeout) : 49 50 event(event), 51 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 std::cerr << "Failed to start the request timer. RC = " << e.what() 78 << "\n"; 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 std::cerr << "Failed to stop the request timer. RC = " << rc 92 << "\n"; 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 phosphor::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&&) = default; 138 Request& operator=(const Request&) = delete; 139 Request& operator=(Request&&) = default; 140 ~Request() = default; 141 142 /** @brief Constructor 143 * 144 * @param[in] fd - fd of the MCTP communication socket 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(int fd, mctp_eid_t eid, sdeventplus::Event& event, 154 pldm::Request&& requestMsg, uint8_t numRetries, 155 std::chrono::milliseconds timeout, int currentSendbuffSize, 156 bool verbose) : 157 RequestRetryTimer(event, numRetries, timeout), 158 fd(fd), eid(eid), requestMsg(std::move(requestMsg)), 159 currentSendbuffSize(currentSendbuffSize), verbose(verbose) 160 {} 161 162 private: 163 int fd; //!< file descriptor of MCTP communications socket 164 mctp_eid_t eid; //!< endpoint ID of the remote MCTP endpoint 165 pldm::Request requestMsg; //!< PLDM request message 166 mutable int currentSendbuffSize; //!< current Send Buffer size 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 if (currentSendbuffSize >= 0 && 180 (size_t)currentSendbuffSize < requestMsg.size()) 181 { 182 int oldSendbuffSize = currentSendbuffSize; 183 currentSendbuffSize = requestMsg.size(); 184 int res = 185 setsockopt(fd, SOL_SOCKET, SO_SNDBUF, ¤tSendbuffSize, 186 sizeof(currentSendbuffSize)); 187 if (res == -1) 188 { 189 std::cerr 190 << "Requester : Failed to set the new send buffer size [bytes] : " 191 << currentSendbuffSize 192 << " from current size [bytes]: " << oldSendbuffSize 193 << " , Error : " << strerror(errno) << std::endl; 194 return PLDM_ERROR; 195 } 196 } 197 pldm::flightrecorder::FlightRecorder::GetInstance().saveRecord( 198 requestMsg, true); 199 auto rc = pldm_send(eid, fd, requestMsg.data(), requestMsg.size()); 200 if (rc < 0) 201 { 202 std::cerr << "Failed to send PLDM message. RC = " << rc 203 << ", errno = " << errno << "\n"; 204 return PLDM_ERROR; 205 } 206 return PLDM_SUCCESS; 207 } 208 }; 209 210 } // namespace requester 211 212 } // namespace pldm 213