blob: ffbb16994e2a7cac0b02802ef8604adc1eaa6cfe [file] [log] [blame]
Tom Joseph74f27c72021-05-16 07:58:53 -07001#pragma once
2
Manojkiran Edaef773052021-07-29 09:29:28 +05303#include "common/flight_recorder.hpp"
Tom Joseph74f27c72021-05-16 07:58:53 -07004#include "common/types.hpp"
Tom Josephe5268cd2021-09-07 13:04:03 +05305#include "common/utils.hpp"
Tom Joseph74f27c72021-05-16 07:58:53 -07006
George Liuc453e162022-12-21 17:16:23 +08007#include <libpldm/base.h>
8#include <libpldm/pldm.h>
Manojkiran Eda9fffea22021-10-27 16:03:27 +05309#include <sys/socket.h>
10
Tom Joseph74f27c72021-05-16 07:58:53 -070011#include <sdbusplus/timer.hpp>
12#include <sdeventplus/event.hpp>
13
14#include <chrono>
15#include <functional>
16#include <iostream>
17
18namespace pldm
19{
20
21namespace requester
22{
23
Tom Joseph74f27c72021-05-16 07:58:53 -070024/** @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 */
31class 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,
Brad Bishop5079ac42021-08-19 18:35:06 -040048 std::chrono::milliseconds timeout) :
Tom Joseph74f27c72021-05-16 07:58:53 -070049
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();
Tom Josepha5ed6582021-06-17 22:08:47 -070062 if (rc)
Tom Joseph74f27c72021-05-16 07:58:53 -070063 {
64 return rc;
65 }
66
67 try
68 {
69 if (numRetries)
70 {
Brad Bishop5079ac42021-08-19 18:35:06 -040071 timer.start(duration_cast<std::chrono::microseconds>(timeout),
72 true);
Tom Joseph74f27c72021-05-16 07:58:53 -070073 }
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
Brad Bishop5079ac42021-08-19 18:35:06 -040099 std::chrono::milliseconds
100 timeout; //!< time to wait between each retry in milliseconds
Tom Joseph74f27c72021-05-16 07:58:53 -0700101 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 */
132class 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
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530146 * @param[in] currrentSendbuffSize - the current send buffer size
Tom Joseph74f27c72021-05-16 07:58:53 -0700147 * @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
Tom Josephe5268cd2021-09-07 13:04:03 +0530151 * @param[in] verbose - verbose tracing flag
Tom Joseph74f27c72021-05-16 07:58:53 -0700152 */
153 explicit Request(int fd, mctp_eid_t eid, sdeventplus::Event& event,
154 pldm::Request&& requestMsg, uint8_t numRetries,
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530155 std::chrono::milliseconds timeout, int currentSendbuffSize,
156 bool verbose) :
Tom Joseph74f27c72021-05-16 07:58:53 -0700157 RequestRetryTimer(event, numRetries, timeout),
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530158 fd(fd), eid(eid), requestMsg(std::move(requestMsg)),
159 currentSendbuffSize(currentSendbuffSize), verbose(verbose)
Tom Joseph74f27c72021-05-16 07:58:53 -0700160 {}
161
162 private:
Sampa Misra42336b52021-08-18 01:07:07 -0500163 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
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530166 mutable int currentSendbuffSize; //!< current Send Buffer size
167 bool verbose; //!< verbose tracing flag
Tom Joseph74f27c72021-05-16 07:58:53 -0700168
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 {
Tom Josephe5268cd2021-09-07 13:04:03 +0530175 if (verbose)
176 {
177 pldm::utils::printBuffer(pldm::utils::Tx, requestMsg);
178 }
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530179 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 }
Manojkiran Edaef773052021-07-29 09:29:28 +0530197 pldm::flightrecorder::FlightRecorder::GetInstance().saveRecord(
198 requestMsg, true);
Tom Joseph74f27c72021-05-16 07:58:53 -0700199 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
Sampa Misrac0c79482021-06-02 08:01:54 -0500212} // namespace pldm