xref: /openbmc/pldm/requester/request.hpp (revision 1e5c81e0)
174f27c73STom Joseph #pragma once
274f27c73STom Joseph 
3ef773059SManojkiran Eda #include "common/flight_recorder.hpp"
41ed5f7a6SRashmica Gupta #include "common/transport.hpp"
574f27c73STom Joseph #include "common/types.hpp"
6e5268cdaSTom Joseph #include "common/utils.hpp"
774f27c73STom Joseph 
8c453e164SGeorge Liu #include <libpldm/base.h>
99fffea2cSManojkiran Eda #include <sys/socket.h>
109fffea2cSManojkiran Eda 
1149cfb138SRiya Dixit #include <phosphor-logging/lg2.hpp>
1274f27c73STom Joseph #include <sdbusplus/timer.hpp>
1374f27c73STom Joseph #include <sdeventplus/event.hpp>
1474f27c73STom Joseph 
1574f27c73STom Joseph #include <chrono>
1674f27c73STom Joseph #include <functional>
1774f27c73STom Joseph #include <iostream>
1874f27c73STom Joseph 
1949cfb138SRiya Dixit PHOSPHOR_LOG2_USING;
2049cfb138SRiya Dixit 
2174f27c73STom Joseph namespace pldm
2274f27c73STom Joseph {
2374f27c73STom Joseph namespace requester
2474f27c73STom Joseph {
2574f27c73STom Joseph /** @class RequestRetryTimer
2674f27c73STom Joseph  *
2774f27c73STom Joseph  *  The abstract base class for implementing the PLDM request retry logic. This
2874f27c73STom Joseph  *  class handles number of times the PLDM request needs to be retried if the
2974f27c73STom Joseph  *  response is not received and the time to wait between each retry. It
3074f27c73STom Joseph  *  provides APIs to start and stop the request flow.
3174f27c73STom Joseph  */
3274f27c73STom Joseph class RequestRetryTimer
3374f27c73STom Joseph {
3474f27c73STom Joseph   public:
3574f27c73STom Joseph     RequestRetryTimer() = delete;
3674f27c73STom Joseph     RequestRetryTimer(const RequestRetryTimer&) = delete;
37a7dbca53SPavithra Barithaya     RequestRetryTimer(RequestRetryTimer&&) = delete;
3874f27c73STom Joseph     RequestRetryTimer& operator=(const RequestRetryTimer&) = delete;
39a7dbca53SPavithra Barithaya     RequestRetryTimer& operator=(RequestRetryTimer&&) = delete;
4074f27c73STom Joseph     virtual ~RequestRetryTimer() = default;
4174f27c73STom Joseph 
4274f27c73STom Joseph     /** @brief Constructor
4374f27c73STom Joseph      *
4474f27c73STom Joseph      *  @param[in] event - reference to PLDM daemon's main event loop
4574f27c73STom Joseph      *  @param[in] numRetries - number of request retries
4674f27c73STom Joseph      *  @param[in] timeout - time to wait between each retry in milliseconds
4774f27c73STom Joseph      */
RequestRetryTimer(sdeventplus::Event & event,uint8_t numRetries,std::chrono::milliseconds timeout)4874f27c73STom Joseph     explicit RequestRetryTimer(sdeventplus::Event& event, uint8_t numRetries,
495079ac4aSBrad Bishop                                std::chrono::milliseconds timeout) :
5074f27c73STom Joseph 
5174f27c73STom Joseph         event(event),
5274f27c73STom Joseph         numRetries(numRetries), timeout(timeout),
5374f27c73STom Joseph         timer(event.get(), std::bind_front(&RequestRetryTimer::callback, this))
5474f27c73STom Joseph     {}
5574f27c73STom Joseph 
5674f27c73STom Joseph     /** @brief Starts the request flow and arms the timer for request retries
5774f27c73STom Joseph      *
5874f27c73STom Joseph      *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
5974f27c73STom Joseph      */
start()6074f27c73STom Joseph     int start()
6174f27c73STom Joseph     {
6274f27c73STom Joseph         auto rc = send();
63a5ed6585STom Joseph         if (rc)
6474f27c73STom Joseph         {
6574f27c73STom Joseph             return rc;
6674f27c73STom Joseph         }
6774f27c73STom Joseph 
6874f27c73STom Joseph         try
6974f27c73STom Joseph         {
7074f27c73STom Joseph             if (numRetries)
7174f27c73STom Joseph             {
725079ac4aSBrad Bishop                 timer.start(duration_cast<std::chrono::microseconds>(timeout),
735079ac4aSBrad Bishop                             true);
7474f27c73STom Joseph             }
7574f27c73STom Joseph         }
7674f27c73STom Joseph         catch (const std::runtime_error& e)
7774f27c73STom Joseph         {
78087a751fSRiya Dixit             error("Failed to start the request timer, error - {ERROR}", "ERROR",
79087a751fSRiya Dixit                   e);
8074f27c73STom Joseph             return PLDM_ERROR;
8174f27c73STom Joseph         }
8274f27c73STom Joseph 
8374f27c73STom Joseph         return PLDM_SUCCESS;
8474f27c73STom Joseph     }
8574f27c73STom Joseph 
8674f27c73STom Joseph     /** @brief Stops the timer and no further request retries happen */
stop()8774f27c73STom Joseph     void stop()
8874f27c73STom Joseph     {
8974f27c73STom Joseph         auto rc = timer.stop();
9074f27c73STom Joseph         if (rc)
9174f27c73STom Joseph         {
92087a751fSRiya Dixit             error("Failed to stop the request timer, response code '{RC}'",
93*1e5c81e0SRiya Dixit                   "RC", rc);
9474f27c73STom Joseph         }
9574f27c73STom Joseph     }
9674f27c73STom Joseph 
9774f27c73STom Joseph   protected:
9874f27c73STom Joseph     sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
9974f27c73STom Joseph     uint8_t numRetries;        //!< number of request retries
1005079ac4aSBrad Bishop     std::chrono::milliseconds
1015079ac4aSBrad Bishop         timeout;            //!< time to wait between each retry in milliseconds
10235535cf2SPatrick Williams     sdbusplus::Timer timer; //!< manages starting timers and handling timeouts
10374f27c73STom Joseph 
10474f27c73STom Joseph     /** @brief Sends the PLDM request message
10574f27c73STom Joseph      *
10674f27c73STom Joseph      *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
10774f27c73STom Joseph      */
10874f27c73STom Joseph     virtual int send() const = 0;
10974f27c73STom Joseph 
11074f27c73STom Joseph     /** @brief Callback function invoked when the timeout happens */
callback()11174f27c73STom Joseph     void callback()
11274f27c73STom Joseph     {
11374f27c73STom Joseph         if (numRetries--)
11474f27c73STom Joseph         {
11574f27c73STom Joseph             send();
11674f27c73STom Joseph         }
11774f27c73STom Joseph         else
11874f27c73STom Joseph         {
11974f27c73STom Joseph             stop();
12074f27c73STom Joseph         }
12174f27c73STom Joseph     }
12274f27c73STom Joseph };
12374f27c73STom Joseph 
12474f27c73STom Joseph /** @class Request
12574f27c73STom Joseph  *
12674f27c73STom Joseph  *  The concrete implementation of RequestIntf. This class implements the send()
12774f27c73STom Joseph  *  to send the PLDM request message over MCTP socket.
12874f27c73STom Joseph  *  This class encapsulates the PLDM request message, the number of times the
12974f27c73STom Joseph  *  request needs to retried if the response is not received and the amount of
13074f27c73STom Joseph  *  time to wait between each retry. It provides APIs to start and stop the
13174f27c73STom Joseph  *  request flow.
13274f27c73STom Joseph  */
13374f27c73STom Joseph class Request final : public RequestRetryTimer
13474f27c73STom Joseph {
13574f27c73STom Joseph   public:
13674f27c73STom Joseph     Request() = delete;
13774f27c73STom Joseph     Request(const Request&) = delete;
138a7dbca53SPavithra Barithaya     Request(Request&&) = delete;
13974f27c73STom Joseph     Request& operator=(const Request&) = delete;
140a7dbca53SPavithra Barithaya     Request& operator=(Request&&) = delete;
14174f27c73STom Joseph     ~Request() = default;
14274f27c73STom Joseph 
14374f27c73STom Joseph     /** @brief Constructor
14474f27c73STom Joseph      *
1451ed5f7a6SRashmica Gupta      *  @param[in] pldm_transport - PLDM transport object
14674f27c73STom Joseph      *  @param[in] eid - endpoint ID of the remote MCTP endpoint
1479fffea2cSManojkiran Eda      *  @param[in] currrentSendbuffSize - the current send buffer size
14874f27c73STom Joseph      *  @param[in] event - reference to PLDM daemon's main event loop
14974f27c73STom Joseph      *  @param[in] requestMsg - PLDM request message
15074f27c73STom Joseph      *  @param[in] numRetries - number of request retries
15174f27c73STom Joseph      *  @param[in] timeout - time to wait between each retry in milliseconds
152e5268cdaSTom Joseph      *  @param[in] verbose - verbose tracing flag
15374f27c73STom Joseph      */
Request(PldmTransport * pldmTransport,mctp_eid_t eid,sdeventplus::Event & event,pldm::Request && requestMsg,uint8_t numRetries,std::chrono::milliseconds timeout,bool verbose)1541ed5f7a6SRashmica Gupta     explicit Request(PldmTransport* pldmTransport, mctp_eid_t eid,
1551ed5f7a6SRashmica Gupta                      sdeventplus::Event& event, pldm::Request&& requestMsg,
1561ed5f7a6SRashmica Gupta                      uint8_t numRetries, std::chrono::milliseconds timeout,
1579fffea2cSManojkiran Eda                      bool verbose) :
15874f27c73STom Joseph         RequestRetryTimer(event, numRetries, timeout),
1591ed5f7a6SRashmica Gupta         pldmTransport(pldmTransport), eid(eid),
1601ed5f7a6SRashmica Gupta         requestMsg(std::move(requestMsg)), verbose(verbose)
16174f27c73STom Joseph     {}
16274f27c73STom Joseph 
16374f27c73STom Joseph   private:
1641ed5f7a6SRashmica Gupta     PldmTransport* pldmTransport; //!< PLDM transport
16574f27c73STom Joseph     mctp_eid_t eid;               //!< endpoint ID of the remote MCTP endpoint
16642336b5cSSampa Misra     pldm::Request requestMsg;     //!< PLDM request message
167e5268cdaSTom Joseph     bool verbose;                 //!< verbose tracing flag
16874f27c73STom Joseph 
16974f27c73STom Joseph     /** @brief Sends the PLDM request message on the socket
17074f27c73STom Joseph      *
17174f27c73STom Joseph      *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
17274f27c73STom Joseph      */
send() const17374f27c73STom Joseph     int send() const
17474f27c73STom Joseph     {
175e5268cdaSTom Joseph         if (verbose)
176e5268cdaSTom Joseph         {
177e5268cdaSTom Joseph             pldm::utils::printBuffer(pldm::utils::Tx, requestMsg);
178e5268cdaSTom Joseph         }
179ef773059SManojkiran Eda         pldm::flightrecorder::FlightRecorder::GetInstance().saveRecord(
180ef773059SManojkiran Eda             requestMsg, true);
1811ed5f7a6SRashmica Gupta         const struct pldm_msg_hdr* hdr =
1821ed5f7a6SRashmica Gupta             (struct pldm_msg_hdr*)(requestMsg.data());
1831ed5f7a6SRashmica Gupta         if (!hdr->request)
1841ed5f7a6SRashmica Gupta         {
1851ed5f7a6SRashmica Gupta             return PLDM_REQUESTER_NOT_REQ_MSG;
1861ed5f7a6SRashmica Gupta         }
1871ed5f7a6SRashmica Gupta 
1881ed5f7a6SRashmica Gupta         if (pldmTransport == nullptr)
1891ed5f7a6SRashmica Gupta         {
1901ed5f7a6SRashmica Gupta             error("Invalid transport: Unable to send PLDM request");
1911ed5f7a6SRashmica Gupta             return PLDM_ERROR;
1921ed5f7a6SRashmica Gupta         }
1931ed5f7a6SRashmica Gupta 
1941ed5f7a6SRashmica Gupta         auto rc = pldmTransport->sendMsg(static_cast<pldm_tid_t>(eid),
1951ed5f7a6SRashmica Gupta                                          requestMsg.data(), requestMsg.size());
19674f27c73STom Joseph         if (rc < 0)
19774f27c73STom Joseph         {
198087a751fSRiya Dixit             error(
199087a751fSRiya Dixit                 "Failed to send pldmTransport message, response code '{RC}' and error - {ERROR}",
200*1e5c81e0SRiya Dixit                 "RC", rc, "ERROR", errno);
20174f27c73STom Joseph             return PLDM_ERROR;
20274f27c73STom Joseph         }
20374f27c73STom Joseph         return PLDM_SUCCESS;
20474f27c73STom Joseph     }
20574f27c73STom Joseph };
20674f27c73STom Joseph 
20774f27c73STom Joseph } // namespace requester
20874f27c73STom Joseph 
20974f27c73STom Joseph } // namespace pldm
210