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