blob: 6987b16de3a63c3654caa63f95c0035ba3026b27 [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>
Patrick Williamscdbb9e22023-07-13 18:05:40 -050018#include <functional>
Tom Joseph74f27c72021-05-16 07:58:53 -070019#include <memory>
20#include <tuple>
21#include <unordered_map>
22
Riya Dixit49cfb132023-03-02 04:26:53 -060023PHOSPHOR_LOG2_USING;
24
Tom Joseph74f27c72021-05-16 07:58:53 -070025namespace pldm
26{
Tom Joseph74f27c72021-05-16 07:58:53 -070027namespace requester
28{
Tom Joseph74f27c72021-05-16 07:58:53 -070029/** @struct RequestKey
30 *
31 * RequestKey uniquely identifies the PLDM request message to match it with the
32 * response and a combination of MCTP endpoint ID, PLDM instance ID, PLDM type
33 * and PLDM command is the key.
34 */
35struct RequestKey
36{
37 mctp_eid_t eid; //!< MCTP endpoint ID
38 uint8_t instanceId; //!< PLDM instance ID
39 uint8_t type; //!< PLDM type
40 uint8_t command; //!< PLDM command
41
42 bool operator==(const RequestKey& e) const
43 {
44 return ((eid == e.eid) && (instanceId == e.instanceId) &&
45 (type == e.type) && (command == e.command));
46 }
47};
48
49/** @struct RequestKeyHasher
50 *
51 * This is a simple hash function, since the instance ID generator API
52 * generates unique instance IDs for MCTP endpoint ID.
53 */
54struct RequestKeyHasher
55{
56 std::size_t operator()(const RequestKey& key) const
57 {
58 return (key.eid << 24 | key.instanceId << 16 | key.type << 8 |
59 key.command);
60 }
61};
62
Patrick Williamscdbb9e22023-07-13 18:05:40 -050063using ResponseHandler = std::move_only_function<void(
Tom Joseph74f27c72021-05-16 07:58:53 -070064 mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)>;
Tom Joseph74f27c72021-05-16 07:58:53 -070065
66/** @class Handler
67 *
68 * This class handles the lifecycle of the PLDM request message based on the
69 * instance ID expiration interval, number of request retries and the timeout
70 * waiting for a response. The registered response handlers are invoked with
71 * response once the PLDM responder sends the response. If no response is
72 * received within the instance ID expiration interval or any other failure the
73 * response handler is invoked with the empty response.
74 *
75 * @tparam RequestInterface - Request class type
76 */
77template <class RequestInterface>
78class Handler
79{
Tom Joseph74f27c72021-05-16 07:58:53 -070080 public:
81 Handler() = delete;
82 Handler(const Handler&) = delete;
83 Handler(Handler&&) = delete;
84 Handler& operator=(const Handler&) = delete;
85 Handler& operator=(Handler&&) = delete;
86 ~Handler() = default;
87
88 /** @brief Constructor
89 *
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +100090 * @param[in] pldm_transport - PLDM requester
Tom Joseph74f27c72021-05-16 07:58:53 -070091 * @param[in] event - reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +093092 * @param[in] instanceIdDb - reference to an InstanceIdDb
Tom Josephe5268cd2021-09-07 13:04:03 +053093 * @param[in] verbose - verbose tracing flag
Tom Joseph74f27c72021-05-16 07:58:53 -070094 * @param[in] instanceIdExpiryInterval - instance ID expiration interval
95 * @param[in] numRetries - number of request retries
96 * @param[in] responseTimeOut - time to wait between each retry
97 */
98 explicit Handler(
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +100099 PldmTransport* pldmTransport, sdeventplus::Event& event,
100 pldm::InstanceIdDb& instanceIdDb, bool verbose,
Brad Bishop5079ac42021-08-19 18:35:06 -0400101 std::chrono::seconds instanceIdExpiryInterval =
102 std::chrono::seconds(INSTANCE_ID_EXPIRATION_INTERVAL),
Tom Joseph74f27c72021-05-16 07:58:53 -0700103 uint8_t numRetries = static_cast<uint8_t>(NUMBER_OF_REQUEST_RETRIES),
Brad Bishop5079ac42021-08-19 18:35:06 -0400104 std::chrono::milliseconds responseTimeOut =
105 std::chrono::milliseconds(RESPONSE_TIME_OUT)) :
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000106 pldmTransport(pldmTransport),
107 event(event), instanceIdDb(instanceIdDb), verbose(verbose),
Tom Joseph74f27c72021-05-16 07:58:53 -0700108 instanceIdExpiryInterval(instanceIdExpiryInterval),
109 numRetries(numRetries), responseTimeOut(responseTimeOut)
110 {}
111
112 /** @brief Register a PLDM request message
113 *
114 * @param[in] eid - endpoint ID of the remote MCTP endpoint
115 * @param[in] instanceId - instance ID to match request and response
116 * @param[in] type - PLDM type
117 * @param[in] command - PLDM command
118 * @param[in] requestMsg - PLDM request message
119 * @param[in] responseHandler - Response handler for this request
120 *
121 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
122 */
123 int registerRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
124 uint8_t command, pldm::Request&& requestMsg,
125 ResponseHandler&& responseHandler)
126 {
127 RequestKey key{eid, instanceId, type, command};
128
129 auto instanceIdExpiryCallBack = [key, this](void) {
130 if (this->handlers.contains(key))
131 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600132 error(
133 "Response not received for the request, instance ID expired. EID = {EID} INSTANCE_ID = {INST_ID} TYPE = {REQ_KEY_TYPE} COMMAND = {REQ_KEY_CMD}",
134 "EID", (unsigned)key.eid, "INST_ID",
135 (unsigned)key.instanceId, "REQ_KEY_TYPE",
136 (unsigned)key.type, "REQ_KEY_CMD", (unsigned)key.command);
Patrick Williams6da4f912023-05-10 07:50:53 -0500137 auto& [request, responseHandler,
138 timerInstance] = this->handlers[key];
Tom Joseph74f27c72021-05-16 07:58:53 -0700139 request->stop();
140 auto rc = timerInstance->stop();
141 if (rc)
142 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600143 error(
144 "Failed to stop the instance ID expiry timer. RC = {RC}",
145 "RC", static_cast<int>(rc));
Tom Joseph74f27c72021-05-16 07:58:53 -0700146 }
147 // Call response handler with an empty response to indicate no
148 // response
149 responseHandler(key.eid, nullptr, 0);
150 this->removeRequestContainer.emplace(
151 key, std::make_unique<sdeventplus::source::Defer>(
152 event, std::bind(&Handler::removeRequestEntry,
153 this, key)));
154 }
155 else
156 {
157 // This condition is not possible, if a response is received
158 // before the instance ID expiry, then the response handler
159 // is executed and the entry will be removed.
160 assert(false);
161 }
162 };
163
Thu Nguyenb4e8cfd2023-03-23 15:06:44 +0700164 if (handlers.contains(key))
165 {
166 error("The eid:InstanceID {EID}:{IID} is using.", "EID",
167 (unsigned)eid, "IID", (unsigned)instanceId);
168 return PLDM_ERROR;
169 }
170
Tom Joseph74f27c72021-05-16 07:58:53 -0700171 auto request = std::make_unique<RequestInterface>(
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000172 pldmTransport, eid, event, std::move(requestMsg), numRetries,
173 responseTimeOut, verbose);
Tom Joseph74f27c72021-05-16 07:58:53 -0700174 auto timer = std::make_unique<phosphor::Timer>(
175 event.get(), instanceIdExpiryCallBack);
176
177 auto rc = request->start();
Tom Josepha5ed6582021-06-17 22:08:47 -0700178 if (rc)
Tom Joseph74f27c72021-05-16 07:58:53 -0700179 {
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930180 instanceIdDb.free(eid, instanceId);
Riya Dixit49cfb132023-03-02 04:26:53 -0600181 error("Failure to send the PLDM request message");
Tom Joseph74f27c72021-05-16 07:58:53 -0700182 return rc;
183 }
184
185 try
186 {
Brad Bishop5079ac42021-08-19 18:35:06 -0400187 timer->start(duration_cast<std::chrono::microseconds>(
188 instanceIdExpiryInterval));
Tom Joseph74f27c72021-05-16 07:58:53 -0700189 }
190 catch (const std::runtime_error& e)
191 {
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930192 instanceIdDb.free(eid, instanceId);
Riya Dixit49cfb132023-03-02 04:26:53 -0600193 error(
194 "Failed to start the instance ID expiry timer. RC = {ERR_EXCEP}",
195 "ERR_EXCEP", e.what());
Tom Joseph74f27c72021-05-16 07:58:53 -0700196 return PLDM_ERROR;
197 }
198
199 handlers.emplace(key, std::make_tuple(std::move(request),
200 std::move(responseHandler),
201 std::move(timer)));
202 return rc;
203 }
204
205 /** @brief Handle PLDM response message
206 *
207 * @param[in] eid - endpoint ID of the remote MCTP endpoint
208 * @param[in] instanceId - instance ID to match request and response
209 * @param[in] type - PLDM type
210 * @param[in] command - PLDM command
211 * @param[in] response - PLDM response message
212 * @param[in] respMsgLen - length of the response message
213 */
214 void handleResponse(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
215 uint8_t command, const pldm_msg* response,
216 size_t respMsgLen)
217 {
218 RequestKey key{eid, instanceId, type, command};
219 if (handlers.contains(key))
220 {
221 auto& [request, responseHandler, timerInstance] = handlers[key];
222 request->stop();
223 auto rc = timerInstance->stop();
224 if (rc)
225 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600226 error("Failed to stop the instance ID expiry timer. RC = {RC}",
227 "RC", static_cast<int>(rc));
Tom Joseph74f27c72021-05-16 07:58:53 -0700228 }
229 responseHandler(eid, response, respMsgLen);
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930230 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700231 handlers.erase(key);
232 }
233 else
234 {
235 // Got a response for a PLDM request message not registered with the
236 // request handler, so freeing up the instance ID, this can be other
237 // OpenBMC applications relying on PLDM D-Bus apis like
238 // openpower-occ-control and softoff
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930239 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700240 }
241 }
242
243 private:
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000244 PldmTransport* pldmTransport; //!< PLDM transport object
Brad Bishop5079ac42021-08-19 18:35:06 -0400245 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930246 pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930247 bool verbose; //!< verbose tracing flag
Brad Bishop5079ac42021-08-19 18:35:06 -0400248 std::chrono::seconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930249 instanceIdExpiryInterval; //!< Instance ID expiration interval
250 uint8_t numRetries; //!< number of request retries
Brad Bishop5079ac42021-08-19 18:35:06 -0400251 std::chrono::milliseconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930252 responseTimeOut; //!< time to wait between each retry
Tom Joseph74f27c72021-05-16 07:58:53 -0700253
Tom Josephe5268cd2021-09-07 13:04:03 +0530254 /** @brief Container for storing the details of the PLDM request
255 * message, handler for the corresponding PLDM response and the
256 * timer object for the Instance ID expiration
Tom Joseph74f27c72021-05-16 07:58:53 -0700257 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400258 using RequestValue =
259 std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
260 std::unique_ptr<phosphor::Timer>>;
Tom Joseph74f27c72021-05-16 07:58:53 -0700261
262 /** @brief Container for storing the PLDM request entries */
263 std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
264
265 /** @brief Container to store information about the request entries to be
266 * removed after the instance ID timer expires
267 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400268 std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
269 RequestKeyHasher>
Tom Joseph74f27c72021-05-16 07:58:53 -0700270 removeRequestContainer;
271
272 /** @brief Remove request entry for which the instance ID expired
273 *
274 * @param[in] key - key for the Request
275 */
276 void removeRequestEntry(RequestKey key)
277 {
278 if (removeRequestContainer.contains(key))
279 {
280 removeRequestContainer[key].reset();
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930281 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700282 handlers.erase(key);
283 removeRequestContainer.erase(key);
284 }
285 }
286};
287
288} // namespace requester
289
290} // namespace pldm