| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 1 | #pragma once | 
|  | 2 |  | 
| Andrew Jeffery | 2abbce7 | 2023-10-18 10:17:35 +1030 | [diff] [blame] | 3 | #include "common/instance_id.hpp" | 
| Rashmica Gupta | 1ed5f7a | 2023-05-22 13:56:42 +1000 | [diff] [blame] | 4 | #include "common/transport.hpp" | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 5 | #include "common/types.hpp" | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 6 | #include "request.hpp" | 
|  | 7 |  | 
| George Liu | c453e16 | 2022-12-21 17:16:23 +0800 | [diff] [blame] | 8 | #include <libpldm/base.h> | 
| Manojkiran Eda | 9fffea2 | 2021-10-27 16:03:27 +0530 | [diff] [blame] | 9 | #include <sys/socket.h> | 
|  | 10 |  | 
| Riya Dixit | 49cfb13 | 2023-03-02 04:26:53 -0600 | [diff] [blame] | 11 | #include <phosphor-logging/lg2.hpp> | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 12 | #include <sdbusplus/async.hpp> | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 13 | #include <sdbusplus/timer.hpp> | 
|  | 14 | #include <sdeventplus/event.hpp> | 
|  | 15 | #include <sdeventplus/source/event.hpp> | 
|  | 16 |  | 
|  | 17 | #include <cassert> | 
|  | 18 | #include <chrono> | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 19 | #include <deque> | 
| Patrick Williams | cdbb9e2 | 2023-07-13 18:05:40 -0500 | [diff] [blame] | 20 | #include <functional> | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 21 | #include <memory> | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 22 | #include <mutex> | 
|  | 23 | #include <queue> | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 24 | #include <tuple> | 
|  | 25 | #include <unordered_map> | 
|  | 26 |  | 
| Riya Dixit | 49cfb13 | 2023-03-02 04:26:53 -0600 | [diff] [blame] | 27 | PHOSPHOR_LOG2_USING; | 
|  | 28 |  | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 29 | namespace pldm | 
|  | 30 | { | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 31 | namespace requester | 
|  | 32 | { | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 33 | /** @struct RequestKey | 
|  | 34 | * | 
|  | 35 | *  RequestKey uniquely identifies the PLDM request message to match it with the | 
|  | 36 | *  response and a combination of MCTP endpoint ID, PLDM instance ID, PLDM type | 
|  | 37 | *  and PLDM command is the key. | 
|  | 38 | */ | 
|  | 39 | struct RequestKey | 
|  | 40 | { | 
|  | 41 | mctp_eid_t eid;     //!< MCTP endpoint ID | 
|  | 42 | uint8_t instanceId; //!< PLDM instance ID | 
|  | 43 | uint8_t type;       //!< PLDM type | 
|  | 44 | uint8_t command;    //!< PLDM command | 
|  | 45 |  | 
|  | 46 | bool operator==(const RequestKey& e) const | 
|  | 47 | { | 
|  | 48 | return ((eid == e.eid) && (instanceId == e.instanceId) && | 
|  | 49 | (type == e.type) && (command == e.command)); | 
|  | 50 | } | 
|  | 51 | }; | 
|  | 52 |  | 
|  | 53 | /** @struct RequestKeyHasher | 
|  | 54 | * | 
|  | 55 | *  This is a simple hash function, since the instance ID generator API | 
|  | 56 | *  generates unique instance IDs for MCTP endpoint ID. | 
|  | 57 | */ | 
|  | 58 | struct RequestKeyHasher | 
|  | 59 | { | 
|  | 60 | std::size_t operator()(const RequestKey& key) const | 
|  | 61 | { | 
|  | 62 | return (key.eid << 24 | key.instanceId << 16 | key.type << 8 | | 
|  | 63 | key.command); | 
|  | 64 | } | 
|  | 65 | }; | 
|  | 66 |  | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 67 | using ResponseHandler = std::function<void( | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 68 | mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)>; | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 69 |  | 
| Thu Nguyen | 6b901e4 | 2024-07-10 15:21:10 +0700 | [diff] [blame] | 70 | /** @brief The response from SendRecvMsg with coroutine API | 
|  | 71 | * | 
|  | 72 | *  The response when registers PLDM request message using the SendRecvMsg | 
|  | 73 | *  with coroutine API. | 
|  | 74 | *  Responded tuple includes <CompleteCode, ResponseMgs, ResponseMsgLength> | 
|  | 75 | *  Value: [PLDM_ERROR, _, _] if registerRequest fails. | 
|  | 76 | *         [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out. | 
|  | 77 | *         [PLDM_SUCCESS, ResponseMsg, ResponseMsgLength] if succeeded | 
|  | 78 | */ | 
|  | 79 | using SendRecvCoResp = std::tuple<int, const pldm_msg*, size_t>; | 
|  | 80 |  | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 81 | /** @struct RegisteredRequest | 
|  | 82 | * | 
|  | 83 | *  This struct is used to store the registered request to one endpoint. | 
|  | 84 | */ | 
|  | 85 | struct RegisteredRequest | 
|  | 86 | { | 
|  | 87 | RequestKey key;                  //!< Responder MCTP endpoint ID | 
|  | 88 | std::vector<uint8_t> reqMsg;     //!< Request messages queue | 
|  | 89 | ResponseHandler responseHandler; //!< Waiting for response flag | 
|  | 90 | }; | 
|  | 91 |  | 
|  | 92 | /** @struct EndpointMessageQueue | 
|  | 93 | * | 
|  | 94 | *  This struct is used to save the list of request messages of one endpoint and | 
|  | 95 | *  the existing of the request message to the endpoint with its' EID. | 
|  | 96 | */ | 
|  | 97 | struct EndpointMessageQueue | 
|  | 98 | { | 
|  | 99 | mctp_eid_t eid; //!< Responder MCTP endpoint ID | 
|  | 100 | std::deque<std::shared_ptr<RegisteredRequest>> requestQueue; //!< Queue | 
|  | 101 | bool activeRequest; //!< Waiting for response flag | 
|  | 102 |  | 
|  | 103 | bool operator==(const mctp_eid_t& mctpEid) const | 
|  | 104 | { | 
|  | 105 | return (eid == mctpEid); | 
|  | 106 | } | 
|  | 107 | }; | 
|  | 108 |  | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 109 | /** @class Handler | 
|  | 110 | * | 
|  | 111 | *  This class handles the lifecycle of the PLDM request message based on the | 
|  | 112 | *  instance ID expiration interval, number of request retries and the timeout | 
|  | 113 | *  waiting for a response. The registered response handlers are invoked with | 
|  | 114 | *  response once the PLDM responder sends the response. If no response is | 
|  | 115 | *  received within the instance ID expiration interval or any other failure the | 
|  | 116 | *  response handler is invoked with the empty response. | 
|  | 117 | * | 
|  | 118 | * @tparam RequestInterface - Request class type | 
|  | 119 | */ | 
|  | 120 | template <class RequestInterface> | 
|  | 121 | class Handler | 
|  | 122 | { | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 123 | public: | 
|  | 124 | Handler() = delete; | 
|  | 125 | Handler(const Handler&) = delete; | 
|  | 126 | Handler(Handler&&) = delete; | 
|  | 127 | Handler& operator=(const Handler&) = delete; | 
|  | 128 | Handler& operator=(Handler&&) = delete; | 
|  | 129 | ~Handler() = default; | 
|  | 130 |  | 
|  | 131 | /** @brief Constructor | 
|  | 132 | * | 
| Rashmica Gupta | 1ed5f7a | 2023-05-22 13:56:42 +1000 | [diff] [blame] | 133 | *  @param[in] pldm_transport - PLDM requester | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 134 | *  @param[in] event - reference to PLDM daemon's main event loop | 
| Andrew Jeffery | a330b2f | 2023-05-04 14:55:37 +0930 | [diff] [blame] | 135 | *  @param[in] instanceIdDb - reference to an InstanceIdDb | 
| Tom Joseph | e5268cd | 2021-09-07 13:04:03 +0530 | [diff] [blame] | 136 | *  @param[in] verbose - verbose tracing flag | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 137 | *  @param[in] instanceIdExpiryInterval - instance ID expiration interval | 
|  | 138 | *  @param[in] numRetries - number of request retries | 
|  | 139 | *  @param[in] responseTimeOut - time to wait between each retry | 
|  | 140 | */ | 
|  | 141 | explicit Handler( | 
| Rashmica Gupta | 1ed5f7a | 2023-05-22 13:56:42 +1000 | [diff] [blame] | 142 | PldmTransport* pldmTransport, sdeventplus::Event& event, | 
|  | 143 | pldm::InstanceIdDb& instanceIdDb, bool verbose, | 
| Brad Bishop | 5079ac4 | 2021-08-19 18:35:06 -0400 | [diff] [blame] | 144 | std::chrono::seconds instanceIdExpiryInterval = | 
|  | 145 | std::chrono::seconds(INSTANCE_ID_EXPIRATION_INTERVAL), | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 146 | uint8_t numRetries = static_cast<uint8_t>(NUMBER_OF_REQUEST_RETRIES), | 
| Brad Bishop | 5079ac4 | 2021-08-19 18:35:06 -0400 | [diff] [blame] | 147 | std::chrono::milliseconds responseTimeOut = | 
|  | 148 | std::chrono::milliseconds(RESPONSE_TIME_OUT)) : | 
| Patrick Williams | 16c2a0a | 2024-08-16 15:20:59 -0400 | [diff] [blame] | 149 | pldmTransport(pldmTransport), event(event), instanceIdDb(instanceIdDb), | 
|  | 150 | verbose(verbose), instanceIdExpiryInterval(instanceIdExpiryInterval), | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 151 | numRetries(numRetries), responseTimeOut(responseTimeOut) | 
|  | 152 | {} | 
|  | 153 |  | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 154 | void instanceIdExpiryCallBack(RequestKey key) | 
|  | 155 | { | 
|  | 156 | auto eid = key.eid; | 
|  | 157 | if (this->handlers.contains(key)) | 
|  | 158 | { | 
| Riya Dixit | 087a751 | 2024-04-06 14:28:08 -0500 | [diff] [blame] | 159 | info( | 
|  | 160 | "Instance ID expiry for EID '{EID}' using InstanceID '{INSTANCEID}'", | 
| Riya Dixit | 1e5c81e | 2024-05-03 07:54:00 -0500 | [diff] [blame] | 161 | "EID", key.eid, "INSTANCEID", key.instanceId); | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 162 | auto& [request, responseHandler, | 
|  | 163 | timerInstance] = this->handlers[key]; | 
|  | 164 | request->stop(); | 
|  | 165 | auto rc = timerInstance->stop(); | 
|  | 166 | if (rc) | 
|  | 167 | { | 
| Riya Dixit | 087a751 | 2024-04-06 14:28:08 -0500 | [diff] [blame] | 168 | error( | 
|  | 169 | "Failed to stop the instance ID expiry timer, response code '{RC}'", | 
| Riya Dixit | 1e5c81e | 2024-05-03 07:54:00 -0500 | [diff] [blame] | 170 | "RC", rc); | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 171 | } | 
|  | 172 | // Call response handler with an empty response to indicate no | 
|  | 173 | // response | 
|  | 174 | responseHandler(eid, nullptr, 0); | 
|  | 175 | this->removeRequestContainer.emplace( | 
|  | 176 | key, | 
|  | 177 | std::make_unique<sdeventplus::source::Defer>( | 
|  | 178 | event, std::bind(&Handler::removeRequestEntry, this, key))); | 
|  | 179 | endpointMessageQueues[eid]->activeRequest = false; | 
|  | 180 |  | 
|  | 181 | /* try to send new request if the endpoint is free */ | 
|  | 182 | pollEndpointQueue(eid); | 
|  | 183 | } | 
|  | 184 | else | 
|  | 185 | { | 
|  | 186 | // This condition is not possible, if a response is received | 
|  | 187 | // before the instance ID expiry, then the response handler | 
|  | 188 | // is executed and the entry will be removed. | 
|  | 189 | assert(false); | 
|  | 190 | } | 
|  | 191 | } | 
|  | 192 |  | 
|  | 193 | /** @brief Send the remaining PLDM request messages in endpoint queue | 
|  | 194 | * | 
|  | 195 | *  @param[in] eid - endpoint ID of the remote MCTP endpoint | 
|  | 196 | */ | 
|  | 197 | int pollEndpointQueue(mctp_eid_t eid) | 
|  | 198 | { | 
|  | 199 | if (endpointMessageQueues[eid]->activeRequest || | 
|  | 200 | endpointMessageQueues[eid]->requestQueue.empty()) | 
|  | 201 | { | 
|  | 202 | return PLDM_SUCCESS; | 
|  | 203 | } | 
|  | 204 |  | 
|  | 205 | endpointMessageQueues[eid]->activeRequest = true; | 
|  | 206 | auto requestMsg = endpointMessageQueues[eid]->requestQueue.front(); | 
|  | 207 | endpointMessageQueues[eid]->requestQueue.pop_front(); | 
|  | 208 |  | 
|  | 209 | auto request = std::make_unique<RequestInterface>( | 
|  | 210 | pldmTransport, requestMsg->key.eid, event, | 
|  | 211 | std::move(requestMsg->reqMsg), numRetries, responseTimeOut, | 
|  | 212 | verbose); | 
| Patrick Williams | 35535cf | 2023-12-05 12:45:02 -0600 | [diff] [blame] | 213 | auto timer = std::make_unique<sdbusplus::Timer>( | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 214 | event.get(), std::bind(&Handler::instanceIdExpiryCallBack, this, | 
|  | 215 | requestMsg->key)); | 
|  | 216 |  | 
|  | 217 | auto rc = request->start(); | 
|  | 218 | if (rc) | 
|  | 219 | { | 
|  | 220 | instanceIdDb.free(requestMsg->key.eid, requestMsg->key.instanceId); | 
| Riya Dixit | 087a751 | 2024-04-06 14:28:08 -0500 | [diff] [blame] | 221 | error( | 
|  | 222 | "Failure to send the PLDM request message for polling endpoint queue, response code '{RC}'", | 
|  | 223 | "RC", rc); | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 224 | endpointMessageQueues[eid]->activeRequest = false; | 
|  | 225 | return rc; | 
|  | 226 | } | 
|  | 227 |  | 
|  | 228 | try | 
|  | 229 | { | 
|  | 230 | timer->start(duration_cast<std::chrono::microseconds>( | 
|  | 231 | instanceIdExpiryInterval)); | 
|  | 232 | } | 
|  | 233 | catch (const std::runtime_error& e) | 
|  | 234 | { | 
|  | 235 | instanceIdDb.free(requestMsg->key.eid, requestMsg->key.instanceId); | 
|  | 236 | error( | 
| Riya Dixit | 087a751 | 2024-04-06 14:28:08 -0500 | [diff] [blame] | 237 | "Failed to start the instance ID expiry timer, error - {ERROR}", | 
|  | 238 | "ERROR", e); | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 239 | endpointMessageQueues[eid]->activeRequest = false; | 
|  | 240 | return PLDM_ERROR; | 
|  | 241 | } | 
|  | 242 |  | 
|  | 243 | handlers.emplace(requestMsg->key, | 
|  | 244 | std::make_tuple(std::move(request), | 
|  | 245 | std::move(requestMsg->responseHandler), | 
|  | 246 | std::move(timer))); | 
|  | 247 | return PLDM_SUCCESS; | 
|  | 248 | } | 
|  | 249 |  | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 250 | /** @brief Register a PLDM request message | 
|  | 251 | * | 
|  | 252 | *  @param[in] eid - endpoint ID of the remote MCTP endpoint | 
|  | 253 | *  @param[in] instanceId - instance ID to match request and response | 
|  | 254 | *  @param[in] type - PLDM type | 
|  | 255 | *  @param[in] command - PLDM command | 
|  | 256 | *  @param[in] requestMsg - PLDM request message | 
|  | 257 | *  @param[in] responseHandler - Response handler for this request | 
|  | 258 | * | 
|  | 259 | *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise | 
|  | 260 | */ | 
|  | 261 | int registerRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type, | 
|  | 262 | uint8_t command, pldm::Request&& requestMsg, | 
|  | 263 | ResponseHandler&& responseHandler) | 
|  | 264 | { | 
|  | 265 | RequestKey key{eid, instanceId, type, command}; | 
|  | 266 |  | 
| Thu Nguyen | b4e8cfd | 2023-03-23 15:06:44 +0700 | [diff] [blame] | 267 | if (handlers.contains(key)) | 
|  | 268 | { | 
| Riya Dixit | 087a751 | 2024-04-06 14:28:08 -0500 | [diff] [blame] | 269 | error( | 
|  | 270 | "Register request for EID '{EID}' is using InstanceID '{INSTANCEID}'", | 
| Riya Dixit | 1e5c81e | 2024-05-03 07:54:00 -0500 | [diff] [blame] | 271 | "EID", eid, "INSTANCEID", instanceId); | 
| Thu Nguyen | b4e8cfd | 2023-03-23 15:06:44 +0700 | [diff] [blame] | 272 | return PLDM_ERROR; | 
|  | 273 | } | 
|  | 274 |  | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 275 | auto inputRequest = std::make_shared<RegisteredRequest>( | 
|  | 276 | key, std::move(requestMsg), std::move(responseHandler)); | 
|  | 277 | if (endpointMessageQueues.contains(eid)) | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 278 | { | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 279 | endpointMessageQueues[eid]->requestQueue.push_back(inputRequest); | 
|  | 280 | } | 
|  | 281 | else | 
|  | 282 | { | 
|  | 283 | std::deque<std::shared_ptr<RegisteredRequest>> reqQueue; | 
|  | 284 | reqQueue.push_back(inputRequest); | 
|  | 285 | endpointMessageQueues[eid] = | 
|  | 286 | std::make_shared<EndpointMessageQueue>(eid, reqQueue, false); | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 287 | } | 
|  | 288 |  | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 289 | /* try to send new request if the endpoint is free */ | 
| Eric Yang | 4bf3ed8 | 2025-03-14 18:45:53 +0800 | [diff] [blame] | 290 | auto rc = pollEndpointQueue(eid); | 
|  | 291 | if (rc != PLDM_SUCCESS) | 
|  | 292 | { | 
|  | 293 | error( | 
|  | 294 | "Failed to process request queue for EID {EID}, response code {RC}.", | 
|  | 295 | "EID", eid, "RC", rc); | 
|  | 296 | return rc; | 
|  | 297 | } | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 298 |  | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 299 | return PLDM_SUCCESS; | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 300 | } | 
|  | 301 |  | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 302 | /** @brief Unregister a PLDM request message | 
|  | 303 | * | 
|  | 304 | *  @param[in] eid - endpoint ID of the remote MCTP endpoint | 
|  | 305 | *  @param[in] instanceId - instance ID to match request and response | 
|  | 306 | *  @param[in] type - PLDM type | 
|  | 307 | *  @param[in] command - PLDM command | 
|  | 308 | * | 
|  | 309 | *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise | 
|  | 310 | */ | 
|  | 311 | int unregisterRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type, | 
|  | 312 | uint8_t command) | 
|  | 313 | { | 
|  | 314 | RequestKey key{eid, instanceId, type, command}; | 
|  | 315 |  | 
|  | 316 | /* handlers only contain key when the message is already sent */ | 
|  | 317 | if (handlers.contains(key)) | 
|  | 318 | { | 
|  | 319 | auto& [request, responseHandler, timerInstance] = handlers[key]; | 
|  | 320 | request->stop(); | 
|  | 321 | auto rc = timerInstance->stop(); | 
|  | 322 | if (rc) | 
|  | 323 | { | 
|  | 324 | error( | 
|  | 325 | "Failed to stop the instance ID expiry timer, response code '{RC}'", | 
|  | 326 | "RC", static_cast<int>(rc)); | 
|  | 327 | } | 
|  | 328 |  | 
|  | 329 | instanceIdDb.free(key.eid, key.instanceId); | 
|  | 330 | handlers.erase(key); | 
|  | 331 | endpointMessageQueues[eid]->activeRequest = false; | 
|  | 332 | /* try to send new request if the endpoint is free */ | 
|  | 333 | pollEndpointQueue(eid); | 
|  | 334 |  | 
|  | 335 | return PLDM_SUCCESS; | 
|  | 336 | } | 
|  | 337 | else | 
|  | 338 | { | 
|  | 339 | if (!endpointMessageQueues.contains(eid)) | 
|  | 340 | { | 
|  | 341 | error( | 
|  | 342 | "Can't find request for EID '{EID}' is using InstanceID '{INSTANCEID}' in Endpoint message Queue", | 
|  | 343 | "EID", (unsigned)eid, "INSTANCEID", (unsigned)instanceId); | 
|  | 344 | return PLDM_ERROR; | 
|  | 345 | } | 
|  | 346 | auto requestMsg = endpointMessageQueues[eid]->requestQueue; | 
|  | 347 | /* Find the registered request in the requestQueue */ | 
|  | 348 | for (auto it = requestMsg.begin(); it != requestMsg.end();) | 
|  | 349 | { | 
|  | 350 | auto msg = *it; | 
|  | 351 | if (msg->key == key) | 
|  | 352 | { | 
|  | 353 | // erase and get the next valid iterator | 
|  | 354 | it = endpointMessageQueues[eid]->requestQueue.erase(it); | 
|  | 355 | instanceIdDb.free(key.eid, key.instanceId); | 
|  | 356 | return PLDM_SUCCESS; | 
|  | 357 | } | 
|  | 358 | else | 
|  | 359 | { | 
|  | 360 | ++it; // increment iterator only if not erasing | 
|  | 361 | } | 
|  | 362 | } | 
|  | 363 | } | 
|  | 364 |  | 
|  | 365 | return PLDM_ERROR; | 
|  | 366 | } | 
|  | 367 |  | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 368 | /** @brief Handle PLDM response message | 
|  | 369 | * | 
|  | 370 | *  @param[in] eid - endpoint ID of the remote MCTP endpoint | 
|  | 371 | *  @param[in] instanceId - instance ID to match request and response | 
|  | 372 | *  @param[in] type - PLDM type | 
|  | 373 | *  @param[in] command - PLDM command | 
|  | 374 | *  @param[in] response - PLDM response message | 
|  | 375 | *  @param[in] respMsgLen - length of the response message | 
|  | 376 | */ | 
|  | 377 | void handleResponse(mctp_eid_t eid, uint8_t instanceId, uint8_t type, | 
|  | 378 | uint8_t command, const pldm_msg* response, | 
|  | 379 | size_t respMsgLen) | 
|  | 380 | { | 
|  | 381 | RequestKey key{eid, instanceId, type, command}; | 
| SeanCChuang | f7d249e | 2024-09-02 14:10:33 +0800 | [diff] [blame] | 382 | if (handlers.contains(key) && !removeRequestContainer.contains(key)) | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 383 | { | 
|  | 384 | auto& [request, responseHandler, timerInstance] = handlers[key]; | 
|  | 385 | request->stop(); | 
|  | 386 | auto rc = timerInstance->stop(); | 
|  | 387 | if (rc) | 
|  | 388 | { | 
| Riya Dixit | 087a751 | 2024-04-06 14:28:08 -0500 | [diff] [blame] | 389 | error( | 
|  | 390 | "Failed to stop the instance ID expiry timer, response code '{RC}'", | 
| Riya Dixit | 1e5c81e | 2024-05-03 07:54:00 -0500 | [diff] [blame] | 391 | "RC", rc); | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 392 | } | 
|  | 393 | responseHandler(eid, response, respMsgLen); | 
| Andrew Jeffery | a330b2f | 2023-05-04 14:55:37 +0930 | [diff] [blame] | 394 | instanceIdDb.free(key.eid, key.instanceId); | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 395 | handlers.erase(key); | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 396 |  | 
|  | 397 | endpointMessageQueues[eid]->activeRequest = false; | 
|  | 398 | /* try to send new request if the endpoint is free */ | 
|  | 399 | pollEndpointQueue(eid); | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 400 | } | 
|  | 401 | else | 
|  | 402 | { | 
|  | 403 | // Got a response for a PLDM request message not registered with the | 
|  | 404 | // request handler, so freeing up the instance ID, this can be other | 
|  | 405 | // OpenBMC applications relying on PLDM D-Bus apis like | 
|  | 406 | // openpower-occ-control and softoff | 
| Andrew Jeffery | a330b2f | 2023-05-04 14:55:37 +0930 | [diff] [blame] | 407 | instanceIdDb.free(key.eid, key.instanceId); | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 408 | } | 
|  | 409 | } | 
|  | 410 |  | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 411 | /** @brief Wrap registerRequest with coroutine API. | 
|  | 412 | * | 
| Thu Nguyen | 6b901e4 | 2024-07-10 15:21:10 +0700 | [diff] [blame] | 413 | *  @return Return [PLDM_ERROR, _, _] if registerRequest fails. | 
|  | 414 | *          Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out. | 
|  | 415 | *          Return [PLDM_SUCCESS, resp, len] if succeeded | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 416 | */ | 
| Patrick Williams | 366507c | 2025-02-03 14:28:01 -0500 | [diff] [blame] | 417 | stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto sendRecvMsg( | 
|  | 418 | mctp_eid_t eid, pldm::Request&& request); | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 419 |  | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 420 | private: | 
| Rashmica Gupta | 1ed5f7a | 2023-05-22 13:56:42 +1000 | [diff] [blame] | 421 | PldmTransport* pldmTransport; //!< PLDM transport object | 
| Brad Bishop | 5079ac4 | 2021-08-19 18:35:06 -0400 | [diff] [blame] | 422 | sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop | 
| Andrew Jeffery | a330b2f | 2023-05-04 14:55:37 +0930 | [diff] [blame] | 423 | pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb | 
| Andrew Jeffery | a330b2f | 2023-05-04 14:55:37 +0930 | [diff] [blame] | 424 | bool verbose;                     //!< verbose tracing flag | 
| Brad Bishop | 5079ac4 | 2021-08-19 18:35:06 -0400 | [diff] [blame] | 425 | std::chrono::seconds | 
| Andrew Jeffery | a330b2f | 2023-05-04 14:55:37 +0930 | [diff] [blame] | 426 | instanceIdExpiryInterval;     //!< Instance ID expiration interval | 
|  | 427 | uint8_t numRetries;               //!< number of request retries | 
| Brad Bishop | 5079ac4 | 2021-08-19 18:35:06 -0400 | [diff] [blame] | 428 | std::chrono::milliseconds | 
| Andrew Jeffery | a330b2f | 2023-05-04 14:55:37 +0930 | [diff] [blame] | 429 | responseTimeOut;              //!< time to wait between each retry | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 430 |  | 
| Tom Joseph | e5268cd | 2021-09-07 13:04:03 +0530 | [diff] [blame] | 431 | /** @brief Container for storing the details of the PLDM request | 
|  | 432 | *         message, handler for the corresponding PLDM response and the | 
|  | 433 | *         timer object for the Instance ID expiration | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 434 | */ | 
| Brad Bishop | 5079ac4 | 2021-08-19 18:35:06 -0400 | [diff] [blame] | 435 | using RequestValue = | 
|  | 436 | std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler, | 
| Patrick Williams | 35535cf | 2023-12-05 12:45:02 -0600 | [diff] [blame] | 437 | std::unique_ptr<sdbusplus::Timer>>; | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 438 |  | 
| Thu Nguyen | 4ddee3a | 2023-08-03 08:20:03 +0700 | [diff] [blame] | 439 | // Manage the requests of responders base on MCTP EID | 
|  | 440 | std::map<mctp_eid_t, std::shared_ptr<EndpointMessageQueue>> | 
|  | 441 | endpointMessageQueues; | 
|  | 442 |  | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 443 | /** @brief Container for storing the PLDM request entries */ | 
|  | 444 | std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers; | 
|  | 445 |  | 
|  | 446 | /** @brief Container to store information about the request entries to be | 
|  | 447 | *         removed after the instance ID timer expires | 
|  | 448 | */ | 
| Brad Bishop | 5079ac4 | 2021-08-19 18:35:06 -0400 | [diff] [blame] | 449 | std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>, | 
|  | 450 | RequestKeyHasher> | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 451 | removeRequestContainer; | 
|  | 452 |  | 
|  | 453 | /** @brief Remove request entry for which the instance ID expired | 
|  | 454 | * | 
|  | 455 | *  @param[in] key - key for the Request | 
|  | 456 | */ | 
|  | 457 | void removeRequestEntry(RequestKey key) | 
|  | 458 | { | 
|  | 459 | if (removeRequestContainer.contains(key)) | 
|  | 460 | { | 
|  | 461 | removeRequestContainer[key].reset(); | 
| Andrew Jeffery | a330b2f | 2023-05-04 14:55:37 +0930 | [diff] [blame] | 462 | instanceIdDb.free(key.eid, key.instanceId); | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 463 | handlers.erase(key); | 
|  | 464 | removeRequestContainer.erase(key); | 
|  | 465 | } | 
|  | 466 | } | 
|  | 467 | }; | 
|  | 468 |  | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 469 | /** @class SendRecvMsgOperation | 
|  | 470 | * | 
|  | 471 | *  Represents the state and logic for a single send/receive message operation | 
|  | 472 | * | 
|  | 473 | * @tparam RequestInterface - Request class type | 
|  | 474 | * @tparam stdexec::receiver - Execute receiver | 
|  | 475 | */ | 
|  | 476 | template <class RequestInterface, stdexec::receiver R> | 
|  | 477 | struct SendRecvMsgOperation | 
|  | 478 | { | 
|  | 479 | SendRecvMsgOperation() = delete; | 
|  | 480 |  | 
|  | 481 | explicit SendRecvMsgOperation(Handler<RequestInterface>& handler, | 
|  | 482 | mctp_eid_t eid, pldm::Request&& request, | 
|  | 483 | R&& r) : | 
| Patrick Williams | 16c2a0a | 2024-08-16 15:20:59 -0400 | [diff] [blame] | 484 | handler(handler), request(std::move(request)), receiver(std::move(r)) | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 485 | { | 
|  | 486 | auto requestMsg = | 
|  | 487 | reinterpret_cast<const pldm_msg*>(this->request.data()); | 
|  | 488 | requestKey = RequestKey{ | 
|  | 489 | eid, | 
|  | 490 | requestMsg->hdr.instance_id, | 
|  | 491 | requestMsg->hdr.type, | 
|  | 492 | requestMsg->hdr.command, | 
|  | 493 | }; | 
|  | 494 | response = nullptr; | 
|  | 495 | respMsgLen = 0; | 
|  | 496 | } | 
|  | 497 |  | 
|  | 498 | /** @brief Checks if the operation has been requested to stop. | 
|  | 499 | *         If so, it sets the state to stopped.Registers the request with | 
|  | 500 | *         the handler. If registration fails, sets an error on the | 
|  | 501 | *         receiver. If stopping is possible, sets up a stop callback. | 
|  | 502 | * | 
|  | 503 | *  @param[in] op - operation request | 
|  | 504 | * | 
|  | 505 | *  @return Execute errors | 
|  | 506 | */ | 
|  | 507 | friend void tag_invoke(stdexec::start_t, SendRecvMsgOperation& op) noexcept | 
|  | 508 | { | 
|  | 509 | auto stopToken = stdexec::get_stop_token(stdexec::get_env(op.receiver)); | 
|  | 510 |  | 
|  | 511 | // operation already cancelled | 
|  | 512 | if (stopToken.stop_requested()) | 
|  | 513 | { | 
|  | 514 | return stdexec::set_stopped(std::move(op.receiver)); | 
|  | 515 | } | 
|  | 516 |  | 
|  | 517 | using namespace std::placeholders; | 
|  | 518 | auto rc = op.handler.registerRequest( | 
|  | 519 | op.requestKey.eid, op.requestKey.instanceId, op.requestKey.type, | 
|  | 520 | op.requestKey.command, std::move(op.request), | 
|  | 521 | std::bind(&SendRecvMsgOperation::onComplete, &op, _1, _2, _3)); | 
|  | 522 | if (rc) | 
|  | 523 | { | 
| Thu Nguyen | 6b901e4 | 2024-07-10 15:21:10 +0700 | [diff] [blame] | 524 | return stdexec::set_value(std::move(op.receiver), rc, | 
|  | 525 | static_cast<const pldm_msg*>(nullptr), | 
|  | 526 | static_cast<size_t>(0)); | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 527 | } | 
|  | 528 |  | 
|  | 529 | if (stopToken.stop_possible()) | 
|  | 530 | { | 
|  | 531 | op.stopCallback.emplace( | 
|  | 532 | std::move(stopToken), | 
|  | 533 | std::bind(&SendRecvMsgOperation::onStop, &op)); | 
|  | 534 | } | 
|  | 535 | } | 
|  | 536 |  | 
|  | 537 | /** @brief Unregisters the request and sets the state to stopped on the | 
|  | 538 | *         receiver. | 
|  | 539 | */ | 
|  | 540 | void onStop() | 
|  | 541 | { | 
|  | 542 | handler.unregisterRequest(requestKey.eid, requestKey.instanceId, | 
|  | 543 | requestKey.type, requestKey.command); | 
|  | 544 | return stdexec::set_stopped(std::move(receiver)); | 
|  | 545 | } | 
|  | 546 |  | 
|  | 547 | /** @brief This function resets the stop callback. Validates the response | 
|  | 548 | *         and sets either an error or a value on the receiver. | 
|  | 549 | * | 
|  | 550 | *  @param[in] eid - endpoint ID of the remote MCTP endpoint | 
|  | 551 | *  @param[in] response - PLDM response message | 
|  | 552 | *  @param[in] respMsgLen - length of the response message | 
|  | 553 | * | 
|  | 554 | *  @return PLDM completion code | 
|  | 555 | */ | 
|  | 556 | void onComplete(mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) | 
|  | 557 | { | 
|  | 558 | stopCallback.reset(); | 
|  | 559 | assert(eid == this->requestKey.eid); | 
| Thu Nguyen | 6b901e4 | 2024-07-10 15:21:10 +0700 | [diff] [blame] | 560 | auto rc = PLDM_SUCCESS; | 
|  | 561 | if (!response && !respMsgLen) | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 562 | { | 
| Thu Nguyen | 6b901e4 | 2024-07-10 15:21:10 +0700 | [diff] [blame] | 563 | rc = PLDM_ERROR_NOT_READY; | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 564 | } | 
| Thu Nguyen | 6b901e4 | 2024-07-10 15:21:10 +0700 | [diff] [blame] | 565 | return stdexec::set_value(std::move(receiver), static_cast<int>(rc), | 
|  | 566 | response, respMsgLen); | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 567 | } | 
|  | 568 |  | 
|  | 569 | private: | 
|  | 570 | /** @brief Reference to a Handler object that manages the request/response | 
|  | 571 | *         logic. | 
|  | 572 | */ | 
|  | 573 | requester::Handler<RequestInterface>& handler; | 
|  | 574 |  | 
|  | 575 | /** @brief Stores information about the request such as eid, instanceId, | 
|  | 576 | *         type, and command. | 
|  | 577 | */ | 
|  | 578 | RequestKey requestKey; | 
|  | 579 |  | 
|  | 580 | /** @brief The request message to be sent. | 
|  | 581 | */ | 
|  | 582 | pldm::Request request; | 
|  | 583 |  | 
|  | 584 | /** @brief The response message for the sent request message. | 
|  | 585 | */ | 
|  | 586 | const pldm_msg* response; | 
|  | 587 |  | 
|  | 588 | /** @brief The length of response message for the sent request message. | 
|  | 589 | */ | 
|  | 590 | size_t respMsgLen; | 
|  | 591 |  | 
|  | 592 | /** @brief The receiver to be notified with the result of the operation. | 
|  | 593 | */ | 
|  | 594 | R receiver; | 
|  | 595 |  | 
|  | 596 | /** @brief An optional callback that handles stopping the operation if | 
|  | 597 | *         requested. | 
|  | 598 | */ | 
|  | 599 | std::optional<typename stdexec::stop_token_of_t< | 
|  | 600 | stdexec::env_of_t<R>>::template callback_type<std::function<void()>>> | 
|  | 601 | stopCallback = std::nullopt; | 
|  | 602 | }; | 
|  | 603 |  | 
|  | 604 | /** @class SendRecvMsgSender | 
|  | 605 | * | 
|  | 606 | *  Represents the single message sender | 
|  | 607 | * | 
|  | 608 | * @tparam RequestInterface - Request class type | 
|  | 609 | */ | 
|  | 610 | template <class RequestInterface> | 
|  | 611 | struct SendRecvMsgSender | 
|  | 612 | { | 
|  | 613 | using is_sender = void; | 
|  | 614 |  | 
|  | 615 | SendRecvMsgSender() = delete; | 
|  | 616 |  | 
|  | 617 | explicit SendRecvMsgSender(requester::Handler<RequestInterface>& handler, | 
|  | 618 | mctp_eid_t eid, pldm::Request&& request) : | 
| Patrick Williams | 16c2a0a | 2024-08-16 15:20:59 -0400 | [diff] [blame] | 619 | handler(handler), eid(eid), request(std::move(request)) | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 620 | {} | 
|  | 621 |  | 
|  | 622 | friend auto tag_invoke(stdexec::get_completion_signatures_t, | 
|  | 623 | const SendRecvMsgSender&, auto) | 
|  | 624 | -> stdexec::completion_signatures< | 
| Thu Nguyen | 6b901e4 | 2024-07-10 15:21:10 +0700 | [diff] [blame] | 625 | stdexec::set_value_t(int, const pldm_msg*, size_t), | 
|  | 626 | stdexec::set_stopped_t()>; | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 627 |  | 
|  | 628 | /** @brief Execute the sending the request message */ | 
|  | 629 | template <stdexec::receiver R> | 
|  | 630 | friend auto tag_invoke(stdexec::connect_t, SendRecvMsgSender&& self, R r) | 
|  | 631 | { | 
|  | 632 | return SendRecvMsgOperation<RequestInterface, R>( | 
|  | 633 | self.handler, self.eid, std::move(self.request), std::move(r)); | 
|  | 634 | } | 
|  | 635 |  | 
|  | 636 | private: | 
|  | 637 | /** @brief Reference to a Handler object that manages the request/response | 
|  | 638 | *         logic. | 
|  | 639 | */ | 
|  | 640 | requester::Handler<RequestInterface>& handler; | 
|  | 641 |  | 
|  | 642 | /** @brief MCTP Endpoint ID of request message */ | 
|  | 643 | mctp_eid_t eid; | 
|  | 644 |  | 
|  | 645 | /** @brief Request message */ | 
|  | 646 | pldm::Request request; | 
|  | 647 | }; | 
|  | 648 |  | 
| Thu Nguyen | 6b901e4 | 2024-07-10 15:21:10 +0700 | [diff] [blame] | 649 | /** @brief Wrap registerRequest with coroutine API. | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 650 | * | 
|  | 651 | *  @param[in] eid - endpoint ID of the remote MCTP endpoint | 
|  | 652 | *  @param[in] request - PLDM request message | 
|  | 653 | * | 
| Thu Nguyen | 6b901e4 | 2024-07-10 15:21:10 +0700 | [diff] [blame] | 654 | *  @return Return [PLDM_ERROR, _, _] if registerRequest fails. | 
|  | 655 | *          Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out. | 
|  | 656 | *          Return [PLDM_SUCCESS, resp, len] if succeeded | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 657 | */ | 
|  | 658 | template <class RequestInterface> | 
| Thu Nguyen | 6b901e4 | 2024-07-10 15:21:10 +0700 | [diff] [blame] | 659 | stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 660 | Handler<RequestInterface>::sendRecvMsg(mctp_eid_t eid, | 
|  | 661 | pldm::Request&& request) | 
|  | 662 | { | 
|  | 663 | return SendRecvMsgSender(*this, eid, std::move(request)) | | 
| Thu Nguyen | 6b901e4 | 2024-07-10 15:21:10 +0700 | [diff] [blame] | 664 | stdexec::then([](int rc, const pldm_msg* resp, size_t respLen) { | 
| Patrick Williams | 16c2a0a | 2024-08-16 15:20:59 -0400 | [diff] [blame] | 665 | return std::make_tuple(rc, resp, respLen); | 
|  | 666 | }); | 
| Gilbert Chen | a85c69d | 2024-01-09 21:25:26 +0700 | [diff] [blame] | 667 | } | 
|  | 668 |  | 
| Tom Joseph | 74f27c7 | 2021-05-16 07:58:53 -0700 | [diff] [blame] | 669 | } // namespace requester | 
|  | 670 |  | 
|  | 671 | } // namespace pldm |