1 #pragma once 2 3 #include "libpldm/base.h" 4 #include "libpldm/requester/pldm.h" 5 6 #include "common/flight_recorder.hpp" 7 #include "common/types.hpp" 8 #include "common/utils.hpp" 9 10 #include <sys/socket.h> 11 12 #include <sdbusplus/timer.hpp> 13 #include <sdeventplus/event.hpp> 14 15 #include <chrono> 16 #include <functional> 17 #include <iostream> 18 19 namespace pldm 20 { 21 22 namespace requester 23 { 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&&) = default; 38 RequestRetryTimer& operator=(const RequestRetryTimer&) = delete; 39 RequestRetryTimer& operator=(RequestRetryTimer&&) = default; 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 std::cerr << "Failed to start the request timer. RC = " << e.what() 79 << "\n"; 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 std::cerr << "Failed to stop the request timer. RC = " << rc 93 << "\n"; 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 phosphor::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&&) = default; 139 Request& operator=(const Request&) = delete; 140 Request& operator=(Request&&) = default; 141 ~Request() = default; 142 143 /** @brief Constructor 144 * 145 * @param[in] fd - fd of the MCTP communication socket 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(int fd, mctp_eid_t eid, sdeventplus::Event& event, 155 pldm::Request&& requestMsg, uint8_t numRetries, 156 std::chrono::milliseconds timeout, int currentSendbuffSize, 157 bool verbose) : 158 RequestRetryTimer(event, numRetries, timeout), 159 fd(fd), eid(eid), requestMsg(std::move(requestMsg)), 160 currentSendbuffSize(currentSendbuffSize), verbose(verbose) 161 {} 162 163 private: 164 int fd; //!< file descriptor of MCTP communications socket 165 mctp_eid_t eid; //!< endpoint ID of the remote MCTP endpoint 166 pldm::Request requestMsg; //!< PLDM request message 167 mutable int currentSendbuffSize; //!< current Send Buffer size 168 bool verbose; //!< verbose tracing flag 169 170 /** @brief Sends the PLDM request message on the socket 171 * 172 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise 173 */ 174 int send() const 175 { 176 if (verbose) 177 { 178 pldm::utils::printBuffer(pldm::utils::Tx, requestMsg); 179 } 180 if (currentSendbuffSize >= 0 && 181 (size_t)currentSendbuffSize < requestMsg.size()) 182 { 183 int oldSendbuffSize = currentSendbuffSize; 184 currentSendbuffSize = requestMsg.size(); 185 int res = 186 setsockopt(fd, SOL_SOCKET, SO_SNDBUF, ¤tSendbuffSize, 187 sizeof(currentSendbuffSize)); 188 if (res == -1) 189 { 190 std::cerr 191 << "Requester : Failed to set the new send buffer size [bytes] : " 192 << currentSendbuffSize 193 << " from current size [bytes]: " << oldSendbuffSize 194 << " , Error : " << strerror(errno) << std::endl; 195 return PLDM_ERROR; 196 } 197 } 198 pldm::flightrecorder::FlightRecorder::GetInstance().saveRecord( 199 requestMsg, true); 200 auto rc = pldm_send(eid, fd, requestMsg.data(), requestMsg.size()); 201 if (rc < 0) 202 { 203 std::cerr << "Failed to send PLDM message. RC = " << rc 204 << ", errno = " << errno << "\n"; 205 return PLDM_ERROR; 206 } 207 return PLDM_SUCCESS; 208 } 209 }; 210 211 } // namespace requester 212 213 } // namespace pldm 214