blob: 727adf9874ac72118d16e9fe9ef013882f069e79 [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"
Andrew Jefferya330b2f2023-05-04 14:55:37 +09306#include "pldmd/instance_id.hpp"
Tom Joseph74f27c72021-05-16 07:58:53 -07007#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>
Riya Dixit49cfb132023-03-02 04:26:53 -060014#include <phosphor-logging/lg2.hpp>
Tom Joseph74f27c72021-05-16 07:58:53 -070015#include <sdbusplus/timer.hpp>
16#include <sdeventplus/event.hpp>
17#include <sdeventplus/source/event.hpp>
18
19#include <cassert>
20#include <chrono>
21#include <memory>
22#include <tuple>
23#include <unordered_map>
24
Riya Dixit49cfb132023-03-02 04:26:53 -060025PHOSPHOR_LOG2_USING;
26
Tom Joseph74f27c72021-05-16 07:58:53 -070027namespace pldm
28{
Tom Joseph74f27c72021-05-16 07:58:53 -070029namespace requester
30{
Tom Joseph74f27c72021-05-16 07:58:53 -070031/** @struct RequestKey
32 *
33 * RequestKey uniquely identifies the PLDM request message to match it with the
34 * response and a combination of MCTP endpoint ID, PLDM instance ID, PLDM type
35 * and PLDM command is the key.
36 */
37struct RequestKey
38{
39 mctp_eid_t eid; //!< MCTP endpoint ID
40 uint8_t instanceId; //!< PLDM instance ID
41 uint8_t type; //!< PLDM type
42 uint8_t command; //!< PLDM command
43
44 bool operator==(const RequestKey& e) const
45 {
46 return ((eid == e.eid) && (instanceId == e.instanceId) &&
47 (type == e.type) && (command == e.command));
48 }
49};
50
51/** @struct RequestKeyHasher
52 *
53 * This is a simple hash function, since the instance ID generator API
54 * generates unique instance IDs for MCTP endpoint ID.
55 */
56struct RequestKeyHasher
57{
58 std::size_t operator()(const RequestKey& key) const
59 {
60 return (key.eid << 24 | key.instanceId << 16 | key.type << 8 |
61 key.command);
62 }
63};
64
65using ResponseHandler = fu2::unique_function<void(
66 mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)>;
Tom Joseph74f27c72021-05-16 07:58:53 -070067
68/** @class Handler
69 *
70 * This class handles the lifecycle of the PLDM request message based on the
71 * instance ID expiration interval, number of request retries and the timeout
72 * waiting for a response. The registered response handlers are invoked with
73 * response once the PLDM responder sends the response. If no response is
74 * received within the instance ID expiration interval or any other failure the
75 * response handler is invoked with the empty response.
76 *
77 * @tparam RequestInterface - Request class type
78 */
79template <class RequestInterface>
80class Handler
81{
Tom Joseph74f27c72021-05-16 07:58:53 -070082 public:
83 Handler() = delete;
84 Handler(const Handler&) = delete;
85 Handler(Handler&&) = delete;
86 Handler& operator=(const Handler&) = delete;
87 Handler& operator=(Handler&&) = delete;
88 ~Handler() = default;
89
90 /** @brief Constructor
91 *
92 * @param[in] fd - fd of MCTP communications socket
93 * @param[in] event - reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +093094 * @param[in] instanceIdDb - reference to an InstanceIdDb
Manojkiran Eda9fffea22021-10-27 16:03:27 +053095 * @param[in] currentSendbuffSize - current send buffer size
Tom Josephe5268cd2021-09-07 13:04:03 +053096 * @param[in] verbose - verbose tracing flag
Tom Joseph74f27c72021-05-16 07:58:53 -070097 * @param[in] instanceIdExpiryInterval - instance ID expiration interval
98 * @param[in] numRetries - number of request retries
99 * @param[in] responseTimeOut - time to wait between each retry
100 */
101 explicit Handler(
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930102 int fd, sdeventplus::Event& event, pldm::InstanceIdDb& instanceIdDb,
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530103 int currentSendbuffSize, bool verbose,
Brad Bishop5079ac42021-08-19 18:35:06 -0400104 std::chrono::seconds instanceIdExpiryInterval =
105 std::chrono::seconds(INSTANCE_ID_EXPIRATION_INTERVAL),
Tom Joseph74f27c72021-05-16 07:58:53 -0700106 uint8_t numRetries = static_cast<uint8_t>(NUMBER_OF_REQUEST_RETRIES),
Brad Bishop5079ac42021-08-19 18:35:06 -0400107 std::chrono::milliseconds responseTimeOut =
108 std::chrono::milliseconds(RESPONSE_TIME_OUT)) :
Tom Joseph74f27c72021-05-16 07:58:53 -0700109 fd(fd),
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930110 event(event), instanceIdDb(instanceIdDb),
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530111 currentSendbuffSize(currentSendbuffSize), verbose(verbose),
Tom Joseph74f27c72021-05-16 07:58:53 -0700112 instanceIdExpiryInterval(instanceIdExpiryInterval),
113 numRetries(numRetries), responseTimeOut(responseTimeOut)
114 {}
115
116 /** @brief Register a PLDM request message
117 *
118 * @param[in] eid - endpoint ID of the remote MCTP endpoint
119 * @param[in] instanceId - instance ID to match request and response
120 * @param[in] type - PLDM type
121 * @param[in] command - PLDM command
122 * @param[in] requestMsg - PLDM request message
123 * @param[in] responseHandler - Response handler for this request
124 *
125 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
126 */
127 int registerRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
128 uint8_t command, pldm::Request&& requestMsg,
129 ResponseHandler&& responseHandler)
130 {
131 RequestKey key{eid, instanceId, type, command};
132
133 auto instanceIdExpiryCallBack = [key, this](void) {
134 if (this->handlers.contains(key))
135 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600136 error(
137 "Response not received for the request, instance ID expired. EID = {EID} INSTANCE_ID = {INST_ID} TYPE = {REQ_KEY_TYPE} COMMAND = {REQ_KEY_CMD}",
138 "EID", (unsigned)key.eid, "INST_ID",
139 (unsigned)key.instanceId, "REQ_KEY_TYPE",
140 (unsigned)key.type, "REQ_KEY_CMD", (unsigned)key.command);
Patrick Williams6da4f912023-05-10 07:50:53 -0500141 auto& [request, responseHandler,
142 timerInstance] = this->handlers[key];
Tom Joseph74f27c72021-05-16 07:58:53 -0700143 request->stop();
144 auto rc = timerInstance->stop();
145 if (rc)
146 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600147 error(
148 "Failed to stop the instance ID expiry timer. RC = {RC}",
149 "RC", static_cast<int>(rc));
Tom Joseph74f27c72021-05-16 07:58:53 -0700150 }
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
Thu Nguyenb4e8cfd2023-03-23 15:06:44 +0700168 if (handlers.contains(key))
169 {
170 error("The eid:InstanceID {EID}:{IID} is using.", "EID",
171 (unsigned)eid, "IID", (unsigned)instanceId);
172 return PLDM_ERROR;
173 }
174
Tom Joseph74f27c72021-05-16 07:58:53 -0700175 auto request = std::make_unique<RequestInterface>(
Tom Josephe5268cd2021-09-07 13:04:03 +0530176 fd, eid, event, std::move(requestMsg), numRetries, responseTimeOut,
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530177 currentSendbuffSize, verbose);
Tom Joseph74f27c72021-05-16 07:58:53 -0700178 auto timer = std::make_unique<phosphor::Timer>(
179 event.get(), instanceIdExpiryCallBack);
180
181 auto rc = request->start();
Tom Josepha5ed6582021-06-17 22:08:47 -0700182 if (rc)
Tom Joseph74f27c72021-05-16 07:58:53 -0700183 {
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930184 instanceIdDb.free(eid, instanceId);
Riya Dixit49cfb132023-03-02 04:26:53 -0600185 error("Failure to send the PLDM request message");
Tom Joseph74f27c72021-05-16 07:58:53 -0700186 return rc;
187 }
188
189 try
190 {
Brad Bishop5079ac42021-08-19 18:35:06 -0400191 timer->start(duration_cast<std::chrono::microseconds>(
192 instanceIdExpiryInterval));
Tom Joseph74f27c72021-05-16 07:58:53 -0700193 }
194 catch (const std::runtime_error& e)
195 {
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930196 instanceIdDb.free(eid, instanceId);
Riya Dixit49cfb132023-03-02 04:26:53 -0600197 error(
198 "Failed to start the instance ID expiry timer. RC = {ERR_EXCEP}",
199 "ERR_EXCEP", e.what());
Tom Joseph74f27c72021-05-16 07:58:53 -0700200 return PLDM_ERROR;
201 }
202
203 handlers.emplace(key, std::make_tuple(std::move(request),
204 std::move(responseHandler),
205 std::move(timer)));
206 return rc;
207 }
208
209 /** @brief Handle PLDM response message
210 *
211 * @param[in] eid - endpoint ID of the remote MCTP endpoint
212 * @param[in] instanceId - instance ID to match request and response
213 * @param[in] type - PLDM type
214 * @param[in] command - PLDM command
215 * @param[in] response - PLDM response message
216 * @param[in] respMsgLen - length of the response message
217 */
218 void handleResponse(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
219 uint8_t command, const pldm_msg* response,
220 size_t respMsgLen)
221 {
222 RequestKey key{eid, instanceId, type, command};
223 if (handlers.contains(key))
224 {
225 auto& [request, responseHandler, timerInstance] = handlers[key];
226 request->stop();
227 auto rc = timerInstance->stop();
228 if (rc)
229 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600230 error("Failed to stop the instance ID expiry timer. RC = {RC}",
231 "RC", static_cast<int>(rc));
Tom Joseph74f27c72021-05-16 07:58:53 -0700232 }
233 responseHandler(eid, response, respMsgLen);
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930234 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700235 handlers.erase(key);
236 }
237 else
238 {
239 // Got a response for a PLDM request message not registered with the
240 // request handler, so freeing up the instance ID, this can be other
241 // OpenBMC applications relying on PLDM D-Bus apis like
242 // openpower-occ-control and softoff
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930243 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700244 }
245 }
246
247 private:
Brad Bishop5079ac42021-08-19 18:35:06 -0400248 int fd; //!< file descriptor of MCTP communications socket
249 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930250 pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
251 int currentSendbuffSize; //!< current Send Buffer size
252 bool verbose; //!< verbose tracing flag
Brad Bishop5079ac42021-08-19 18:35:06 -0400253 std::chrono::seconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930254 instanceIdExpiryInterval; //!< Instance ID expiration interval
255 uint8_t numRetries; //!< number of request retries
Brad Bishop5079ac42021-08-19 18:35:06 -0400256 std::chrono::milliseconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930257 responseTimeOut; //!< time to wait between each retry
Tom Joseph74f27c72021-05-16 07:58:53 -0700258
Tom Josephe5268cd2021-09-07 13:04:03 +0530259 /** @brief Container for storing the details of the PLDM request
260 * message, handler for the corresponding PLDM response and the
261 * timer object for the Instance ID expiration
Tom Joseph74f27c72021-05-16 07:58:53 -0700262 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400263 using RequestValue =
264 std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
265 std::unique_ptr<phosphor::Timer>>;
Tom Joseph74f27c72021-05-16 07:58:53 -0700266
267 /** @brief Container for storing the PLDM request entries */
268 std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
269
270 /** @brief Container to store information about the request entries to be
271 * removed after the instance ID timer expires
272 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400273 std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
274 RequestKeyHasher>
Tom Joseph74f27c72021-05-16 07:58:53 -0700275 removeRequestContainer;
276
277 /** @brief Remove request entry for which the instance ID expired
278 *
279 * @param[in] key - key for the Request
280 */
281 void removeRequestEntry(RequestKey key)
282 {
283 if (removeRequestContainer.contains(key))
284 {
285 removeRequestContainer[key].reset();
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930286 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700287 handlers.erase(key);
288 removeRequestContainer.erase(key);
289 }
290 }
291};
292
293} // namespace requester
294
295} // namespace pldm