xref: /openbmc/pldm/requester/request.hpp (revision 74f27c73)
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