blob: f1a91d77bf1b4eebc8e4db36c858321e8228dbc6 [file] [log] [blame]
Tom Joseph74f27c72021-05-16 07:58:53 -07001#pragma once
2
3#include "libpldm/base.h"
4#include "libpldm/requester/pldm.h"
5
6#include "common/types.hpp"
7
8#include <sdbusplus/timer.hpp>
9#include <sdeventplus/event.hpp>
10
11#include <chrono>
12#include <functional>
13#include <iostream>
14
15namespace pldm
16{
17
18namespace requester
19{
20
Tom Joseph74f27c72021-05-16 07:58:53 -070021/** @class RequestRetryTimer
22 *
23 * The abstract base class for implementing the PLDM request retry logic. This
24 * class handles number of times the PLDM request needs to be retried if the
25 * response is not received and the time to wait between each retry. It
26 * provides APIs to start and stop the request flow.
27 */
28class RequestRetryTimer
29{
30 public:
31 RequestRetryTimer() = delete;
32 RequestRetryTimer(const RequestRetryTimer&) = delete;
33 RequestRetryTimer(RequestRetryTimer&&) = default;
34 RequestRetryTimer& operator=(const RequestRetryTimer&) = delete;
35 RequestRetryTimer& operator=(RequestRetryTimer&&) = default;
36 virtual ~RequestRetryTimer() = default;
37
38 /** @brief Constructor
39 *
40 * @param[in] event - reference to PLDM daemon's main event loop
41 * @param[in] numRetries - number of request retries
42 * @param[in] timeout - time to wait between each retry in milliseconds
43 */
44 explicit RequestRetryTimer(sdeventplus::Event& event, uint8_t numRetries,
Brad Bishop5079ac42021-08-19 18:35:06 -040045 std::chrono::milliseconds timeout) :
Tom Joseph74f27c72021-05-16 07:58:53 -070046
47 event(event),
48 numRetries(numRetries), timeout(timeout),
49 timer(event.get(), std::bind_front(&RequestRetryTimer::callback, this))
50 {}
51
52 /** @brief Starts the request flow and arms the timer for request retries
53 *
54 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
55 */
56 int start()
57 {
58 auto rc = send();
Tom Josepha5ed6582021-06-17 22:08:47 -070059 if (rc)
Tom Joseph74f27c72021-05-16 07:58:53 -070060 {
61 return rc;
62 }
63
64 try
65 {
66 if (numRetries)
67 {
Brad Bishop5079ac42021-08-19 18:35:06 -040068 timer.start(duration_cast<std::chrono::microseconds>(timeout),
69 true);
Tom Joseph74f27c72021-05-16 07:58:53 -070070 }
71 }
72 catch (const std::runtime_error& e)
73 {
74 std::cerr << "Failed to start the request timer. RC = " << e.what()
75 << "\n";
76 return PLDM_ERROR;
77 }
78
79 return PLDM_SUCCESS;
80 }
81
82 /** @brief Stops the timer and no further request retries happen */
83 void stop()
84 {
85 auto rc = timer.stop();
86 if (rc)
87 {
88 std::cerr << "Failed to stop the request timer. RC = " << rc
89 << "\n";
90 }
91 }
92
93 protected:
94 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
95 uint8_t numRetries; //!< number of request retries
Brad Bishop5079ac42021-08-19 18:35:06 -040096 std::chrono::milliseconds
97 timeout; //!< time to wait between each retry in milliseconds
Tom Joseph74f27c72021-05-16 07:58:53 -070098 phosphor::Timer timer; //!< manages starting timers and handling timeouts
99
100 /** @brief Sends the PLDM request message
101 *
102 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
103 */
104 virtual int send() const = 0;
105
106 /** @brief Callback function invoked when the timeout happens */
107 void callback()
108 {
109 if (numRetries--)
110 {
111 send();
112 }
113 else
114 {
115 stop();
116 }
117 }
118};
119
120/** @class Request
121 *
122 * The concrete implementation of RequestIntf. This class implements the send()
123 * to send the PLDM request message over MCTP socket.
124 * This class encapsulates the PLDM request message, the number of times the
125 * request needs to retried if the response is not received and the amount of
126 * time to wait between each retry. It provides APIs to start and stop the
127 * request flow.
128 */
129class Request final : public RequestRetryTimer
130{
131 public:
132 Request() = delete;
133 Request(const Request&) = delete;
134 Request(Request&&) = default;
135 Request& operator=(const Request&) = delete;
136 Request& operator=(Request&&) = default;
137 ~Request() = default;
138
139 /** @brief Constructor
140 *
141 * @param[in] fd - fd of the MCTP communication socket
142 * @param[in] eid - endpoint ID of the remote MCTP endpoint
143 * @param[in] event - reference to PLDM daemon's main event loop
144 * @param[in] requestMsg - PLDM request message
145 * @param[in] numRetries - number of request retries
146 * @param[in] timeout - time to wait between each retry in milliseconds
147 */
148 explicit Request(int fd, mctp_eid_t eid, sdeventplus::Event& event,
149 pldm::Request&& requestMsg, uint8_t numRetries,
Brad Bishop5079ac42021-08-19 18:35:06 -0400150 std::chrono::milliseconds timeout) :
Tom Joseph74f27c72021-05-16 07:58:53 -0700151 RequestRetryTimer(event, numRetries, timeout),
152 fd(fd), eid(eid), requestMsg(std::move(requestMsg))
153 {}
154
155 private:
Sampa Misra42336b52021-08-18 01:07:07 -0500156 int fd; //!< file descriptor of MCTP communications socket
157 mctp_eid_t eid; //!< endpoint ID of the remote MCTP endpoint
158 pldm::Request requestMsg; //!< PLDM request message
Tom Joseph74f27c72021-05-16 07:58:53 -0700159
160 /** @brief Sends the PLDM request message on the socket
161 *
162 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
163 */
164 int send() const
165 {
166 auto rc = pldm_send(eid, fd, requestMsg.data(), requestMsg.size());
167 if (rc < 0)
168 {
169 std::cerr << "Failed to send PLDM message. RC = " << rc
170 << ", errno = " << errno << "\n";
171 return PLDM_ERROR;
172 }
173 return PLDM_SUCCESS;
174 }
175};
176
177} // namespace requester
178
Sampa Misrac0c79482021-06-02 08:01:54 -0500179} // namespace pldm