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