blob: 12c36feb9f8fee1c4192cf45e6b8786b6fcdefe8 [file] [log] [blame]
Tom Joseph74f27c72021-05-16 07:58:53 -07001#pragma once
2
3#include "config.h"
4
Tom Joseph74f27c72021-05-16 07:58:53 -07005#include "common/types.hpp"
6#include "pldmd/dbus_impl_requester.hpp"
7#include "request.hpp"
8
George Liuc453e162022-12-21 17:16:23 +08009#include <libpldm/base.h>
10#include <libpldm/pldm.h>
Manojkiran Eda9fffea22021-10-27 16:03:27 +053011#include <sys/socket.h>
12
Tom Joseph74f27c72021-05-16 07:58:53 -070013#include <function2/function2.hpp>
14#include <sdbusplus/timer.hpp>
15#include <sdeventplus/event.hpp>
16#include <sdeventplus/source/event.hpp>
17
18#include <cassert>
19#include <chrono>
20#include <memory>
21#include <tuple>
22#include <unordered_map>
23
24namespace pldm
25{
26
27namespace requester
28{
29
30/** @struct RequestKey
31 *
32 * RequestKey uniquely identifies the PLDM request message to match it with the
33 * response and a combination of MCTP endpoint ID, PLDM instance ID, PLDM type
34 * and PLDM command is the key.
35 */
36struct RequestKey
37{
38 mctp_eid_t eid; //!< MCTP endpoint ID
39 uint8_t instanceId; //!< PLDM instance ID
40 uint8_t type; //!< PLDM type
41 uint8_t command; //!< PLDM command
42
43 bool operator==(const RequestKey& e) const
44 {
45 return ((eid == e.eid) && (instanceId == e.instanceId) &&
46 (type == e.type) && (command == e.command));
47 }
48};
49
50/** @struct RequestKeyHasher
51 *
52 * This is a simple hash function, since the instance ID generator API
53 * generates unique instance IDs for MCTP endpoint ID.
54 */
55struct RequestKeyHasher
56{
57 std::size_t operator()(const RequestKey& key) const
58 {
59 return (key.eid << 24 | key.instanceId << 16 | key.type << 8 |
60 key.command);
61 }
62};
63
64using ResponseHandler = fu2::unique_function<void(
65 mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)>;
Tom Joseph74f27c72021-05-16 07:58:53 -070066
67/** @class Handler
68 *
69 * This class handles the lifecycle of the PLDM request message based on the
70 * instance ID expiration interval, number of request retries and the timeout
71 * waiting for a response. The registered response handlers are invoked with
72 * response once the PLDM responder sends the response. If no response is
73 * received within the instance ID expiration interval or any other failure the
74 * response handler is invoked with the empty response.
75 *
76 * @tparam RequestInterface - Request class type
77 */
78template <class RequestInterface>
79class Handler
80{
Tom Joseph74f27c72021-05-16 07:58:53 -070081 public:
82 Handler() = delete;
83 Handler(const Handler&) = delete;
84 Handler(Handler&&) = delete;
85 Handler& operator=(const Handler&) = delete;
86 Handler& operator=(Handler&&) = delete;
87 ~Handler() = default;
88
89 /** @brief Constructor
90 *
91 * @param[in] fd - fd of MCTP communications socket
92 * @param[in] event - reference to PLDM daemon's main event loop
93 * @param[in] requester - reference to Requester object
Manojkiran Eda9fffea22021-10-27 16:03:27 +053094 * @param[in] currentSendbuffSize - current send buffer size
Tom Josephe5268cd2021-09-07 13:04:03 +053095 * @param[in] verbose - verbose tracing flag
Tom Joseph74f27c72021-05-16 07:58:53 -070096 * @param[in] instanceIdExpiryInterval - instance ID expiration interval
97 * @param[in] numRetries - number of request retries
98 * @param[in] responseTimeOut - time to wait between each retry
99 */
100 explicit Handler(
Brad Bishop5079ac42021-08-19 18:35:06 -0400101 int fd, sdeventplus::Event& event, pldm::dbus_api::Requester& requester,
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530102 int currentSendbuffSize, bool verbose,
Brad Bishop5079ac42021-08-19 18:35:06 -0400103 std::chrono::seconds instanceIdExpiryInterval =
104 std::chrono::seconds(INSTANCE_ID_EXPIRATION_INTERVAL),
Tom Joseph74f27c72021-05-16 07:58:53 -0700105 uint8_t numRetries = static_cast<uint8_t>(NUMBER_OF_REQUEST_RETRIES),
Brad Bishop5079ac42021-08-19 18:35:06 -0400106 std::chrono::milliseconds responseTimeOut =
107 std::chrono::milliseconds(RESPONSE_TIME_OUT)) :
Tom Joseph74f27c72021-05-16 07:58:53 -0700108 fd(fd),
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530109 event(event), requester(requester),
110 currentSendbuffSize(currentSendbuffSize), verbose(verbose),
Tom Joseph74f27c72021-05-16 07:58:53 -0700111 instanceIdExpiryInterval(instanceIdExpiryInterval),
112 numRetries(numRetries), responseTimeOut(responseTimeOut)
113 {}
114
115 /** @brief Register a PLDM request message
116 *
117 * @param[in] eid - endpoint ID of the remote MCTP endpoint
118 * @param[in] instanceId - instance ID to match request and response
119 * @param[in] type - PLDM type
120 * @param[in] command - PLDM command
121 * @param[in] requestMsg - PLDM request message
122 * @param[in] responseHandler - Response handler for this request
123 *
124 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
125 */
126 int registerRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
127 uint8_t command, pldm::Request&& requestMsg,
128 ResponseHandler&& responseHandler)
129 {
130 RequestKey key{eid, instanceId, type, command};
131
132 auto instanceIdExpiryCallBack = [key, this](void) {
133 if (this->handlers.contains(key))
134 {
135 std::cerr << "Response not received for the request, instance "
136 "ID expired."
Manojkiran Eda394fac62021-07-22 15:58:29 +0530137 << " EID = " << (unsigned)key.eid
138 << " INSTANCE_ID = " << (unsigned)key.instanceId
139 << " TYPE = " << (unsigned)key.type
140 << " COMMAND = " << (unsigned)key.command << "\n";
Tom Joseph74f27c72021-05-16 07:58:53 -0700141 auto& [request, responseHandler, timerInstance] =
142 this->handlers[key];
143 request->stop();
144 auto rc = timerInstance->stop();
145 if (rc)
146 {
147 std::cerr
148 << "Failed to stop the instance ID expiry timer. RC = "
149 << rc << "\n";
150 }
151 // Call response handler with an empty response to indicate no
152 // response
153 responseHandler(key.eid, nullptr, 0);
154 this->removeRequestContainer.emplace(
155 key, std::make_unique<sdeventplus::source::Defer>(
156 event, std::bind(&Handler::removeRequestEntry,
157 this, key)));
158 }
159 else
160 {
161 // This condition is not possible, if a response is received
162 // before the instance ID expiry, then the response handler
163 // is executed and the entry will be removed.
164 assert(false);
165 }
166 };
167
168 auto request = std::make_unique<RequestInterface>(
Tom Josephe5268cd2021-09-07 13:04:03 +0530169 fd, eid, event, std::move(requestMsg), numRetries, responseTimeOut,
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530170 currentSendbuffSize, verbose);
Tom Joseph74f27c72021-05-16 07:58:53 -0700171 auto timer = std::make_unique<phosphor::Timer>(
172 event.get(), instanceIdExpiryCallBack);
173
174 auto rc = request->start();
Tom Josepha5ed6582021-06-17 22:08:47 -0700175 if (rc)
Tom Joseph74f27c72021-05-16 07:58:53 -0700176 {
Tom Josepha5ed6582021-06-17 22:08:47 -0700177 requester.markFree(eid, instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700178 std::cerr << "Failure to send the PLDM request message"
179 << "\n";
180 return rc;
181 }
182
183 try
184 {
Brad Bishop5079ac42021-08-19 18:35:06 -0400185 timer->start(duration_cast<std::chrono::microseconds>(
186 instanceIdExpiryInterval));
Tom Joseph74f27c72021-05-16 07:58:53 -0700187 }
188 catch (const std::runtime_error& e)
189 {
Tom Josepha5ed6582021-06-17 22:08:47 -0700190 requester.markFree(eid, instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700191 std::cerr << "Failed to start the instance ID expiry timer. RC = "
192 << e.what() << "\n";
193 return PLDM_ERROR;
194 }
195
196 handlers.emplace(key, std::make_tuple(std::move(request),
197 std::move(responseHandler),
198 std::move(timer)));
199 return rc;
200 }
201
202 /** @brief Handle PLDM response message
203 *
204 * @param[in] eid - endpoint ID of the remote MCTP endpoint
205 * @param[in] instanceId - instance ID to match request and response
206 * @param[in] type - PLDM type
207 * @param[in] command - PLDM command
208 * @param[in] response - PLDM response message
209 * @param[in] respMsgLen - length of the response message
210 */
211 void handleResponse(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
212 uint8_t command, const pldm_msg* response,
213 size_t respMsgLen)
214 {
215 RequestKey key{eid, instanceId, type, command};
216 if (handlers.contains(key))
217 {
218 auto& [request, responseHandler, timerInstance] = handlers[key];
219 request->stop();
220 auto rc = timerInstance->stop();
221 if (rc)
222 {
223 std::cerr
224 << "Failed to stop the instance ID expiry timer. RC = "
225 << rc << "\n";
226 }
227 responseHandler(eid, response, respMsgLen);
228 requester.markFree(key.eid, key.instanceId);
229 handlers.erase(key);
230 }
231 else
232 {
233 // Got a response for a PLDM request message not registered with the
234 // request handler, so freeing up the instance ID, this can be other
235 // OpenBMC applications relying on PLDM D-Bus apis like
236 // openpower-occ-control and softoff
237 requester.markFree(key.eid, key.instanceId);
238 }
239 }
240
241 private:
Brad Bishop5079ac42021-08-19 18:35:06 -0400242 int fd; //!< file descriptor of MCTP communications socket
243 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
244 pldm::dbus_api::Requester& requester; //!< reference to Requester object
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530245 int currentSendbuffSize; //!< current Send Buffer size
Tom Josephe5268cd2021-09-07 13:04:03 +0530246 bool verbose; //!< verbose tracing flag
Brad Bishop5079ac42021-08-19 18:35:06 -0400247 std::chrono::seconds
248 instanceIdExpiryInterval; //!< Instance ID expiration interval
249 uint8_t numRetries; //!< number of request retries
250 std::chrono::milliseconds
251 responseTimeOut; //!< time to wait between each retry
Tom Joseph74f27c72021-05-16 07:58:53 -0700252
Tom Josephe5268cd2021-09-07 13:04:03 +0530253 /** @brief Container for storing the details of the PLDM request
254 * message, handler for the corresponding PLDM response and the
255 * timer object for the Instance ID expiration
Tom Joseph74f27c72021-05-16 07:58:53 -0700256 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400257 using RequestValue =
258 std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
259 std::unique_ptr<phosphor::Timer>>;
Tom Joseph74f27c72021-05-16 07:58:53 -0700260
261 /** @brief Container for storing the PLDM request entries */
262 std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
263
264 /** @brief Container to store information about the request entries to be
265 * removed after the instance ID timer expires
266 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400267 std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
268 RequestKeyHasher>
Tom Joseph74f27c72021-05-16 07:58:53 -0700269 removeRequestContainer;
270
271 /** @brief Remove request entry for which the instance ID expired
272 *
273 * @param[in] key - key for the Request
274 */
275 void removeRequestEntry(RequestKey key)
276 {
277 if (removeRequestContainer.contains(key))
278 {
279 removeRequestContainer[key].reset();
280 requester.markFree(key.eid, key.instanceId);
281 handlers.erase(key);
282 removeRequestContainer.erase(key);
283 }
284 }
285};
286
287} // namespace requester
288
289} // namespace pldm