xref: /openbmc/pldm/requester/request.hpp (revision d15fa099)
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 <phosphor-logging/lg2.hpp>
12 #include <sdbusplus/timer.hpp>
13 #include <sdeventplus/event.hpp>
14 
15 #include <chrono>
16 #include <functional>
17 #include <iostream>
18 
19 PHOSPHOR_LOG2_USING;
20 
21 namespace pldm
22 {
23 namespace requester
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             error("Failed to start the request timer. RC = {ERR_EXCEP}",
79                   "ERR_EXCEP", e.what());
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             error("Failed to stop the request timer. RC = {RC}", "RC",
93                   static_cast<int>(rc));
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 = setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
186                                  &currentSendbuffSize,
187                                  sizeof(currentSendbuffSize));
188             if (res == -1)
189             {
190                 error(
191                     "Requester : Failed to set the new send buffer size [bytes] : {CURR_SND_BUF_SIZE} from current size [bytes]: {OLD_BUF_SIZE} , Error : {ERR}",
192                     "CURR_SND_BUF_SIZE", currentSendbuffSize, "OLD_BUF_SIZE",
193                     oldSendbuffSize, "ERR", strerror(errno));
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             error("Failed to send PLDM message. RC = {RC}, errno = {ERR}", "RC",
203                   static_cast<int>(rc), "ERR", errno);
204             return PLDM_ERROR;
205         }
206         return PLDM_SUCCESS;
207     }
208 };
209 
210 } // namespace requester
211 
212 } // namespace pldm
213