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