blob: 3b1eca55cb50f07eba400e39f9b511633fb6ad90 [file] [log] [blame]
Tom Joseph74f27c72021-05-16 07:58:53 -07001#pragma once
2
Andrew Jeffery2abbce72023-10-18 10:17:35 +10303#include "common/instance_id.hpp"
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +10004#include "common/transport.hpp"
Tom Joseph74f27c72021-05-16 07:58:53 -07005#include "common/types.hpp"
Tom Joseph74f27c72021-05-16 07:58:53 -07006#include "request.hpp"
7
George Liuc453e162022-12-21 17:16:23 +08008#include <libpldm/base.h>
Manojkiran Eda9fffea22021-10-27 16:03:27 +05309#include <sys/socket.h>
10
Riya Dixit49cfb132023-03-02 04:26:53 -060011#include <phosphor-logging/lg2.hpp>
Tom Joseph74f27c72021-05-16 07:58:53 -070012#include <sdbusplus/timer.hpp>
13#include <sdeventplus/event.hpp>
14#include <sdeventplus/source/event.hpp>
15
16#include <cassert>
17#include <chrono>
Thu Nguyen4ddee3a2023-08-03 08:20:03 +070018#include <deque>
Patrick Williamscdbb9e22023-07-13 18:05:40 -050019#include <functional>
Tom Joseph74f27c72021-05-16 07:58:53 -070020#include <memory>
Thu Nguyen4ddee3a2023-08-03 08:20:03 +070021#include <mutex>
22#include <queue>
Tom Joseph74f27c72021-05-16 07:58:53 -070023#include <tuple>
24#include <unordered_map>
25
Riya Dixit49cfb132023-03-02 04:26:53 -060026PHOSPHOR_LOG2_USING;
27
Tom Joseph74f27c72021-05-16 07:58:53 -070028namespace pldm
29{
Tom Joseph74f27c72021-05-16 07:58:53 -070030namespace requester
31{
Tom Joseph74f27c72021-05-16 07:58:53 -070032/** @struct RequestKey
33 *
34 * RequestKey uniquely identifies the PLDM request message to match it with the
35 * response and a combination of MCTP endpoint ID, PLDM instance ID, PLDM type
36 * and PLDM command is the key.
37 */
38struct RequestKey
39{
40 mctp_eid_t eid; //!< MCTP endpoint ID
41 uint8_t instanceId; //!< PLDM instance ID
42 uint8_t type; //!< PLDM type
43 uint8_t command; //!< PLDM command
44
45 bool operator==(const RequestKey& e) const
46 {
47 return ((eid == e.eid) && (instanceId == e.instanceId) &&
48 (type == e.type) && (command == e.command));
49 }
50};
51
52/** @struct RequestKeyHasher
53 *
54 * This is a simple hash function, since the instance ID generator API
55 * generates unique instance IDs for MCTP endpoint ID.
56 */
57struct RequestKeyHasher
58{
59 std::size_t operator()(const RequestKey& key) const
60 {
61 return (key.eid << 24 | key.instanceId << 16 | key.type << 8 |
62 key.command);
63 }
64};
65
Thu Nguyen4ddee3a2023-08-03 08:20:03 +070066using ResponseHandler = std::function<void(
Tom Joseph74f27c72021-05-16 07:58:53 -070067 mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)>;
Tom Joseph74f27c72021-05-16 07:58:53 -070068
Thu Nguyen4ddee3a2023-08-03 08:20:03 +070069/** @struct RegisteredRequest
70 *
71 * This struct is used to store the registered request to one endpoint.
72 */
73struct RegisteredRequest
74{
75 RequestKey key; //!< Responder MCTP endpoint ID
76 std::vector<uint8_t> reqMsg; //!< Request messages queue
77 ResponseHandler responseHandler; //!< Waiting for response flag
78};
79
80/** @struct EndpointMessageQueue
81 *
82 * This struct is used to save the list of request messages of one endpoint and
83 * the existing of the request message to the endpoint with its' EID.
84 */
85struct EndpointMessageQueue
86{
87 mctp_eid_t eid; //!< Responder MCTP endpoint ID
88 std::deque<std::shared_ptr<RegisteredRequest>> requestQueue; //!< Queue
89 bool activeRequest; //!< Waiting for response flag
90
91 bool operator==(const mctp_eid_t& mctpEid) const
92 {
93 return (eid == mctpEid);
94 }
95};
96
Tom Joseph74f27c72021-05-16 07:58:53 -070097/** @class Handler
98 *
99 * This class handles the lifecycle of the PLDM request message based on the
100 * instance ID expiration interval, number of request retries and the timeout
101 * waiting for a response. The registered response handlers are invoked with
102 * response once the PLDM responder sends the response. If no response is
103 * received within the instance ID expiration interval or any other failure the
104 * response handler is invoked with the empty response.
105 *
106 * @tparam RequestInterface - Request class type
107 */
108template <class RequestInterface>
109class Handler
110{
Tom Joseph74f27c72021-05-16 07:58:53 -0700111 public:
112 Handler() = delete;
113 Handler(const Handler&) = delete;
114 Handler(Handler&&) = delete;
115 Handler& operator=(const Handler&) = delete;
116 Handler& operator=(Handler&&) = delete;
117 ~Handler() = default;
118
119 /** @brief Constructor
120 *
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000121 * @param[in] pldm_transport - PLDM requester
Tom Joseph74f27c72021-05-16 07:58:53 -0700122 * @param[in] event - reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930123 * @param[in] instanceIdDb - reference to an InstanceIdDb
Tom Josephe5268cd2021-09-07 13:04:03 +0530124 * @param[in] verbose - verbose tracing flag
Tom Joseph74f27c72021-05-16 07:58:53 -0700125 * @param[in] instanceIdExpiryInterval - instance ID expiration interval
126 * @param[in] numRetries - number of request retries
127 * @param[in] responseTimeOut - time to wait between each retry
128 */
129 explicit Handler(
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000130 PldmTransport* pldmTransport, sdeventplus::Event& event,
131 pldm::InstanceIdDb& instanceIdDb, bool verbose,
Brad Bishop5079ac42021-08-19 18:35:06 -0400132 std::chrono::seconds instanceIdExpiryInterval =
133 std::chrono::seconds(INSTANCE_ID_EXPIRATION_INTERVAL),
Tom Joseph74f27c72021-05-16 07:58:53 -0700134 uint8_t numRetries = static_cast<uint8_t>(NUMBER_OF_REQUEST_RETRIES),
Brad Bishop5079ac42021-08-19 18:35:06 -0400135 std::chrono::milliseconds responseTimeOut =
136 std::chrono::milliseconds(RESPONSE_TIME_OUT)) :
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000137 pldmTransport(pldmTransport),
138 event(event), instanceIdDb(instanceIdDb), verbose(verbose),
Tom Joseph74f27c72021-05-16 07:58:53 -0700139 instanceIdExpiryInterval(instanceIdExpiryInterval),
140 numRetries(numRetries), responseTimeOut(responseTimeOut)
141 {}
142
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700143 void instanceIdExpiryCallBack(RequestKey key)
144 {
145 auto eid = key.eid;
146 if (this->handlers.contains(key))
147 {
Riya Dixit087a7512024-04-06 14:28:08 -0500148 info(
149 "Instance ID expiry for EID '{EID}' using InstanceID '{INSTANCEID}'",
150 "EID", (unsigned)key.eid, "INSTANCEID",
151 (unsigned)key.instanceId);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700152 auto& [request, responseHandler,
153 timerInstance] = this->handlers[key];
154 request->stop();
155 auto rc = timerInstance->stop();
156 if (rc)
157 {
Riya Dixit087a7512024-04-06 14:28:08 -0500158 error(
159 "Failed to stop the instance ID expiry timer, response code '{RC}'",
160 "RC", static_cast<int>(rc));
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700161 }
162 // Call response handler with an empty response to indicate no
163 // response
164 responseHandler(eid, nullptr, 0);
165 this->removeRequestContainer.emplace(
166 key,
167 std::make_unique<sdeventplus::source::Defer>(
168 event, std::bind(&Handler::removeRequestEntry, this, key)));
169 endpointMessageQueues[eid]->activeRequest = false;
170
171 /* try to send new request if the endpoint is free */
172 pollEndpointQueue(eid);
173 }
174 else
175 {
176 // This condition is not possible, if a response is received
177 // before the instance ID expiry, then the response handler
178 // is executed and the entry will be removed.
179 assert(false);
180 }
181 }
182
183 /** @brief Send the remaining PLDM request messages in endpoint queue
184 *
185 * @param[in] eid - endpoint ID of the remote MCTP endpoint
186 */
187 int pollEndpointQueue(mctp_eid_t eid)
188 {
189 if (endpointMessageQueues[eid]->activeRequest ||
190 endpointMessageQueues[eid]->requestQueue.empty())
191 {
192 return PLDM_SUCCESS;
193 }
194
195 endpointMessageQueues[eid]->activeRequest = true;
196 auto requestMsg = endpointMessageQueues[eid]->requestQueue.front();
197 endpointMessageQueues[eid]->requestQueue.pop_front();
198
199 auto request = std::make_unique<RequestInterface>(
200 pldmTransport, requestMsg->key.eid, event,
201 std::move(requestMsg->reqMsg), numRetries, responseTimeOut,
202 verbose);
Patrick Williams35535cf2023-12-05 12:45:02 -0600203 auto timer = std::make_unique<sdbusplus::Timer>(
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700204 event.get(), std::bind(&Handler::instanceIdExpiryCallBack, this,
205 requestMsg->key));
206
207 auto rc = request->start();
208 if (rc)
209 {
210 instanceIdDb.free(requestMsg->key.eid, requestMsg->key.instanceId);
Riya Dixit087a7512024-04-06 14:28:08 -0500211 error(
212 "Failure to send the PLDM request message for polling endpoint queue, response code '{RC}'",
213 "RC", rc);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700214 endpointMessageQueues[eid]->activeRequest = false;
215 return rc;
216 }
217
218 try
219 {
220 timer->start(duration_cast<std::chrono::microseconds>(
221 instanceIdExpiryInterval));
222 }
223 catch (const std::runtime_error& e)
224 {
225 instanceIdDb.free(requestMsg->key.eid, requestMsg->key.instanceId);
226 error(
Riya Dixit087a7512024-04-06 14:28:08 -0500227 "Failed to start the instance ID expiry timer, error - {ERROR}",
228 "ERROR", e);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700229 endpointMessageQueues[eid]->activeRequest = false;
230 return PLDM_ERROR;
231 }
232
233 handlers.emplace(requestMsg->key,
234 std::make_tuple(std::move(request),
235 std::move(requestMsg->responseHandler),
236 std::move(timer)));
237 return PLDM_SUCCESS;
238 }
239
Tom Joseph74f27c72021-05-16 07:58:53 -0700240 /** @brief Register a PLDM request message
241 *
242 * @param[in] eid - endpoint ID of the remote MCTP endpoint
243 * @param[in] instanceId - instance ID to match request and response
244 * @param[in] type - PLDM type
245 * @param[in] command - PLDM command
246 * @param[in] requestMsg - PLDM request message
247 * @param[in] responseHandler - Response handler for this request
248 *
249 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
250 */
251 int registerRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
252 uint8_t command, pldm::Request&& requestMsg,
253 ResponseHandler&& responseHandler)
254 {
255 RequestKey key{eid, instanceId, type, command};
256
Thu Nguyenb4e8cfd2023-03-23 15:06:44 +0700257 if (handlers.contains(key))
258 {
Riya Dixit087a7512024-04-06 14:28:08 -0500259 error(
260 "Register request for EID '{EID}' is using InstanceID '{INSTANCEID}'",
261 "EID", (unsigned)eid, "INSTANCEID", (unsigned)instanceId);
Thu Nguyenb4e8cfd2023-03-23 15:06:44 +0700262 return PLDM_ERROR;
263 }
264
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700265 auto inputRequest = std::make_shared<RegisteredRequest>(
266 key, std::move(requestMsg), std::move(responseHandler));
267 if (endpointMessageQueues.contains(eid))
Tom Joseph74f27c72021-05-16 07:58:53 -0700268 {
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700269 endpointMessageQueues[eid]->requestQueue.push_back(inputRequest);
270 }
271 else
272 {
273 std::deque<std::shared_ptr<RegisteredRequest>> reqQueue;
274 reqQueue.push_back(inputRequest);
275 endpointMessageQueues[eid] =
276 std::make_shared<EndpointMessageQueue>(eid, reqQueue, false);
Tom Joseph74f27c72021-05-16 07:58:53 -0700277 }
278
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700279 /* try to send new request if the endpoint is free */
280 pollEndpointQueue(eid);
Tom Joseph74f27c72021-05-16 07:58:53 -0700281
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700282 return PLDM_SUCCESS;
Tom Joseph74f27c72021-05-16 07:58:53 -0700283 }
284
285 /** @brief Handle PLDM response message
286 *
287 * @param[in] eid - endpoint ID of the remote MCTP endpoint
288 * @param[in] instanceId - instance ID to match request and response
289 * @param[in] type - PLDM type
290 * @param[in] command - PLDM command
291 * @param[in] response - PLDM response message
292 * @param[in] respMsgLen - length of the response message
293 */
294 void handleResponse(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
295 uint8_t command, const pldm_msg* response,
296 size_t respMsgLen)
297 {
298 RequestKey key{eid, instanceId, type, command};
299 if (handlers.contains(key))
300 {
301 auto& [request, responseHandler, timerInstance] = handlers[key];
302 request->stop();
303 auto rc = timerInstance->stop();
304 if (rc)
305 {
Riya Dixit087a7512024-04-06 14:28:08 -0500306 error(
307 "Failed to stop the instance ID expiry timer, response code '{RC}'",
308 "RC", static_cast<int>(rc));
Tom Joseph74f27c72021-05-16 07:58:53 -0700309 }
310 responseHandler(eid, response, respMsgLen);
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930311 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700312 handlers.erase(key);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700313
314 endpointMessageQueues[eid]->activeRequest = false;
315 /* try to send new request if the endpoint is free */
316 pollEndpointQueue(eid);
Tom Joseph74f27c72021-05-16 07:58:53 -0700317 }
318 else
319 {
320 // Got a response for a PLDM request message not registered with the
321 // request handler, so freeing up the instance ID, this can be other
322 // OpenBMC applications relying on PLDM D-Bus apis like
323 // openpower-occ-control and softoff
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930324 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700325 }
326 }
327
328 private:
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000329 PldmTransport* pldmTransport; //!< PLDM transport object
Brad Bishop5079ac42021-08-19 18:35:06 -0400330 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930331 pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930332 bool verbose; //!< verbose tracing flag
Brad Bishop5079ac42021-08-19 18:35:06 -0400333 std::chrono::seconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930334 instanceIdExpiryInterval; //!< Instance ID expiration interval
335 uint8_t numRetries; //!< number of request retries
Brad Bishop5079ac42021-08-19 18:35:06 -0400336 std::chrono::milliseconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930337 responseTimeOut; //!< time to wait between each retry
Tom Joseph74f27c72021-05-16 07:58:53 -0700338
Tom Josephe5268cd2021-09-07 13:04:03 +0530339 /** @brief Container for storing the details of the PLDM request
340 * message, handler for the corresponding PLDM response and the
341 * timer object for the Instance ID expiration
Tom Joseph74f27c72021-05-16 07:58:53 -0700342 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400343 using RequestValue =
344 std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
Patrick Williams35535cf2023-12-05 12:45:02 -0600345 std::unique_ptr<sdbusplus::Timer>>;
Tom Joseph74f27c72021-05-16 07:58:53 -0700346
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700347 // Manage the requests of responders base on MCTP EID
348 std::map<mctp_eid_t, std::shared_ptr<EndpointMessageQueue>>
349 endpointMessageQueues;
350
Tom Joseph74f27c72021-05-16 07:58:53 -0700351 /** @brief Container for storing the PLDM request entries */
352 std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
353
354 /** @brief Container to store information about the request entries to be
355 * removed after the instance ID timer expires
356 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400357 std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
358 RequestKeyHasher>
Tom Joseph74f27c72021-05-16 07:58:53 -0700359 removeRequestContainer;
360
361 /** @brief Remove request entry for which the instance ID expired
362 *
363 * @param[in] key - key for the Request
364 */
365 void removeRequestEntry(RequestKey key)
366 {
367 if (removeRequestContainer.contains(key))
368 {
369 removeRequestContainer[key].reset();
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930370 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700371 handlers.erase(key);
372 removeRequestContainer.erase(key);
373 }
374 }
375};
376
377} // namespace requester
378
379} // namespace pldm