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