xref: /openbmc/pldm/requester/request.hpp (revision 03b01ca8)
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 using namespace std::chrono;
22 
23 /** @class RequestRetryTimer
24  *
25  *  The abstract base class for implementing the PLDM request retry logic. This
26  *  class handles number of times the PLDM request needs to be retried if the
27  *  response is not received and the time to wait between each retry. It
28  *  provides APIs to start and stop the request flow.
29  */
30 class RequestRetryTimer
31 {
32   public:
33     RequestRetryTimer() = delete;
34     RequestRetryTimer(const RequestRetryTimer&) = delete;
35     RequestRetryTimer(RequestRetryTimer&&) = default;
36     RequestRetryTimer& operator=(const RequestRetryTimer&) = delete;
37     RequestRetryTimer& operator=(RequestRetryTimer&&) = default;
38     virtual ~RequestRetryTimer() = default;
39 
40     /** @brief Constructor
41      *
42      *  @param[in] event - reference to PLDM daemon's main event loop
43      *  @param[in] numRetries - number of request retries
44      *  @param[in] timeout - time to wait between each retry in milliseconds
45      */
46     explicit RequestRetryTimer(sdeventplus::Event& event, uint8_t numRetries,
47                                milliseconds timeout) :
48 
49         event(event),
50         numRetries(numRetries), timeout(timeout),
51         timer(event.get(), std::bind_front(&RequestRetryTimer::callback, this))
52     {}
53 
54     /** @brief Starts the request flow and arms the timer for request retries
55      *
56      *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
57      */
58     int start()
59     {
60         auto rc = send();
61         if (rc)
62         {
63             return rc;
64         }
65 
66         try
67         {
68             if (numRetries)
69             {
70                 timer.start(duration_cast<microseconds>(timeout), true);
71             }
72         }
73         catch (const std::runtime_error& e)
74         {
75             std::cerr << "Failed to start the request timer. RC = " << e.what()
76                       << "\n";
77             return PLDM_ERROR;
78         }
79 
80         return PLDM_SUCCESS;
81     }
82 
83     /** @brief Stops the timer and no further request retries happen */
84     void stop()
85     {
86         auto rc = timer.stop();
87         if (rc)
88         {
89             std::cerr << "Failed to stop the request timer. RC = " << rc
90                       << "\n";
91         }
92     }
93 
94   protected:
95     sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
96     uint8_t numRetries;        //!< number of request retries
97     milliseconds 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                      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