xref: /openbmc/pldm/requester/request.hpp (revision 51cc5bda)
1 #pragma once
2 
3 #include "common/flight_recorder.hpp"
4 #include "common/transport.hpp"
5 #include "common/types.hpp"
6 #include "common/utils.hpp"
7 
8 #include <libpldm/base.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&&) = delete;
38     RequestRetryTimer& operator=(const RequestRetryTimer&) = delete;
39     RequestRetryTimer& operator=(RequestRetryTimer&&) = delete;
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, error - {ERROR}", "ERROR",
79                   e);
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, response code '{RC}'",
93                   "RC", 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     sdbusplus::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&&) = delete;
139     Request& operator=(const Request&) = delete;
140     Request& operator=(Request&&) = delete;
141     ~Request() = default;
142 
143     /** @brief Constructor
144      *
145      *  @param[in] pldm_transport - PLDM transport object
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(PldmTransport* pldmTransport, mctp_eid_t eid,
155                      sdeventplus::Event& event, pldm::Request&& requestMsg,
156                      uint8_t numRetries, std::chrono::milliseconds timeout,
157                      bool verbose) :
158         RequestRetryTimer(event, numRetries, timeout),
159         pldmTransport(pldmTransport), eid(eid),
160         requestMsg(std::move(requestMsg)), verbose(verbose)
161     {}
162 
163   private:
164     PldmTransport* pldmTransport; //!< PLDM transport
165     mctp_eid_t eid;               //!< endpoint ID of the remote MCTP endpoint
166     pldm::Request requestMsg;     //!< PLDM request message
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         pldm::flightrecorder::FlightRecorder::GetInstance().saveRecord(
180             requestMsg, true);
181         const struct pldm_msg_hdr* hdr =
182             (struct pldm_msg_hdr*)(requestMsg.data());
183         if (!hdr->request)
184         {
185             return PLDM_REQUESTER_NOT_REQ_MSG;
186         }
187 
188         if (pldmTransport == nullptr)
189         {
190             error("Invalid transport: Unable to send PLDM request");
191             return PLDM_ERROR;
192         }
193 
194         auto rc = pldmTransport->sendMsg(static_cast<pldm_tid_t>(eid),
195                                          requestMsg.data(), requestMsg.size());
196         if (rc < 0)
197         {
198             error(
199                 "Failed to send pldmTransport message, response code '{RC}' and error - {ERROR}",
200                 "RC", rc, "ERROR", errno);
201             return PLDM_ERROR;
202         }
203         return PLDM_SUCCESS;
204     }
205 };
206 
207 } // namespace requester
208 
209 } // namespace pldm
210