xref: /openbmc/pldm/requester/request.hpp (revision 423e8f53)
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), 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             error("Failed to start the request timer, error - {ERROR}", "ERROR",
78                   e);
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             error("Failed to stop the request timer, response code '{RC}'",
92                   "RC", rc);
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     sdbusplus::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&&) = delete;
138     Request& operator=(const Request&) = delete;
139     Request& operator=(Request&&) = delete;
140     ~Request() = default;
141 
142     /** @brief Constructor
143      *
144      *  @param[in] pldm_transport - PLDM transport object
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(PldmTransport* pldmTransport, mctp_eid_t eid,
154                      sdeventplus::Event& event, pldm::Request&& requestMsg,
155                      uint8_t numRetries, std::chrono::milliseconds timeout,
156                      bool verbose) :
157         RequestRetryTimer(event, numRetries, timeout),
158         pldmTransport(pldmTransport), eid(eid),
159         requestMsg(std::move(requestMsg)), verbose(verbose)
160     {}
161 
162   private:
163     PldmTransport* pldmTransport; //!< PLDM transport
164     mctp_eid_t eid;               //!< endpoint ID of the remote MCTP endpoint
165     pldm::Request requestMsg;     //!< PLDM request message
166     bool verbose;                 //!< verbose tracing flag
167 
168     /** @brief Sends the PLDM request message on the socket
169      *
170      *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
171      */
172     int send() const
173     {
174         if (verbose)
175         {
176             pldm::utils::printBuffer(pldm::utils::Tx, requestMsg);
177         }
178         pldm::flightrecorder::FlightRecorder::GetInstance().saveRecord(
179             requestMsg, true);
180         const struct pldm_msg_hdr* hdr =
181             (struct pldm_msg_hdr*)(requestMsg.data());
182         if (!hdr->request)
183         {
184             return PLDM_REQUESTER_NOT_REQ_MSG;
185         }
186 
187         if (pldmTransport == nullptr)
188         {
189             error("Invalid transport: Unable to send PLDM request");
190             return PLDM_ERROR;
191         }
192 
193         auto rc = pldmTransport->sendMsg(static_cast<pldm_tid_t>(eid),
194                                          requestMsg.data(), requestMsg.size());
195         if (rc < 0)
196         {
197             error(
198                 "Failed to send pldmTransport message, response code '{RC}' and error - {ERROR}",
199                 "RC", rc, "ERROR", errno);
200             return PLDM_ERROR;
201         }
202         return PLDM_SUCCESS;
203     }
204 };
205 
206 } // namespace requester
207 
208 } // namespace pldm
209