blob: b03e05dec45b93564abddd063cb1c7b5d4b8f9ee [file] [log] [blame]
Tom Joseph74f27c72021-05-16 07:58:53 -07001#pragma once
2
Tom Joseph74f27c72021-05-16 07:58:53 -07003#include "common/types.hpp"
Andrew Jefferya330b2f2023-05-04 14:55:37 +09304#include "pldmd/instance_id.hpp"
Tom Joseph74f27c72021-05-16 07:58:53 -07005#include "request.hpp"
6
George Liuc453e162022-12-21 17:16:23 +08007#include <libpldm/base.h>
8#include <libpldm/pldm.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 *
90 * @param[in] fd - fd of MCTP communications socket
91 * @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
Manojkiran Eda9fffea22021-10-27 16:03:27 +053093 * @param[in] currentSendbuffSize - current send buffer size
Tom Josephe5268cd2021-09-07 13:04:03 +053094 * @param[in] verbose - verbose tracing flag
Tom Joseph74f27c72021-05-16 07:58:53 -070095 * @param[in] instanceIdExpiryInterval - instance ID expiration interval
96 * @param[in] numRetries - number of request retries
97 * @param[in] responseTimeOut - time to wait between each retry
98 */
99 explicit Handler(
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930100 int fd, sdeventplus::Event& event, pldm::InstanceIdDb& instanceIdDb,
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530101 int currentSendbuffSize, bool verbose,
Brad Bishop5079ac42021-08-19 18:35:06 -0400102 std::chrono::seconds instanceIdExpiryInterval =
103 std::chrono::seconds(INSTANCE_ID_EXPIRATION_INTERVAL),
Tom Joseph74f27c72021-05-16 07:58:53 -0700104 uint8_t numRetries = static_cast<uint8_t>(NUMBER_OF_REQUEST_RETRIES),
Brad Bishop5079ac42021-08-19 18:35:06 -0400105 std::chrono::milliseconds responseTimeOut =
106 std::chrono::milliseconds(RESPONSE_TIME_OUT)) :
Tom Joseph74f27c72021-05-16 07:58:53 -0700107 fd(fd),
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930108 event(event), instanceIdDb(instanceIdDb),
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530109 currentSendbuffSize(currentSendbuffSize), verbose(verbose),
Tom Joseph74f27c72021-05-16 07:58:53 -0700110 instanceIdExpiryInterval(instanceIdExpiryInterval),
111 numRetries(numRetries), responseTimeOut(responseTimeOut)
112 {}
113
114 /** @brief Register a PLDM request message
115 *
116 * @param[in] eid - endpoint ID of the remote MCTP endpoint
117 * @param[in] instanceId - instance ID to match request and response
118 * @param[in] type - PLDM type
119 * @param[in] command - PLDM command
120 * @param[in] requestMsg - PLDM request message
121 * @param[in] responseHandler - Response handler for this request
122 *
123 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
124 */
125 int registerRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
126 uint8_t command, pldm::Request&& requestMsg,
127 ResponseHandler&& responseHandler)
128 {
129 RequestKey key{eid, instanceId, type, command};
130
131 auto instanceIdExpiryCallBack = [key, this](void) {
132 if (this->handlers.contains(key))
133 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600134 error(
135 "Response not received for the request, instance ID expired. EID = {EID} INSTANCE_ID = {INST_ID} TYPE = {REQ_KEY_TYPE} COMMAND = {REQ_KEY_CMD}",
136 "EID", (unsigned)key.eid, "INST_ID",
137 (unsigned)key.instanceId, "REQ_KEY_TYPE",
138 (unsigned)key.type, "REQ_KEY_CMD", (unsigned)key.command);
Patrick Williams6da4f912023-05-10 07:50:53 -0500139 auto& [request, responseHandler,
140 timerInstance] = this->handlers[key];
Tom Joseph74f27c72021-05-16 07:58:53 -0700141 request->stop();
142 auto rc = timerInstance->stop();
143 if (rc)
144 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600145 error(
146 "Failed to stop the instance ID expiry timer. RC = {RC}",
147 "RC", static_cast<int>(rc));
Tom Joseph74f27c72021-05-16 07:58:53 -0700148 }
149 // Call response handler with an empty response to indicate no
150 // response
151 responseHandler(key.eid, nullptr, 0);
152 this->removeRequestContainer.emplace(
153 key, std::make_unique<sdeventplus::source::Defer>(
154 event, std::bind(&Handler::removeRequestEntry,
155 this, key)));
156 }
157 else
158 {
159 // This condition is not possible, if a response is received
160 // before the instance ID expiry, then the response handler
161 // is executed and the entry will be removed.
162 assert(false);
163 }
164 };
165
Thu Nguyenb4e8cfd2023-03-23 15:06:44 +0700166 if (handlers.contains(key))
167 {
168 error("The eid:InstanceID {EID}:{IID} is using.", "EID",
169 (unsigned)eid, "IID", (unsigned)instanceId);
170 return PLDM_ERROR;
171 }
172
Tom Joseph74f27c72021-05-16 07:58:53 -0700173 auto request = std::make_unique<RequestInterface>(
Tom Josephe5268cd2021-09-07 13:04:03 +0530174 fd, eid, event, std::move(requestMsg), numRetries, responseTimeOut,
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530175 currentSendbuffSize, verbose);
Tom Joseph74f27c72021-05-16 07:58:53 -0700176 auto timer = std::make_unique<phosphor::Timer>(
177 event.get(), instanceIdExpiryCallBack);
178
179 auto rc = request->start();
Tom Josepha5ed6582021-06-17 22:08:47 -0700180 if (rc)
Tom Joseph74f27c72021-05-16 07:58:53 -0700181 {
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930182 instanceIdDb.free(eid, instanceId);
Riya Dixit49cfb132023-03-02 04:26:53 -0600183 error("Failure to send the PLDM request message");
Tom Joseph74f27c72021-05-16 07:58:53 -0700184 return rc;
185 }
186
187 try
188 {
Brad Bishop5079ac42021-08-19 18:35:06 -0400189 timer->start(duration_cast<std::chrono::microseconds>(
190 instanceIdExpiryInterval));
Tom Joseph74f27c72021-05-16 07:58:53 -0700191 }
192 catch (const std::runtime_error& e)
193 {
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930194 instanceIdDb.free(eid, instanceId);
Riya Dixit49cfb132023-03-02 04:26:53 -0600195 error(
196 "Failed to start the instance ID expiry timer. RC = {ERR_EXCEP}",
197 "ERR_EXCEP", e.what());
Tom Joseph74f27c72021-05-16 07:58:53 -0700198 return PLDM_ERROR;
199 }
200
201 handlers.emplace(key, std::make_tuple(std::move(request),
202 std::move(responseHandler),
203 std::move(timer)));
204 return rc;
205 }
206
207 /** @brief Handle PLDM response message
208 *
209 * @param[in] eid - endpoint ID of the remote MCTP endpoint
210 * @param[in] instanceId - instance ID to match request and response
211 * @param[in] type - PLDM type
212 * @param[in] command - PLDM command
213 * @param[in] response - PLDM response message
214 * @param[in] respMsgLen - length of the response message
215 */
216 void handleResponse(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
217 uint8_t command, const pldm_msg* response,
218 size_t respMsgLen)
219 {
220 RequestKey key{eid, instanceId, type, command};
221 if (handlers.contains(key))
222 {
223 auto& [request, responseHandler, timerInstance] = handlers[key];
224 request->stop();
225 auto rc = timerInstance->stop();
226 if (rc)
227 {
Riya Dixit49cfb132023-03-02 04:26:53 -0600228 error("Failed to stop the instance ID expiry timer. RC = {RC}",
229 "RC", static_cast<int>(rc));
Tom Joseph74f27c72021-05-16 07:58:53 -0700230 }
231 responseHandler(eid, response, respMsgLen);
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930232 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700233 handlers.erase(key);
234 }
235 else
236 {
237 // Got a response for a PLDM request message not registered with the
238 // request handler, so freeing up the instance ID, this can be other
239 // OpenBMC applications relying on PLDM D-Bus apis like
240 // openpower-occ-control and softoff
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930241 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700242 }
243 }
244
245 private:
Brad Bishop5079ac42021-08-19 18:35:06 -0400246 int fd; //!< file descriptor of MCTP communications socket
247 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930248 pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
249 int currentSendbuffSize; //!< current Send Buffer size
250 bool verbose; //!< verbose tracing flag
Brad Bishop5079ac42021-08-19 18:35:06 -0400251 std::chrono::seconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930252 instanceIdExpiryInterval; //!< Instance ID expiration interval
253 uint8_t numRetries; //!< number of request retries
Brad Bishop5079ac42021-08-19 18:35:06 -0400254 std::chrono::milliseconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930255 responseTimeOut; //!< time to wait between each retry
Tom Joseph74f27c72021-05-16 07:58:53 -0700256
Tom Josephe5268cd2021-09-07 13:04:03 +0530257 /** @brief Container for storing the details of the PLDM request
258 * message, handler for the corresponding PLDM response and the
259 * timer object for the Instance ID expiration
Tom Joseph74f27c72021-05-16 07:58:53 -0700260 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400261 using RequestValue =
262 std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
263 std::unique_ptr<phosphor::Timer>>;
Tom Joseph74f27c72021-05-16 07:58:53 -0700264
265 /** @brief Container for storing the PLDM request entries */
266 std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
267
268 /** @brief Container to store information about the request entries to be
269 * removed after the instance ID timer expires
270 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400271 std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
272 RequestKeyHasher>
Tom Joseph74f27c72021-05-16 07:58:53 -0700273 removeRequestContainer;
274
275 /** @brief Remove request entry for which the instance ID expired
276 *
277 * @param[in] key - key for the Request
278 */
279 void removeRequestEntry(RequestKey key)
280 {
281 if (removeRequestContainer.contains(key))
282 {
283 removeRequestContainer[key].reset();
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930284 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700285 handlers.erase(key);
286 removeRequestContainer.erase(key);
287 }
288 }
289};
290
291} // namespace requester
292
293} // namespace pldm