174f27c73STom Joseph #pragma once 274f27c73STom Joseph 32abbce76SAndrew Jeffery #include "common/instance_id.hpp" 41ed5f7a6SRashmica Gupta #include "common/transport.hpp" 574f27c73STom Joseph #include "common/types.hpp" 674f27c73STom Joseph #include "request.hpp" 774f27c73STom Joseph 8c453e164SGeorge Liu #include <libpldm/base.h> 99fffea2cSManojkiran Eda #include <sys/socket.h> 109fffea2cSManojkiran Eda 1149cfb138SRiya Dixit #include <phosphor-logging/lg2.hpp> 12a85c69d3SGilbert Chen #include <sdbusplus/async.hpp> 1374f27c73STom Joseph #include <sdbusplus/timer.hpp> 1474f27c73STom Joseph #include <sdeventplus/event.hpp> 1574f27c73STom Joseph #include <sdeventplus/source/event.hpp> 1674f27c73STom Joseph 1774f27c73STom Joseph #include <cassert> 1874f27c73STom Joseph #include <chrono> 194ddee3a0SThu Nguyen #include <deque> 20cdbb9e27SPatrick Williams #include <functional> 2174f27c73STom Joseph #include <memory> 224ddee3a0SThu Nguyen #include <mutex> 234ddee3a0SThu Nguyen #include <queue> 2474f27c73STom Joseph #include <tuple> 2574f27c73STom Joseph #include <unordered_map> 2674f27c73STom Joseph 2749cfb138SRiya Dixit PHOSPHOR_LOG2_USING; 2849cfb138SRiya Dixit 2974f27c73STom Joseph namespace pldm 3074f27c73STom Joseph { 3174f27c73STom Joseph namespace requester 3274f27c73STom Joseph { 3374f27c73STom Joseph /** @struct RequestKey 3474f27c73STom Joseph * 3574f27c73STom Joseph * RequestKey uniquely identifies the PLDM request message to match it with the 3674f27c73STom Joseph * response and a combination of MCTP endpoint ID, PLDM instance ID, PLDM type 3774f27c73STom Joseph * and PLDM command is the key. 3874f27c73STom Joseph */ 3974f27c73STom Joseph struct RequestKey 4074f27c73STom Joseph { 4174f27c73STom Joseph mctp_eid_t eid; //!< MCTP endpoint ID 4274f27c73STom Joseph uint8_t instanceId; //!< PLDM instance ID 4374f27c73STom Joseph uint8_t type; //!< PLDM type 4474f27c73STom Joseph uint8_t command; //!< PLDM command 4574f27c73STom Joseph operator ==pldm::requester::RequestKey4674f27c73STom Joseph bool operator==(const RequestKey& e) const 4774f27c73STom Joseph { 4874f27c73STom Joseph return ((eid == e.eid) && (instanceId == e.instanceId) && 4974f27c73STom Joseph (type == e.type) && (command == e.command)); 5074f27c73STom Joseph } 5174f27c73STom Joseph }; 5274f27c73STom Joseph 5374f27c73STom Joseph /** @struct RequestKeyHasher 5474f27c73STom Joseph * 5574f27c73STom Joseph * This is a simple hash function, since the instance ID generator API 5674f27c73STom Joseph * generates unique instance IDs for MCTP endpoint ID. 5774f27c73STom Joseph */ 5874f27c73STom Joseph struct RequestKeyHasher 5974f27c73STom Joseph { operator ()pldm::requester::RequestKeyHasher6074f27c73STom Joseph std::size_t operator()(const RequestKey& key) const 6174f27c73STom Joseph { 6274f27c73STom Joseph return (key.eid << 24 | key.instanceId << 16 | key.type << 8 | 6374f27c73STom Joseph key.command); 6474f27c73STom Joseph } 6574f27c73STom Joseph }; 6674f27c73STom Joseph 674ddee3a0SThu Nguyen using ResponseHandler = std::function<void( 6874f27c73STom Joseph mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)>; 6974f27c73STom Joseph 706b901e4aSThu Nguyen /** @brief The response from SendRecvMsg with coroutine API 716b901e4aSThu Nguyen * 726b901e4aSThu Nguyen * The response when registers PLDM request message using the SendRecvMsg 736b901e4aSThu Nguyen * with coroutine API. 746b901e4aSThu Nguyen * Responded tuple includes <CompleteCode, ResponseMgs, ResponseMsgLength> 756b901e4aSThu Nguyen * Value: [PLDM_ERROR, _, _] if registerRequest fails. 766b901e4aSThu Nguyen * [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out. 776b901e4aSThu Nguyen * [PLDM_SUCCESS, ResponseMsg, ResponseMsgLength] if succeeded 786b901e4aSThu Nguyen */ 796b901e4aSThu Nguyen using SendRecvCoResp = std::tuple<int, const pldm_msg*, size_t>; 806b901e4aSThu Nguyen 814ddee3a0SThu Nguyen /** @struct RegisteredRequest 824ddee3a0SThu Nguyen * 834ddee3a0SThu Nguyen * This struct is used to store the registered request to one endpoint. 844ddee3a0SThu Nguyen */ 854ddee3a0SThu Nguyen struct RegisteredRequest 864ddee3a0SThu Nguyen { 874ddee3a0SThu Nguyen RequestKey key; //!< Responder MCTP endpoint ID 884ddee3a0SThu Nguyen std::vector<uint8_t> reqMsg; //!< Request messages queue 894ddee3a0SThu Nguyen ResponseHandler responseHandler; //!< Waiting for response flag 904ddee3a0SThu Nguyen }; 914ddee3a0SThu Nguyen 924ddee3a0SThu Nguyen /** @struct EndpointMessageQueue 934ddee3a0SThu Nguyen * 944ddee3a0SThu Nguyen * This struct is used to save the list of request messages of one endpoint and 954ddee3a0SThu Nguyen * the existing of the request message to the endpoint with its' EID. 964ddee3a0SThu Nguyen */ 974ddee3a0SThu Nguyen struct EndpointMessageQueue 984ddee3a0SThu Nguyen { 994ddee3a0SThu Nguyen mctp_eid_t eid; //!< Responder MCTP endpoint ID 1004ddee3a0SThu Nguyen std::deque<std::shared_ptr<RegisteredRequest>> requestQueue; //!< Queue 1014ddee3a0SThu Nguyen bool activeRequest; //!< Waiting for response flag 1024ddee3a0SThu Nguyen operator ==pldm::requester::EndpointMessageQueue1034ddee3a0SThu Nguyen bool operator==(const mctp_eid_t& mctpEid) const 1044ddee3a0SThu Nguyen { 1054ddee3a0SThu Nguyen return (eid == mctpEid); 1064ddee3a0SThu Nguyen } 1074ddee3a0SThu Nguyen }; 1084ddee3a0SThu Nguyen 10974f27c73STom Joseph /** @class Handler 11074f27c73STom Joseph * 11174f27c73STom Joseph * This class handles the lifecycle of the PLDM request message based on the 11274f27c73STom Joseph * instance ID expiration interval, number of request retries and the timeout 11374f27c73STom Joseph * waiting for a response. The registered response handlers are invoked with 11474f27c73STom Joseph * response once the PLDM responder sends the response. If no response is 11574f27c73STom Joseph * received within the instance ID expiration interval or any other failure the 11674f27c73STom Joseph * response handler is invoked with the empty response. 11774f27c73STom Joseph * 11874f27c73STom Joseph * @tparam RequestInterface - Request class type 11974f27c73STom Joseph */ 12074f27c73STom Joseph template <class RequestInterface> 12174f27c73STom Joseph class Handler 12274f27c73STom Joseph { 12374f27c73STom Joseph public: 12474f27c73STom Joseph Handler() = delete; 12574f27c73STom Joseph Handler(const Handler&) = delete; 12674f27c73STom Joseph Handler(Handler&&) = delete; 12774f27c73STom Joseph Handler& operator=(const Handler&) = delete; 12874f27c73STom Joseph Handler& operator=(Handler&&) = delete; 12974f27c73STom Joseph ~Handler() = default; 13074f27c73STom Joseph 13174f27c73STom Joseph /** @brief Constructor 13274f27c73STom Joseph * 1331ed5f7a6SRashmica Gupta * @param[in] pldm_transport - PLDM requester 13474f27c73STom Joseph * @param[in] event - reference to PLDM daemon's main event loop 135a330b2f0SAndrew Jeffery * @param[in] instanceIdDb - reference to an InstanceIdDb 136e5268cdaSTom Joseph * @param[in] verbose - verbose tracing flag 13774f27c73STom Joseph * @param[in] instanceIdExpiryInterval - instance ID expiration interval 13874f27c73STom Joseph * @param[in] numRetries - number of request retries 13974f27c73STom Joseph * @param[in] responseTimeOut - time to wait between each retry 14074f27c73STom Joseph */ Handler(PldmTransport * pldmTransport,sdeventplus::Event & event,pldm::InstanceIdDb & instanceIdDb,bool verbose,std::chrono::seconds instanceIdExpiryInterval=std::chrono::seconds (INSTANCE_ID_EXPIRATION_INTERVAL),uint8_t numRetries=static_cast<uint8_t> (NUMBER_OF_REQUEST_RETRIES),std::chrono::milliseconds responseTimeOut=std::chrono::milliseconds (RESPONSE_TIME_OUT))14174f27c73STom Joseph explicit Handler( 1421ed5f7a6SRashmica Gupta PldmTransport* pldmTransport, sdeventplus::Event& event, 1431ed5f7a6SRashmica Gupta pldm::InstanceIdDb& instanceIdDb, bool verbose, 1445079ac4aSBrad Bishop std::chrono::seconds instanceIdExpiryInterval = 1455079ac4aSBrad Bishop std::chrono::seconds(INSTANCE_ID_EXPIRATION_INTERVAL), 14674f27c73STom Joseph uint8_t numRetries = static_cast<uint8_t>(NUMBER_OF_REQUEST_RETRIES), 1475079ac4aSBrad Bishop std::chrono::milliseconds responseTimeOut = 1485079ac4aSBrad Bishop std::chrono::milliseconds(RESPONSE_TIME_OUT)) : 14916c2a0a0SPatrick Williams pldmTransport(pldmTransport), event(event), instanceIdDb(instanceIdDb), 15016c2a0a0SPatrick Williams verbose(verbose), instanceIdExpiryInterval(instanceIdExpiryInterval), 15174f27c73STom Joseph numRetries(numRetries), responseTimeOut(responseTimeOut) 15274f27c73STom Joseph {} 15374f27c73STom Joseph instanceIdExpiryCallBack(RequestKey key)1544ddee3a0SThu Nguyen void instanceIdExpiryCallBack(RequestKey key) 1554ddee3a0SThu Nguyen { 1564ddee3a0SThu Nguyen auto eid = key.eid; 1574ddee3a0SThu Nguyen if (this->handlers.contains(key)) 1584ddee3a0SThu Nguyen { 159087a751fSRiya Dixit info( 160087a751fSRiya Dixit "Instance ID expiry for EID '{EID}' using InstanceID '{INSTANCEID}'", 1611e5c81e0SRiya Dixit "EID", key.eid, "INSTANCEID", key.instanceId); 1624ddee3a0SThu Nguyen auto& [request, responseHandler, 1634ddee3a0SThu Nguyen timerInstance] = this->handlers[key]; 1644ddee3a0SThu Nguyen request->stop(); 1654ddee3a0SThu Nguyen auto rc = timerInstance->stop(); 1664ddee3a0SThu Nguyen if (rc) 1674ddee3a0SThu Nguyen { 168087a751fSRiya Dixit error( 169087a751fSRiya Dixit "Failed to stop the instance ID expiry timer, response code '{RC}'", 1701e5c81e0SRiya Dixit "RC", rc); 1714ddee3a0SThu Nguyen } 1724ddee3a0SThu Nguyen // Call response handler with an empty response to indicate no 1734ddee3a0SThu Nguyen // response 1744ddee3a0SThu Nguyen responseHandler(eid, nullptr, 0); 1754ddee3a0SThu Nguyen this->removeRequestContainer.emplace( 1764ddee3a0SThu Nguyen key, 1774ddee3a0SThu Nguyen std::make_unique<sdeventplus::source::Defer>( 1784ddee3a0SThu Nguyen event, std::bind(&Handler::removeRequestEntry, this, key))); 1794ddee3a0SThu Nguyen endpointMessageQueues[eid]->activeRequest = false; 1804ddee3a0SThu Nguyen 1814ddee3a0SThu Nguyen /* try to send new request if the endpoint is free */ 1824ddee3a0SThu Nguyen pollEndpointQueue(eid); 1834ddee3a0SThu Nguyen } 1844ddee3a0SThu Nguyen else 1854ddee3a0SThu Nguyen { 1864ddee3a0SThu Nguyen // This condition is not possible, if a response is received 1874ddee3a0SThu Nguyen // before the instance ID expiry, then the response handler 1884ddee3a0SThu Nguyen // is executed and the entry will be removed. 1894ddee3a0SThu Nguyen assert(false); 1904ddee3a0SThu Nguyen } 1914ddee3a0SThu Nguyen } 1924ddee3a0SThu Nguyen 1934ddee3a0SThu Nguyen /** @brief Send the remaining PLDM request messages in endpoint queue 1944ddee3a0SThu Nguyen * 1954ddee3a0SThu Nguyen * @param[in] eid - endpoint ID of the remote MCTP endpoint 1964ddee3a0SThu Nguyen */ pollEndpointQueue(mctp_eid_t eid)1974ddee3a0SThu Nguyen int pollEndpointQueue(mctp_eid_t eid) 1984ddee3a0SThu Nguyen { 1994ddee3a0SThu Nguyen if (endpointMessageQueues[eid]->activeRequest || 2004ddee3a0SThu Nguyen endpointMessageQueues[eid]->requestQueue.empty()) 2014ddee3a0SThu Nguyen { 2024ddee3a0SThu Nguyen return PLDM_SUCCESS; 2034ddee3a0SThu Nguyen } 2044ddee3a0SThu Nguyen 2054ddee3a0SThu Nguyen endpointMessageQueues[eid]->activeRequest = true; 2064ddee3a0SThu Nguyen auto requestMsg = endpointMessageQueues[eid]->requestQueue.front(); 2074ddee3a0SThu Nguyen endpointMessageQueues[eid]->requestQueue.pop_front(); 2084ddee3a0SThu Nguyen 2094ddee3a0SThu Nguyen auto request = std::make_unique<RequestInterface>( 2104ddee3a0SThu Nguyen pldmTransport, requestMsg->key.eid, event, 2114ddee3a0SThu Nguyen std::move(requestMsg->reqMsg), numRetries, responseTimeOut, 2124ddee3a0SThu Nguyen verbose); 21335535cf2SPatrick Williams auto timer = std::make_unique<sdbusplus::Timer>( 2144ddee3a0SThu Nguyen event.get(), std::bind(&Handler::instanceIdExpiryCallBack, this, 2154ddee3a0SThu Nguyen requestMsg->key)); 2164ddee3a0SThu Nguyen 2174ddee3a0SThu Nguyen auto rc = request->start(); 2184ddee3a0SThu Nguyen if (rc) 2194ddee3a0SThu Nguyen { 2204ddee3a0SThu Nguyen instanceIdDb.free(requestMsg->key.eid, requestMsg->key.instanceId); 221087a751fSRiya Dixit error( 222087a751fSRiya Dixit "Failure to send the PLDM request message for polling endpoint queue, response code '{RC}'", 223087a751fSRiya Dixit "RC", rc); 2244ddee3a0SThu Nguyen endpointMessageQueues[eid]->activeRequest = false; 2254ddee3a0SThu Nguyen return rc; 2264ddee3a0SThu Nguyen } 2274ddee3a0SThu Nguyen 2284ddee3a0SThu Nguyen try 2294ddee3a0SThu Nguyen { 2304ddee3a0SThu Nguyen timer->start(duration_cast<std::chrono::microseconds>( 2314ddee3a0SThu Nguyen instanceIdExpiryInterval)); 2324ddee3a0SThu Nguyen } 2334ddee3a0SThu Nguyen catch (const std::runtime_error& e) 2344ddee3a0SThu Nguyen { 2354ddee3a0SThu Nguyen instanceIdDb.free(requestMsg->key.eid, requestMsg->key.instanceId); 2364ddee3a0SThu Nguyen error( 237087a751fSRiya Dixit "Failed to start the instance ID expiry timer, error - {ERROR}", 238087a751fSRiya Dixit "ERROR", e); 2394ddee3a0SThu Nguyen endpointMessageQueues[eid]->activeRequest = false; 2404ddee3a0SThu Nguyen return PLDM_ERROR; 2414ddee3a0SThu Nguyen } 2424ddee3a0SThu Nguyen 2434ddee3a0SThu Nguyen handlers.emplace(requestMsg->key, 2444ddee3a0SThu Nguyen std::make_tuple(std::move(request), 2454ddee3a0SThu Nguyen std::move(requestMsg->responseHandler), 2464ddee3a0SThu Nguyen std::move(timer))); 2474ddee3a0SThu Nguyen return PLDM_SUCCESS; 2484ddee3a0SThu Nguyen } 2494ddee3a0SThu Nguyen 25074f27c73STom Joseph /** @brief Register a PLDM request message 25174f27c73STom Joseph * 25274f27c73STom Joseph * @param[in] eid - endpoint ID of the remote MCTP endpoint 25374f27c73STom Joseph * @param[in] instanceId - instance ID to match request and response 25474f27c73STom Joseph * @param[in] type - PLDM type 25574f27c73STom Joseph * @param[in] command - PLDM command 25674f27c73STom Joseph * @param[in] requestMsg - PLDM request message 25774f27c73STom Joseph * @param[in] responseHandler - Response handler for this request 25874f27c73STom Joseph * 25974f27c73STom Joseph * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise 26074f27c73STom Joseph */ registerRequest(mctp_eid_t eid,uint8_t instanceId,uint8_t type,uint8_t command,pldm::Request && requestMsg,ResponseHandler && responseHandler)26174f27c73STom Joseph int registerRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type, 26274f27c73STom Joseph uint8_t command, pldm::Request&& requestMsg, 26374f27c73STom Joseph ResponseHandler&& responseHandler) 26474f27c73STom Joseph { 26574f27c73STom Joseph RequestKey key{eid, instanceId, type, command}; 26674f27c73STom Joseph 267b4e8cfdeSThu Nguyen if (handlers.contains(key)) 268b4e8cfdeSThu Nguyen { 269087a751fSRiya Dixit error( 270087a751fSRiya Dixit "Register request for EID '{EID}' is using InstanceID '{INSTANCEID}'", 2711e5c81e0SRiya Dixit "EID", eid, "INSTANCEID", instanceId); 272b4e8cfdeSThu Nguyen return PLDM_ERROR; 273b4e8cfdeSThu Nguyen } 274b4e8cfdeSThu Nguyen 2754ddee3a0SThu Nguyen auto inputRequest = std::make_shared<RegisteredRequest>( 2764ddee3a0SThu Nguyen key, std::move(requestMsg), std::move(responseHandler)); 2774ddee3a0SThu Nguyen if (endpointMessageQueues.contains(eid)) 27874f27c73STom Joseph { 2794ddee3a0SThu Nguyen endpointMessageQueues[eid]->requestQueue.push_back(inputRequest); 2804ddee3a0SThu Nguyen } 2814ddee3a0SThu Nguyen else 2824ddee3a0SThu Nguyen { 2834ddee3a0SThu Nguyen std::deque<std::shared_ptr<RegisteredRequest>> reqQueue; 2844ddee3a0SThu Nguyen reqQueue.push_back(inputRequest); 2854ddee3a0SThu Nguyen endpointMessageQueues[eid] = 2864ddee3a0SThu Nguyen std::make_shared<EndpointMessageQueue>(eid, reqQueue, false); 28774f27c73STom Joseph } 28874f27c73STom Joseph 2894ddee3a0SThu Nguyen /* try to send new request if the endpoint is free */ 290*4bf3ed84SEric Yang auto rc = pollEndpointQueue(eid); 291*4bf3ed84SEric Yang if (rc != PLDM_SUCCESS) 292*4bf3ed84SEric Yang { 293*4bf3ed84SEric Yang error( 294*4bf3ed84SEric Yang "Failed to process request queue for EID {EID}, response code {RC}.", 295*4bf3ed84SEric Yang "EID", eid, "RC", rc); 296*4bf3ed84SEric Yang return rc; 297*4bf3ed84SEric Yang } 29874f27c73STom Joseph 2994ddee3a0SThu Nguyen return PLDM_SUCCESS; 30074f27c73STom Joseph } 30174f27c73STom Joseph 302a85c69d3SGilbert Chen /** @brief Unregister a PLDM request message 303a85c69d3SGilbert Chen * 304a85c69d3SGilbert Chen * @param[in] eid - endpoint ID of the remote MCTP endpoint 305a85c69d3SGilbert Chen * @param[in] instanceId - instance ID to match request and response 306a85c69d3SGilbert Chen * @param[in] type - PLDM type 307a85c69d3SGilbert Chen * @param[in] command - PLDM command 308a85c69d3SGilbert Chen * 309a85c69d3SGilbert Chen * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise 310a85c69d3SGilbert Chen */ unregisterRequest(mctp_eid_t eid,uint8_t instanceId,uint8_t type,uint8_t command)311a85c69d3SGilbert Chen int unregisterRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type, 312a85c69d3SGilbert Chen uint8_t command) 313a85c69d3SGilbert Chen { 314a85c69d3SGilbert Chen RequestKey key{eid, instanceId, type, command}; 315a85c69d3SGilbert Chen 316a85c69d3SGilbert Chen /* handlers only contain key when the message is already sent */ 317a85c69d3SGilbert Chen if (handlers.contains(key)) 318a85c69d3SGilbert Chen { 319a85c69d3SGilbert Chen auto& [request, responseHandler, timerInstance] = handlers[key]; 320a85c69d3SGilbert Chen request->stop(); 321a85c69d3SGilbert Chen auto rc = timerInstance->stop(); 322a85c69d3SGilbert Chen if (rc) 323a85c69d3SGilbert Chen { 324a85c69d3SGilbert Chen error( 325a85c69d3SGilbert Chen "Failed to stop the instance ID expiry timer, response code '{RC}'", 326a85c69d3SGilbert Chen "RC", static_cast<int>(rc)); 327a85c69d3SGilbert Chen } 328a85c69d3SGilbert Chen 329a85c69d3SGilbert Chen instanceIdDb.free(key.eid, key.instanceId); 330a85c69d3SGilbert Chen handlers.erase(key); 331a85c69d3SGilbert Chen endpointMessageQueues[eid]->activeRequest = false; 332a85c69d3SGilbert Chen /* try to send new request if the endpoint is free */ 333a85c69d3SGilbert Chen pollEndpointQueue(eid); 334a85c69d3SGilbert Chen 335a85c69d3SGilbert Chen return PLDM_SUCCESS; 336a85c69d3SGilbert Chen } 337a85c69d3SGilbert Chen else 338a85c69d3SGilbert Chen { 339a85c69d3SGilbert Chen if (!endpointMessageQueues.contains(eid)) 340a85c69d3SGilbert Chen { 341a85c69d3SGilbert Chen error( 342a85c69d3SGilbert Chen "Can't find request for EID '{EID}' is using InstanceID '{INSTANCEID}' in Endpoint message Queue", 343a85c69d3SGilbert Chen "EID", (unsigned)eid, "INSTANCEID", (unsigned)instanceId); 344a85c69d3SGilbert Chen return PLDM_ERROR; 345a85c69d3SGilbert Chen } 346a85c69d3SGilbert Chen auto requestMsg = endpointMessageQueues[eid]->requestQueue; 347a85c69d3SGilbert Chen /* Find the registered request in the requestQueue */ 348a85c69d3SGilbert Chen for (auto it = requestMsg.begin(); it != requestMsg.end();) 349a85c69d3SGilbert Chen { 350a85c69d3SGilbert Chen auto msg = *it; 351a85c69d3SGilbert Chen if (msg->key == key) 352a85c69d3SGilbert Chen { 353a85c69d3SGilbert Chen // erase and get the next valid iterator 354a85c69d3SGilbert Chen it = endpointMessageQueues[eid]->requestQueue.erase(it); 355a85c69d3SGilbert Chen instanceIdDb.free(key.eid, key.instanceId); 356a85c69d3SGilbert Chen return PLDM_SUCCESS; 357a85c69d3SGilbert Chen } 358a85c69d3SGilbert Chen else 359a85c69d3SGilbert Chen { 360a85c69d3SGilbert Chen ++it; // increment iterator only if not erasing 361a85c69d3SGilbert Chen } 362a85c69d3SGilbert Chen } 363a85c69d3SGilbert Chen } 364a85c69d3SGilbert Chen 365a85c69d3SGilbert Chen return PLDM_ERROR; 366a85c69d3SGilbert Chen } 367a85c69d3SGilbert Chen 36874f27c73STom Joseph /** @brief Handle PLDM response message 36974f27c73STom Joseph * 37074f27c73STom Joseph * @param[in] eid - endpoint ID of the remote MCTP endpoint 37174f27c73STom Joseph * @param[in] instanceId - instance ID to match request and response 37274f27c73STom Joseph * @param[in] type - PLDM type 37374f27c73STom Joseph * @param[in] command - PLDM command 37474f27c73STom Joseph * @param[in] response - PLDM response message 37574f27c73STom Joseph * @param[in] respMsgLen - length of the response message 37674f27c73STom Joseph */ handleResponse(mctp_eid_t eid,uint8_t instanceId,uint8_t type,uint8_t command,const pldm_msg * response,size_t respMsgLen)37774f27c73STom Joseph void handleResponse(mctp_eid_t eid, uint8_t instanceId, uint8_t type, 37874f27c73STom Joseph uint8_t command, const pldm_msg* response, 37974f27c73STom Joseph size_t respMsgLen) 38074f27c73STom Joseph { 38174f27c73STom Joseph RequestKey key{eid, instanceId, type, command}; 382f7d249edSSeanCChuang if (handlers.contains(key) && !removeRequestContainer.contains(key)) 38374f27c73STom Joseph { 38474f27c73STom Joseph auto& [request, responseHandler, timerInstance] = handlers[key]; 38574f27c73STom Joseph request->stop(); 38674f27c73STom Joseph auto rc = timerInstance->stop(); 38774f27c73STom Joseph if (rc) 38874f27c73STom Joseph { 389087a751fSRiya Dixit error( 390087a751fSRiya Dixit "Failed to stop the instance ID expiry timer, response code '{RC}'", 3911e5c81e0SRiya Dixit "RC", rc); 39274f27c73STom Joseph } 39374f27c73STom Joseph responseHandler(eid, response, respMsgLen); 394a330b2f0SAndrew Jeffery instanceIdDb.free(key.eid, key.instanceId); 39574f27c73STom Joseph handlers.erase(key); 3964ddee3a0SThu Nguyen 3974ddee3a0SThu Nguyen endpointMessageQueues[eid]->activeRequest = false; 3984ddee3a0SThu Nguyen /* try to send new request if the endpoint is free */ 3994ddee3a0SThu Nguyen pollEndpointQueue(eid); 40074f27c73STom Joseph } 40174f27c73STom Joseph else 40274f27c73STom Joseph { 40374f27c73STom Joseph // Got a response for a PLDM request message not registered with the 40474f27c73STom Joseph // request handler, so freeing up the instance ID, this can be other 40574f27c73STom Joseph // OpenBMC applications relying on PLDM D-Bus apis like 40674f27c73STom Joseph // openpower-occ-control and softoff 407a330b2f0SAndrew Jeffery instanceIdDb.free(key.eid, key.instanceId); 40874f27c73STom Joseph } 40974f27c73STom Joseph } 41074f27c73STom Joseph 411a85c69d3SGilbert Chen /** @brief Wrap registerRequest with coroutine API. 412a85c69d3SGilbert Chen * 4136b901e4aSThu Nguyen * @return Return [PLDM_ERROR, _, _] if registerRequest fails. 4146b901e4aSThu Nguyen * Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out. 4156b901e4aSThu Nguyen * Return [PLDM_SUCCESS, resp, len] if succeeded 416a85c69d3SGilbert Chen */ 417366507c8SPatrick Williams stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto sendRecvMsg( 418366507c8SPatrick Williams mctp_eid_t eid, pldm::Request&& request); 419a85c69d3SGilbert Chen 42074f27c73STom Joseph private: 4211ed5f7a6SRashmica Gupta PldmTransport* pldmTransport; //!< PLDM transport object 4225079ac4aSBrad Bishop sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop 423a330b2f0SAndrew Jeffery pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb 424e5268cdaSTom Joseph bool verbose; //!< verbose tracing flag 4255079ac4aSBrad Bishop std::chrono::seconds 4265079ac4aSBrad Bishop instanceIdExpiryInterval; //!< Instance ID expiration interval 42774f27c73STom Joseph uint8_t numRetries; //!< number of request retries 4285079ac4aSBrad Bishop std::chrono::milliseconds 4295079ac4aSBrad Bishop responseTimeOut; //!< time to wait between each retry 43074f27c73STom Joseph 431e5268cdaSTom Joseph /** @brief Container for storing the details of the PLDM request 432e5268cdaSTom Joseph * message, handler for the corresponding PLDM response and the 433e5268cdaSTom Joseph * timer object for the Instance ID expiration 43474f27c73STom Joseph */ 4355079ac4aSBrad Bishop using RequestValue = 4365079ac4aSBrad Bishop std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler, 43735535cf2SPatrick Williams std::unique_ptr<sdbusplus::Timer>>; 43874f27c73STom Joseph 4394ddee3a0SThu Nguyen // Manage the requests of responders base on MCTP EID 4404ddee3a0SThu Nguyen std::map<mctp_eid_t, std::shared_ptr<EndpointMessageQueue>> 4414ddee3a0SThu Nguyen endpointMessageQueues; 4424ddee3a0SThu Nguyen 44374f27c73STom Joseph /** @brief Container for storing the PLDM request entries */ 44474f27c73STom Joseph std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers; 44574f27c73STom Joseph 44674f27c73STom Joseph /** @brief Container to store information about the request entries to be 44774f27c73STom Joseph * removed after the instance ID timer expires 44874f27c73STom Joseph */ 4495079ac4aSBrad Bishop std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>, 4505079ac4aSBrad Bishop RequestKeyHasher> 45174f27c73STom Joseph removeRequestContainer; 45274f27c73STom Joseph 45374f27c73STom Joseph /** @brief Remove request entry for which the instance ID expired 45474f27c73STom Joseph * 45574f27c73STom Joseph * @param[in] key - key for the Request 45674f27c73STom Joseph */ removeRequestEntry(RequestKey key)45774f27c73STom Joseph void removeRequestEntry(RequestKey key) 45874f27c73STom Joseph { 45974f27c73STom Joseph if (removeRequestContainer.contains(key)) 46074f27c73STom Joseph { 46174f27c73STom Joseph removeRequestContainer[key].reset(); 462a330b2f0SAndrew Jeffery instanceIdDb.free(key.eid, key.instanceId); 46374f27c73STom Joseph handlers.erase(key); 46474f27c73STom Joseph removeRequestContainer.erase(key); 46574f27c73STom Joseph } 46674f27c73STom Joseph } 46774f27c73STom Joseph }; 46874f27c73STom Joseph 469a85c69d3SGilbert Chen /** @class SendRecvMsgOperation 470a85c69d3SGilbert Chen * 471a85c69d3SGilbert Chen * Represents the state and logic for a single send/receive message operation 472a85c69d3SGilbert Chen * 473a85c69d3SGilbert Chen * @tparam RequestInterface - Request class type 474a85c69d3SGilbert Chen * @tparam stdexec::receiver - Execute receiver 475a85c69d3SGilbert Chen */ 476a85c69d3SGilbert Chen template <class RequestInterface, stdexec::receiver R> 477a85c69d3SGilbert Chen struct SendRecvMsgOperation 478a85c69d3SGilbert Chen { 479a85c69d3SGilbert Chen SendRecvMsgOperation() = delete; 480a85c69d3SGilbert Chen SendRecvMsgOperationpldm::requester::SendRecvMsgOperation481a85c69d3SGilbert Chen explicit SendRecvMsgOperation(Handler<RequestInterface>& handler, 482a85c69d3SGilbert Chen mctp_eid_t eid, pldm::Request&& request, 483a85c69d3SGilbert Chen R&& r) : 48416c2a0a0SPatrick Williams handler(handler), request(std::move(request)), receiver(std::move(r)) 485a85c69d3SGilbert Chen { 486a85c69d3SGilbert Chen auto requestMsg = 487a85c69d3SGilbert Chen reinterpret_cast<const pldm_msg*>(this->request.data()); 488a85c69d3SGilbert Chen requestKey = RequestKey{ 489a85c69d3SGilbert Chen eid, 490a85c69d3SGilbert Chen requestMsg->hdr.instance_id, 491a85c69d3SGilbert Chen requestMsg->hdr.type, 492a85c69d3SGilbert Chen requestMsg->hdr.command, 493a85c69d3SGilbert Chen }; 494a85c69d3SGilbert Chen response = nullptr; 495a85c69d3SGilbert Chen respMsgLen = 0; 496a85c69d3SGilbert Chen } 497a85c69d3SGilbert Chen 498a85c69d3SGilbert Chen /** @brief Checks if the operation has been requested to stop. 499a85c69d3SGilbert Chen * If so, it sets the state to stopped.Registers the request with 500a85c69d3SGilbert Chen * the handler. If registration fails, sets an error on the 501a85c69d3SGilbert Chen * receiver. If stopping is possible, sets up a stop callback. 502a85c69d3SGilbert Chen * 503a85c69d3SGilbert Chen * @param[in] op - operation request 504a85c69d3SGilbert Chen * 505a85c69d3SGilbert Chen * @return Execute errors 506a85c69d3SGilbert Chen */ tag_invoke(stdexec::start_t,SendRecvMsgOperation & op)507a85c69d3SGilbert Chen friend void tag_invoke(stdexec::start_t, SendRecvMsgOperation& op) noexcept 508a85c69d3SGilbert Chen { 509a85c69d3SGilbert Chen auto stopToken = stdexec::get_stop_token(stdexec::get_env(op.receiver)); 510a85c69d3SGilbert Chen 511a85c69d3SGilbert Chen // operation already cancelled 512a85c69d3SGilbert Chen if (stopToken.stop_requested()) 513a85c69d3SGilbert Chen { 514a85c69d3SGilbert Chen return stdexec::set_stopped(std::move(op.receiver)); 515a85c69d3SGilbert Chen } 516a85c69d3SGilbert Chen 517a85c69d3SGilbert Chen using namespace std::placeholders; 518a85c69d3SGilbert Chen auto rc = op.handler.registerRequest( 519a85c69d3SGilbert Chen op.requestKey.eid, op.requestKey.instanceId, op.requestKey.type, 520a85c69d3SGilbert Chen op.requestKey.command, std::move(op.request), 521a85c69d3SGilbert Chen std::bind(&SendRecvMsgOperation::onComplete, &op, _1, _2, _3)); 522a85c69d3SGilbert Chen if (rc) 523a85c69d3SGilbert Chen { 5246b901e4aSThu Nguyen return stdexec::set_value(std::move(op.receiver), rc, 5256b901e4aSThu Nguyen static_cast<const pldm_msg*>(nullptr), 5266b901e4aSThu Nguyen static_cast<size_t>(0)); 527a85c69d3SGilbert Chen } 528a85c69d3SGilbert Chen 529a85c69d3SGilbert Chen if (stopToken.stop_possible()) 530a85c69d3SGilbert Chen { 531a85c69d3SGilbert Chen op.stopCallback.emplace( 532a85c69d3SGilbert Chen std::move(stopToken), 533a85c69d3SGilbert Chen std::bind(&SendRecvMsgOperation::onStop, &op)); 534a85c69d3SGilbert Chen } 535a85c69d3SGilbert Chen } 536a85c69d3SGilbert Chen 537a85c69d3SGilbert Chen /** @brief Unregisters the request and sets the state to stopped on the 538a85c69d3SGilbert Chen * receiver. 539a85c69d3SGilbert Chen */ onStoppldm::requester::SendRecvMsgOperation540a85c69d3SGilbert Chen void onStop() 541a85c69d3SGilbert Chen { 542a85c69d3SGilbert Chen handler.unregisterRequest(requestKey.eid, requestKey.instanceId, 543a85c69d3SGilbert Chen requestKey.type, requestKey.command); 544a85c69d3SGilbert Chen return stdexec::set_stopped(std::move(receiver)); 545a85c69d3SGilbert Chen } 546a85c69d3SGilbert Chen 547a85c69d3SGilbert Chen /** @brief This function resets the stop callback. Validates the response 548a85c69d3SGilbert Chen * and sets either an error or a value on the receiver. 549a85c69d3SGilbert Chen * 550a85c69d3SGilbert Chen * @param[in] eid - endpoint ID of the remote MCTP endpoint 551a85c69d3SGilbert Chen * @param[in] response - PLDM response message 552a85c69d3SGilbert Chen * @param[in] respMsgLen - length of the response message 553a85c69d3SGilbert Chen * 554a85c69d3SGilbert Chen * @return PLDM completion code 555a85c69d3SGilbert Chen */ onCompletepldm::requester::SendRecvMsgOperation556a85c69d3SGilbert Chen void onComplete(mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) 557a85c69d3SGilbert Chen { 558a85c69d3SGilbert Chen stopCallback.reset(); 559a85c69d3SGilbert Chen assert(eid == this->requestKey.eid); 5606b901e4aSThu Nguyen auto rc = PLDM_SUCCESS; 5616b901e4aSThu Nguyen if (!response && !respMsgLen) 562a85c69d3SGilbert Chen { 5636b901e4aSThu Nguyen rc = PLDM_ERROR_NOT_READY; 564a85c69d3SGilbert Chen } 5656b901e4aSThu Nguyen return stdexec::set_value(std::move(receiver), static_cast<int>(rc), 5666b901e4aSThu Nguyen response, respMsgLen); 567a85c69d3SGilbert Chen } 568a85c69d3SGilbert Chen 569a85c69d3SGilbert Chen private: 570a85c69d3SGilbert Chen /** @brief Reference to a Handler object that manages the request/response 571a85c69d3SGilbert Chen * logic. 572a85c69d3SGilbert Chen */ 573a85c69d3SGilbert Chen requester::Handler<RequestInterface>& handler; 574a85c69d3SGilbert Chen 575a85c69d3SGilbert Chen /** @brief Stores information about the request such as eid, instanceId, 576a85c69d3SGilbert Chen * type, and command. 577a85c69d3SGilbert Chen */ 578a85c69d3SGilbert Chen RequestKey requestKey; 579a85c69d3SGilbert Chen 580a85c69d3SGilbert Chen /** @brief The request message to be sent. 581a85c69d3SGilbert Chen */ 582a85c69d3SGilbert Chen pldm::Request request; 583a85c69d3SGilbert Chen 584a85c69d3SGilbert Chen /** @brief The response message for the sent request message. 585a85c69d3SGilbert Chen */ 586a85c69d3SGilbert Chen const pldm_msg* response; 587a85c69d3SGilbert Chen 588a85c69d3SGilbert Chen /** @brief The length of response message for the sent request message. 589a85c69d3SGilbert Chen */ 590a85c69d3SGilbert Chen size_t respMsgLen; 591a85c69d3SGilbert Chen 592a85c69d3SGilbert Chen /** @brief The receiver to be notified with the result of the operation. 593a85c69d3SGilbert Chen */ 594a85c69d3SGilbert Chen R receiver; 595a85c69d3SGilbert Chen 596a85c69d3SGilbert Chen /** @brief An optional callback that handles stopping the operation if 597a85c69d3SGilbert Chen * requested. 598a85c69d3SGilbert Chen */ 599a85c69d3SGilbert Chen std::optional<typename stdexec::stop_token_of_t< 600a85c69d3SGilbert Chen stdexec::env_of_t<R>>::template callback_type<std::function<void()>>> 601a85c69d3SGilbert Chen stopCallback = std::nullopt; 602a85c69d3SGilbert Chen }; 603a85c69d3SGilbert Chen 604a85c69d3SGilbert Chen /** @class SendRecvMsgSender 605a85c69d3SGilbert Chen * 606a85c69d3SGilbert Chen * Represents the single message sender 607a85c69d3SGilbert Chen * 608a85c69d3SGilbert Chen * @tparam RequestInterface - Request class type 609a85c69d3SGilbert Chen */ 610a85c69d3SGilbert Chen template <class RequestInterface> 611a85c69d3SGilbert Chen struct SendRecvMsgSender 612a85c69d3SGilbert Chen { 613a85c69d3SGilbert Chen using is_sender = void; 614a85c69d3SGilbert Chen 615a85c69d3SGilbert Chen SendRecvMsgSender() = delete; 616a85c69d3SGilbert Chen SendRecvMsgSenderpldm::requester::SendRecvMsgSender617a85c69d3SGilbert Chen explicit SendRecvMsgSender(requester::Handler<RequestInterface>& handler, 618a85c69d3SGilbert Chen mctp_eid_t eid, pldm::Request&& request) : 61916c2a0a0SPatrick Williams handler(handler), eid(eid), request(std::move(request)) 620a85c69d3SGilbert Chen {} 621a85c69d3SGilbert Chen 622a85c69d3SGilbert Chen friend auto tag_invoke(stdexec::get_completion_signatures_t, 623a85c69d3SGilbert Chen const SendRecvMsgSender&, auto) 624a85c69d3SGilbert Chen -> stdexec::completion_signatures< 6256b901e4aSThu Nguyen stdexec::set_value_t(int, const pldm_msg*, size_t), 6266b901e4aSThu Nguyen stdexec::set_stopped_t()>; 627a85c69d3SGilbert Chen 628a85c69d3SGilbert Chen /** @brief Execute the sending the request message */ 629a85c69d3SGilbert Chen template <stdexec::receiver R> tag_invoke(stdexec::connect_t,SendRecvMsgSender && self,R r)630a85c69d3SGilbert Chen friend auto tag_invoke(stdexec::connect_t, SendRecvMsgSender&& self, R r) 631a85c69d3SGilbert Chen { 632a85c69d3SGilbert Chen return SendRecvMsgOperation<RequestInterface, R>( 633a85c69d3SGilbert Chen self.handler, self.eid, std::move(self.request), std::move(r)); 634a85c69d3SGilbert Chen } 635a85c69d3SGilbert Chen 636a85c69d3SGilbert Chen private: 637a85c69d3SGilbert Chen /** @brief Reference to a Handler object that manages the request/response 638a85c69d3SGilbert Chen * logic. 639a85c69d3SGilbert Chen */ 640a85c69d3SGilbert Chen requester::Handler<RequestInterface>& handler; 641a85c69d3SGilbert Chen 642a85c69d3SGilbert Chen /** @brief MCTP Endpoint ID of request message */ 643a85c69d3SGilbert Chen mctp_eid_t eid; 644a85c69d3SGilbert Chen 645a85c69d3SGilbert Chen /** @brief Request message */ 646a85c69d3SGilbert Chen pldm::Request request; 647a85c69d3SGilbert Chen }; 648a85c69d3SGilbert Chen 6496b901e4aSThu Nguyen /** @brief Wrap registerRequest with coroutine API. 650a85c69d3SGilbert Chen * 651a85c69d3SGilbert Chen * @param[in] eid - endpoint ID of the remote MCTP endpoint 652a85c69d3SGilbert Chen * @param[in] request - PLDM request message 653a85c69d3SGilbert Chen * 6546b901e4aSThu Nguyen * @return Return [PLDM_ERROR, _, _] if registerRequest fails. 6556b901e4aSThu Nguyen * Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out. 6566b901e4aSThu Nguyen * Return [PLDM_SUCCESS, resp, len] if succeeded 657a85c69d3SGilbert Chen */ 658a85c69d3SGilbert Chen template <class RequestInterface> 6596b901e4aSThu Nguyen stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto sendRecvMsg(mctp_eid_t eid,pldm::Request && request)660a85c69d3SGilbert Chen Handler<RequestInterface>::sendRecvMsg(mctp_eid_t eid, 661a85c69d3SGilbert Chen pldm::Request&& request) 662a85c69d3SGilbert Chen { 663a85c69d3SGilbert Chen return SendRecvMsgSender(*this, eid, std::move(request)) | 6646b901e4aSThu Nguyen stdexec::then([](int rc, const pldm_msg* resp, size_t respLen) { 6656b901e4aSThu Nguyen return std::make_tuple(rc, resp, respLen); 666a85c69d3SGilbert Chen }); 667a85c69d3SGilbert Chen } 668a85c69d3SGilbert Chen 66974f27c73STom Joseph } // namespace requester 67074f27c73STom Joseph 67174f27c73STom Joseph } // namespace pldm 672