blob: ff7f33d829f15cad68f89f88fb3e9b4c1b914f87 [file] [log] [blame]
Tom Joseph74f27c72021-05-16 07:58:53 -07001#pragma once
2
3#include "config.h"
4
5#include "libpldm/base.h"
6#include "libpldm/requester/pldm.h"
7
8#include "common/types.hpp"
9#include "pldmd/dbus_impl_requester.hpp"
10#include "request.hpp"
11
Manojkiran Eda9fffea22021-10-27 16:03:27 +053012#include <sys/socket.h>
13
Tom Joseph74f27c72021-05-16 07:58:53 -070014#include <function2/function2.hpp>
15#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
25namespace pldm
26{
27
28namespace requester
29{
30
31/** @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{
82
83 public:
84 Handler() = delete;
85 Handler(const Handler&) = delete;
86 Handler(Handler&&) = delete;
87 Handler& operator=(const Handler&) = delete;
88 Handler& operator=(Handler&&) = delete;
89 ~Handler() = default;
90
91 /** @brief Constructor
92 *
93 * @param[in] fd - fd of MCTP communications socket
94 * @param[in] event - reference to PLDM daemon's main event loop
95 * @param[in] requester - reference to Requester object
Manojkiran Eda9fffea22021-10-27 16:03:27 +053096 * @param[in] currentSendbuffSize - current send buffer size
Tom Josephe5268cd2021-09-07 13:04:03 +053097 * @param[in] verbose - verbose tracing flag
Tom Joseph74f27c72021-05-16 07:58:53 -070098 * @param[in] instanceIdExpiryInterval - instance ID expiration interval
99 * @param[in] numRetries - number of request retries
100 * @param[in] responseTimeOut - time to wait between each retry
101 */
102 explicit Handler(
Brad Bishop5079ac42021-08-19 18:35:06 -0400103 int fd, sdeventplus::Event& event, pldm::dbus_api::Requester& requester,
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530104 int currentSendbuffSize, bool verbose,
Brad Bishop5079ac42021-08-19 18:35:06 -0400105 std::chrono::seconds instanceIdExpiryInterval =
106 std::chrono::seconds(INSTANCE_ID_EXPIRATION_INTERVAL),
Tom Joseph74f27c72021-05-16 07:58:53 -0700107 uint8_t numRetries = static_cast<uint8_t>(NUMBER_OF_REQUEST_RETRIES),
Brad Bishop5079ac42021-08-19 18:35:06 -0400108 std::chrono::milliseconds responseTimeOut =
109 std::chrono::milliseconds(RESPONSE_TIME_OUT)) :
Tom Joseph74f27c72021-05-16 07:58:53 -0700110 fd(fd),
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530111 event(event), requester(requester),
112 currentSendbuffSize(currentSendbuffSize), verbose(verbose),
Tom Joseph74f27c72021-05-16 07:58:53 -0700113 instanceIdExpiryInterval(instanceIdExpiryInterval),
114 numRetries(numRetries), responseTimeOut(responseTimeOut)
115 {}
116
117 /** @brief Register a PLDM request message
118 *
119 * @param[in] eid - endpoint ID of the remote MCTP endpoint
120 * @param[in] instanceId - instance ID to match request and response
121 * @param[in] type - PLDM type
122 * @param[in] command - PLDM command
123 * @param[in] requestMsg - PLDM request message
124 * @param[in] responseHandler - Response handler for this request
125 *
126 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
127 */
128 int registerRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
129 uint8_t command, pldm::Request&& requestMsg,
130 ResponseHandler&& responseHandler)
131 {
132 RequestKey key{eid, instanceId, type, command};
133
134 auto instanceIdExpiryCallBack = [key, this](void) {
135 if (this->handlers.contains(key))
136 {
137 std::cerr << "Response not received for the request, instance "
138 "ID expired."
Manojkiran Eda394fac62021-07-22 15:58:29 +0530139 << " EID = " << (unsigned)key.eid
140 << " INSTANCE_ID = " << (unsigned)key.instanceId
141 << " TYPE = " << (unsigned)key.type
142 << " COMMAND = " << (unsigned)key.command << "\n";
Tom Joseph74f27c72021-05-16 07:58:53 -0700143 auto& [request, responseHandler, timerInstance] =
144 this->handlers[key];
145 request->stop();
146 auto rc = timerInstance->stop();
147 if (rc)
148 {
149 std::cerr
150 << "Failed to stop the instance ID expiry timer. RC = "
151 << rc << "\n";
152 }
153 // Call response handler with an empty response to indicate no
154 // response
155 responseHandler(key.eid, nullptr, 0);
156 this->removeRequestContainer.emplace(
157 key, std::make_unique<sdeventplus::source::Defer>(
158 event, std::bind(&Handler::removeRequestEntry,
159 this, key)));
160 }
161 else
162 {
163 // This condition is not possible, if a response is received
164 // before the instance ID expiry, then the response handler
165 // is executed and the entry will be removed.
166 assert(false);
167 }
168 };
169
170 auto request = std::make_unique<RequestInterface>(
Tom Josephe5268cd2021-09-07 13:04:03 +0530171 fd, eid, event, std::move(requestMsg), numRetries, responseTimeOut,
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530172 currentSendbuffSize, verbose);
Tom Joseph74f27c72021-05-16 07:58:53 -0700173 auto timer = std::make_unique<phosphor::Timer>(
174 event.get(), instanceIdExpiryCallBack);
175
176 auto rc = request->start();
Tom Josepha5ed6582021-06-17 22:08:47 -0700177 if (rc)
Tom Joseph74f27c72021-05-16 07:58:53 -0700178 {
Tom Josepha5ed6582021-06-17 22:08:47 -0700179 requester.markFree(eid, instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700180 std::cerr << "Failure to send the PLDM request message"
181 << "\n";
182 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 {
Tom Josepha5ed6582021-06-17 22:08:47 -0700192 requester.markFree(eid, instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700193 std::cerr << "Failed to start the instance ID expiry timer. RC = "
194 << e.what() << "\n";
195 return PLDM_ERROR;
196 }
197
198 handlers.emplace(key, std::make_tuple(std::move(request),
199 std::move(responseHandler),
200 std::move(timer)));
201 return rc;
202 }
203
204 /** @brief Handle PLDM response message
205 *
206 * @param[in] eid - endpoint ID of the remote MCTP endpoint
207 * @param[in] instanceId - instance ID to match request and response
208 * @param[in] type - PLDM type
209 * @param[in] command - PLDM command
210 * @param[in] response - PLDM response message
211 * @param[in] respMsgLen - length of the response message
212 */
213 void handleResponse(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
214 uint8_t command, const pldm_msg* response,
215 size_t respMsgLen)
216 {
217 RequestKey key{eid, instanceId, type, command};
218 if (handlers.contains(key))
219 {
220 auto& [request, responseHandler, timerInstance] = handlers[key];
221 request->stop();
222 auto rc = timerInstance->stop();
223 if (rc)
224 {
225 std::cerr
226 << "Failed to stop the instance ID expiry timer. RC = "
227 << rc << "\n";
228 }
229 responseHandler(eid, response, respMsgLen);
230 requester.markFree(key.eid, key.instanceId);
231 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
239 requester.markFree(key.eid, key.instanceId);
240 }
241 }
242
243 private:
Brad Bishop5079ac42021-08-19 18:35:06 -0400244 int fd; //!< file descriptor of MCTP communications socket
245 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
246 pldm::dbus_api::Requester& requester; //!< reference to Requester object
Manojkiran Eda9fffea22021-10-27 16:03:27 +0530247 int currentSendbuffSize; //!< current Send Buffer size
Tom Josephe5268cd2021-09-07 13:04:03 +0530248 bool verbose; //!< verbose tracing flag
Brad Bishop5079ac42021-08-19 18:35:06 -0400249 std::chrono::seconds
250 instanceIdExpiryInterval; //!< Instance ID expiration interval
251 uint8_t numRetries; //!< number of request retries
252 std::chrono::milliseconds
253 responseTimeOut; //!< time to wait between each retry
Tom Joseph74f27c72021-05-16 07:58:53 -0700254
Tom Josephe5268cd2021-09-07 13:04:03 +0530255 /** @brief Container for storing the details of the PLDM request
256 * message, handler for the corresponding PLDM response and the
257 * timer object for the Instance ID expiration
Tom Joseph74f27c72021-05-16 07:58:53 -0700258 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400259 using RequestValue =
260 std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
261 std::unique_ptr<phosphor::Timer>>;
Tom Joseph74f27c72021-05-16 07:58:53 -0700262
263 /** @brief Container for storing the PLDM request entries */
264 std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
265
266 /** @brief Container to store information about the request entries to be
267 * removed after the instance ID timer expires
268 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400269 std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
270 RequestKeyHasher>
Tom Joseph74f27c72021-05-16 07:58:53 -0700271 removeRequestContainer;
272
273 /** @brief Remove request entry for which the instance ID expired
274 *
275 * @param[in] key - key for the Request
276 */
277 void removeRequestEntry(RequestKey key)
278 {
279 if (removeRequestContainer.contains(key))
280 {
281 removeRequestContainer[key].reset();
282 requester.markFree(key.eid, key.instanceId);
283 handlers.erase(key);
284 removeRequestContainer.erase(key);
285 }
286 }
287};
288
289} // namespace requester
290
291} // namespace pldm