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