xref: /openbmc/pldm/requester/handler.hpp (revision 4bf3ed840f13c31720ffa7c255942f3836402f4f)
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