xref: /openbmc/pldm/requester/request.hpp (revision 9fffea2c)
174f27c73STom Joseph #pragma once
274f27c73STom Joseph 
374f27c73STom Joseph #include "libpldm/base.h"
474f27c73STom Joseph #include "libpldm/requester/pldm.h"
574f27c73STom Joseph 
674f27c73STom Joseph #include "common/types.hpp"
7e5268cdaSTom Joseph #include "common/utils.hpp"
874f27c73STom Joseph 
9*9fffea2cSManojkiran Eda #include <sys/socket.h>
10*9fffea2cSManojkiran Eda 
1174f27c73STom Joseph #include <sdbusplus/timer.hpp>
1274f27c73STom Joseph #include <sdeventplus/event.hpp>
1374f27c73STom Joseph 
1474f27c73STom Joseph #include <chrono>
1574f27c73STom Joseph #include <functional>
1674f27c73STom Joseph #include <iostream>
1774f27c73STom Joseph 
1874f27c73STom Joseph namespace pldm
1974f27c73STom Joseph {
2074f27c73STom Joseph 
2174f27c73STom Joseph namespace requester
2274f27c73STom Joseph {
2374f27c73STom Joseph 
2474f27c73STom Joseph /** @class RequestRetryTimer
2574f27c73STom Joseph  *
2674f27c73STom Joseph  *  The abstract base class for implementing the PLDM request retry logic. This
2774f27c73STom Joseph  *  class handles number of times the PLDM request needs to be retried if the
2874f27c73STom Joseph  *  response is not received and the time to wait between each retry. It
2974f27c73STom Joseph  *  provides APIs to start and stop the request flow.
3074f27c73STom Joseph  */
3174f27c73STom Joseph class RequestRetryTimer
3274f27c73STom Joseph {
3374f27c73STom Joseph   public:
3474f27c73STom Joseph     RequestRetryTimer() = delete;
3574f27c73STom Joseph     RequestRetryTimer(const RequestRetryTimer&) = delete;
3674f27c73STom Joseph     RequestRetryTimer(RequestRetryTimer&&) = default;
3774f27c73STom Joseph     RequestRetryTimer& operator=(const RequestRetryTimer&) = delete;
3874f27c73STom Joseph     RequestRetryTimer& operator=(RequestRetryTimer&&) = default;
3974f27c73STom Joseph     virtual ~RequestRetryTimer() = default;
4074f27c73STom Joseph 
4174f27c73STom Joseph     /** @brief Constructor
4274f27c73STom Joseph      *
4374f27c73STom Joseph      *  @param[in] event - reference to PLDM daemon's main event loop
4474f27c73STom Joseph      *  @param[in] numRetries - number of request retries
4574f27c73STom Joseph      *  @param[in] timeout - time to wait between each retry in milliseconds
4674f27c73STom Joseph      */
4774f27c73STom Joseph     explicit RequestRetryTimer(sdeventplus::Event& event, uint8_t numRetries,
485079ac4aSBrad Bishop                                std::chrono::milliseconds timeout) :
4974f27c73STom Joseph 
5074f27c73STom Joseph         event(event),
5174f27c73STom Joseph         numRetries(numRetries), timeout(timeout),
5274f27c73STom Joseph         timer(event.get(), std::bind_front(&RequestRetryTimer::callback, this))
5374f27c73STom Joseph     {}
5474f27c73STom Joseph 
5574f27c73STom Joseph     /** @brief Starts the request flow and arms the timer for request retries
5674f27c73STom Joseph      *
5774f27c73STom Joseph      *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
5874f27c73STom Joseph      */
5974f27c73STom Joseph     int start()
6074f27c73STom Joseph     {
6174f27c73STom Joseph         auto rc = send();
62a5ed6585STom Joseph         if (rc)
6374f27c73STom Joseph         {
6474f27c73STom Joseph             return rc;
6574f27c73STom Joseph         }
6674f27c73STom Joseph 
6774f27c73STom Joseph         try
6874f27c73STom Joseph         {
6974f27c73STom Joseph             if (numRetries)
7074f27c73STom Joseph             {
715079ac4aSBrad Bishop                 timer.start(duration_cast<std::chrono::microseconds>(timeout),
725079ac4aSBrad Bishop                             true);
7374f27c73STom Joseph             }
7474f27c73STom Joseph         }
7574f27c73STom Joseph         catch (const std::runtime_error& e)
7674f27c73STom Joseph         {
7774f27c73STom Joseph             std::cerr << "Failed to start the request timer. RC = " << e.what()
7874f27c73STom Joseph                       << "\n";
7974f27c73STom Joseph             return PLDM_ERROR;
8074f27c73STom Joseph         }
8174f27c73STom Joseph 
8274f27c73STom Joseph         return PLDM_SUCCESS;
8374f27c73STom Joseph     }
8474f27c73STom Joseph 
8574f27c73STom Joseph     /** @brief Stops the timer and no further request retries happen */
8674f27c73STom Joseph     void stop()
8774f27c73STom Joseph     {
8874f27c73STom Joseph         auto rc = timer.stop();
8974f27c73STom Joseph         if (rc)
9074f27c73STom Joseph         {
9174f27c73STom Joseph             std::cerr << "Failed to stop the request timer. RC = " << rc
9274f27c73STom Joseph                       << "\n";
9374f27c73STom Joseph         }
9474f27c73STom Joseph     }
9574f27c73STom Joseph 
9674f27c73STom Joseph   protected:
9774f27c73STom Joseph     sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
9874f27c73STom Joseph     uint8_t numRetries;        //!< number of request retries
995079ac4aSBrad Bishop     std::chrono::milliseconds
1005079ac4aSBrad Bishop         timeout;           //!< time to wait between each retry in milliseconds
10174f27c73STom Joseph     phosphor::Timer timer; //!< manages starting timers and handling timeouts
10274f27c73STom Joseph 
10374f27c73STom Joseph     /** @brief Sends the PLDM request message
10474f27c73STom Joseph      *
10574f27c73STom Joseph      *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
10674f27c73STom Joseph      */
10774f27c73STom Joseph     virtual int send() const = 0;
10874f27c73STom Joseph 
10974f27c73STom Joseph     /** @brief Callback function invoked when the timeout happens */
11074f27c73STom Joseph     void callback()
11174f27c73STom Joseph     {
11274f27c73STom Joseph         if (numRetries--)
11374f27c73STom Joseph         {
11474f27c73STom Joseph             send();
11574f27c73STom Joseph         }
11674f27c73STom Joseph         else
11774f27c73STom Joseph         {
11874f27c73STom Joseph             stop();
11974f27c73STom Joseph         }
12074f27c73STom Joseph     }
12174f27c73STom Joseph };
12274f27c73STom Joseph 
12374f27c73STom Joseph /** @class Request
12474f27c73STom Joseph  *
12574f27c73STom Joseph  *  The concrete implementation of RequestIntf. This class implements the send()
12674f27c73STom Joseph  *  to send the PLDM request message over MCTP socket.
12774f27c73STom Joseph  *  This class encapsulates the PLDM request message, the number of times the
12874f27c73STom Joseph  *  request needs to retried if the response is not received and the amount of
12974f27c73STom Joseph  *  time to wait between each retry. It provides APIs to start and stop the
13074f27c73STom Joseph  *  request flow.
13174f27c73STom Joseph  */
13274f27c73STom Joseph class Request final : public RequestRetryTimer
13374f27c73STom Joseph {
13474f27c73STom Joseph   public:
13574f27c73STom Joseph     Request() = delete;
13674f27c73STom Joseph     Request(const Request&) = delete;
13774f27c73STom Joseph     Request(Request&&) = default;
13874f27c73STom Joseph     Request& operator=(const Request&) = delete;
13974f27c73STom Joseph     Request& operator=(Request&&) = default;
14074f27c73STom Joseph     ~Request() = default;
14174f27c73STom Joseph 
14274f27c73STom Joseph     /** @brief Constructor
14374f27c73STom Joseph      *
14474f27c73STom Joseph      *  @param[in] fd - fd of the MCTP communication socket
14574f27c73STom Joseph      *  @param[in] eid - endpoint ID of the remote MCTP endpoint
146*9fffea2cSManojkiran Eda      *  @param[in] currrentSendbuffSize - the current send buffer size
14774f27c73STom Joseph      *  @param[in] event - reference to PLDM daemon's main event loop
14874f27c73STom Joseph      *  @param[in] requestMsg - PLDM request message
14974f27c73STom Joseph      *  @param[in] numRetries - number of request retries
15074f27c73STom Joseph      *  @param[in] timeout - time to wait between each retry in milliseconds
151e5268cdaSTom Joseph      *  @param[in] verbose - verbose tracing flag
15274f27c73STom Joseph      */
15374f27c73STom Joseph     explicit Request(int fd, mctp_eid_t eid, sdeventplus::Event& event,
15474f27c73STom Joseph                      pldm::Request&& requestMsg, uint8_t numRetries,
155*9fffea2cSManojkiran Eda                      std::chrono::milliseconds timeout, int currentSendbuffSize,
156*9fffea2cSManojkiran Eda                      bool verbose) :
15774f27c73STom Joseph         RequestRetryTimer(event, numRetries, timeout),
158*9fffea2cSManojkiran Eda         fd(fd), eid(eid), requestMsg(std::move(requestMsg)),
159*9fffea2cSManojkiran Eda         currentSendbuffSize(currentSendbuffSize), verbose(verbose)
16074f27c73STom Joseph     {}
16174f27c73STom Joseph 
16274f27c73STom Joseph   private:
16374f27c73STom Joseph     int fd;                   //!< file descriptor of MCTP communications socket
16474f27c73STom Joseph     mctp_eid_t eid;           //!< endpoint ID of the remote MCTP endpoint
16542336b5cSSampa Misra     pldm::Request requestMsg; //!< PLDM request message
166*9fffea2cSManojkiran Eda     mutable int currentSendbuffSize; //!< current Send Buffer size
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      */
17374f27c73STom Joseph     int send() const
17474f27c73STom Joseph     {
175e5268cdaSTom Joseph         if (verbose)
176e5268cdaSTom Joseph         {
177e5268cdaSTom Joseph             pldm::utils::printBuffer(pldm::utils::Tx, requestMsg);
178e5268cdaSTom Joseph         }
179*9fffea2cSManojkiran Eda         if (currentSendbuffSize >= 0 &&
180*9fffea2cSManojkiran Eda             (size_t)currentSendbuffSize < requestMsg.size())
181*9fffea2cSManojkiran Eda         {
182*9fffea2cSManojkiran Eda             int oldSendbuffSize = currentSendbuffSize;
183*9fffea2cSManojkiran Eda             currentSendbuffSize = requestMsg.size();
184*9fffea2cSManojkiran Eda             int res =
185*9fffea2cSManojkiran Eda                 setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &currentSendbuffSize,
186*9fffea2cSManojkiran Eda                            sizeof(currentSendbuffSize));
187*9fffea2cSManojkiran Eda             if (res == -1)
188*9fffea2cSManojkiran Eda             {
189*9fffea2cSManojkiran Eda                 std::cerr
190*9fffea2cSManojkiran Eda                     << "Requester : Failed to set the new send buffer size [bytes] : "
191*9fffea2cSManojkiran Eda                     << currentSendbuffSize
192*9fffea2cSManojkiran Eda                     << " from current size [bytes]: " << oldSendbuffSize
193*9fffea2cSManojkiran Eda                     << " , Error : " << strerror(errno) << std::endl;
194*9fffea2cSManojkiran Eda                 return PLDM_ERROR;
195*9fffea2cSManojkiran Eda             }
196*9fffea2cSManojkiran Eda         }
19774f27c73STom Joseph         auto rc = pldm_send(eid, fd, requestMsg.data(), requestMsg.size());
19874f27c73STom Joseph         if (rc < 0)
19974f27c73STom Joseph         {
20074f27c73STom Joseph             std::cerr << "Failed to send PLDM message. RC = " << rc
20174f27c73STom Joseph                       << ", errno = " << errno << "\n";
20274f27c73STom Joseph             return PLDM_ERROR;
20374f27c73STom Joseph         }
20474f27c73STom Joseph         return PLDM_SUCCESS;
20574f27c73STom Joseph     }
20674f27c73STom Joseph };
20774f27c73STom Joseph 
20874f27c73STom Joseph } // namespace requester
20974f27c73STom Joseph 
21074f27c73STom Joseph } // namespace pldm
211