diff options
author | Vernon Mauery <vernon.mauery@linux.intel.com> | 2018-11-09 08:43:24 -0800 |
---|---|---|
committer | Vernon Mauery <vernon.mauery@linux.intel.com> | 2019-02-01 13:01:18 -0800 |
commit | 6f353e868d6fef90e35ef13da7c7ae912becb97a (patch) | |
tree | 1c6f6ab825c931cdd1d31e97eaa462b49c6f964b | |
parent | 7e4a651799df95e44b4c99825adf18a8ef878f52 (diff) | |
download | phosphor-net-ipmid-6f353e868d6fef90e35ef13da7c7ae912becb97a.tar.gz phosphor-net-ipmid-6f353e868d6fef90e35ef13da7c7ae912becb97a.zip |
netipmid: move sol timers to asio
The IPMI SOL console was using sd_event-based timers directly (without
any abstraction). This moves to a much higher level abstraction that is
very easy to use: asio timers.
Change-Id: Id5df76a1918cdfae420e01884d664234810b7abd
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
-rw-r--r-- | sd_event_loop.cpp | 230 | ||||
-rw-r--r-- | sd_event_loop.hpp | 100 | ||||
-rw-r--r-- | sol/sol_context.cpp | 133 | ||||
-rw-r--r-- | sol/sol_context.hpp | 54 | ||||
-rw-r--r-- | sol/sol_manager.cpp | 12 |
5 files changed, 154 insertions, 375 deletions
diff --git a/sd_event_loop.cpp b/sd_event_loop.cpp index 8d24d23..5be974a 100644 --- a/sd_event_loop.cpp +++ b/sd_event_loop.cpp @@ -61,77 +61,6 @@ void EventLoop::startRmcpReceive() }); } -static int charAccTimerHandler(sd_event_source* s, uint64_t usec, - void* userdata) -{ - auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size(); - - try - { - // The instance is hardcoded to 1, in the case of supporting multiple - // payload instances we would need to populate it from userdata - uint8_t instance = 1; - - if (bufferSize > 0) - { - auto& context = - std::get<sol::Manager&>(singletonPool).getContext(instance); - - int rc = context.sendOutboundPayload(); - - if (rc == 0) - { - return 0; - } - } - - std::get<eventloop::EventLoop&>(singletonPool) - .switchTimer(instance, Timers::ACCUMULATE, true); - } - catch (std::exception& e) - { - log<level::ERR>(e.what()); - } - - return 0; -} - -static int retryTimerHandler(sd_event_source* s, uint64_t usec, void* userdata) -{ - try - { - // The instance is hardcoded to 1, in the case of supporting multiple - // payload instances we would need to populate it from userdata - uint8_t instance = 1; - - auto& context = - std::get<sol::Manager&>(singletonPool).getContext(instance); - - if (context.retryCounter) - { - --context.retryCounter; - std::get<eventloop::EventLoop&>(singletonPool) - .switchTimer(instance, Timers::RETRY, true); - context.resendPayload(sol::Context::noClear); - } - else - { - context.retryCounter = context.maxRetryCount; - context.resendPayload(sol::Context::clear); - std::get<eventloop::EventLoop&>(singletonPool) - .switchTimer(instance, Timers::RETRY, false); - std::get<eventloop::EventLoop&>(singletonPool) - .switchTimer(instance, Timers::ACCUMULATE, true); - } - } - catch (std::exception& e) - { - log<level::ERR>(e.what()); - } - - return 0; -} - int EventLoop::startEventLoop() { sdbusplus::asio::sd_event_wrapper sdEvents(*io); @@ -179,163 +108,4 @@ int EventLoop::startEventLoop() return EXIT_SUCCESS; } -void EventLoop::startSOLPayloadInstance(uint8_t payloadInst, - IntervalType accumulateInterval, - IntervalType retryInterval) -{ - auto instance = payloadInst; - sd_event_source* accTimerSource = nullptr; - sd_event_source* retryTimerSource = nullptr; - int rc = 0; - uint64_t currentTime = 0; - - rc = sd_event_now(event, CLOCK_MONOTONIC, ¤tTime); - if (rc < 0) - { - log<level::ERR>("Failed to get the current timestamp", - entry("RC=%d", rc)); - throw std::runtime_error("Failed to get current timestamp"); - } - - // Create character accumulate timer - rc = sd_event_add_time(event, &accTimerSource, CLOCK_MONOTONIC, - currentTime + accumulateInterval.count(), 0, - charAccTimerHandler, static_cast<void*>(&instance)); - if (rc < 0) - { - log<level::ERR>("Failed to setup the accumulate timer", - entry("RC=%d", rc)); - throw std::runtime_error("Failed to setup accumulate timer"); - } - - // Create retry interval timer and add to the event loop - rc = sd_event_add_time(event, &retryTimerSource, CLOCK_MONOTONIC, - currentTime + retryInterval.count(), 0, - retryTimerHandler, static_cast<void*>(&instance)); - if (rc < 0) - { - log<level::ERR>("Failed to setup the retry timer", entry("RC=%d", rc)); - throw std::runtime_error("Failed to setup retry timer"); - } - - // Enable the Character Accumulate Timer - rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT); - if (rc < 0) - { - log<level::ERR>("Failed to enable the accumulate timer", - entry("RC=%d", rc)); - throw std::runtime_error("Failed to enable accumulate timer"); - } - - // Disable the Retry Interval Timer - rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF); - if (rc < 0) - { - log<level::ERR>("Failed to disable the retry timer", - entry("RC=%d", rc)); - throw std::runtime_error("Failed to disable retry timer"); - } - - EventSource accEventSource(accTimerSource); - EventSource retryEventSource(retryTimerSource); - accTimerSource = nullptr; - retryTimerSource = nullptr; - - TimerMap timer; - timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource), - accumulateInterval)); - timer.emplace(Timers::RETRY, - std::make_tuple(std::move(retryEventSource), retryInterval)); - payloadInfo.emplace(instance, std::move(timer)); -} - -void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst) -{ - auto iter = payloadInfo.find(payloadInst); - if (iter == payloadInfo.end()) - { - log<level::ERR>("SOL Payload instance not found", - entry("PAYLOADINST=%d", payloadInst)); - throw std::runtime_error("SOL payload instance not found"); - } - - int rc = 0; - - /* Destroy the character accumulate timer event source */ - rc = sd_event_source_set_enabled( - (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(), SD_EVENT_OFF); - if (rc < 0) - { - log<level::ERR>("Failed to disable the character accumulate timer", - entry("RC=%d", rc)); - payloadInfo.erase(payloadInst); - throw std::runtime_error("Failed to disable accumulate timer"); - } - - /* Destroy the retry interval timer event source */ - rc = sd_event_source_set_enabled( - (std::get<0>(iter->second.at(Timers::RETRY))).get(), SD_EVENT_OFF); - if (rc < 0) - { - log<level::ERR>("Failed to disable the retry timer", - entry("RC=%d", rc)); - payloadInfo.erase(payloadInst); - throw std::runtime_error("Failed to disable retry timer"); - } - - payloadInfo.erase(payloadInst); -} - -void EventLoop::switchTimer(uint8_t payloadInst, Timers type, bool status) -{ - auto iter = payloadInfo.find(payloadInst); - if (iter == payloadInfo.end()) - { - log<level::ERR>("SOL Payload instance not found", - entry("PAYLOADINST=%d", payloadInst)); - throw std::runtime_error("SOL Payload instance not found"); - } - - int rc = 0; - auto source = (std::get<0>(iter->second.at(type))).get(); - auto interval = std::get<1>(iter->second.at(type)); - - // Turn OFF the timer - if (!status) - { - rc = sd_event_source_set_enabled(source, SD_EVENT_OFF); - if (rc < 0) - { - log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc)); - throw std::runtime_error("Failed to disable timer"); - } - return; - } - - // Turn ON the timer - uint64_t currentTime = 0; - rc = sd_event_now(event, CLOCK_MONOTONIC, ¤tTime); - if (rc < 0) - { - log<level::ERR>("Failed to get the current timestamp", - entry("RC=%d", rc)); - throw std::runtime_error("Failed to get current timestamp"); - } - - rc = sd_event_source_set_time(source, currentTime + interval.count()); - if (rc < 0) - { - log<level::ERR>("sd_event_source_set_time function failed", - entry("RC=%d", rc)); - throw std::runtime_error("sd_event_source_set_time function failed"); - } - - rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); - if (rc < 0) - { - log<level::ERR>("Failed to enable the timer", entry("RC=%d", rc)); - throw std::runtime_error("Failed to enable timer"); - } -} - } // namespace eventloop diff --git a/sd_event_loop.hpp b/sd_event_loop.hpp index 0d9c268..10b24ed 100644 --- a/sd_event_loop.hpp +++ b/sd_event_loop.hpp @@ -11,35 +11,6 @@ namespace eventloop { -/** @struct EventSourceDeleter - * - * Custom deleter for the sd_event_source*. - */ -struct EventSourceDeleter -{ - void operator()(sd_event_source* event) const - { - event = sd_event_source_unref(event); - } -}; - -using EventSource = std::unique_ptr<sd_event_source, EventSourceDeleter>; -using IntervalType = std::chrono::microseconds; - -/** @enum Timers - * - * For SOL functioning, there are two timers involved. The character accumulate - * interval timer is the amount of time that the BMC will wait before - * transmitting a partial SOL packet. The retry interval timer is the time that - * BMC will wait before the first retry and the time between retries when - * sending SOL packets to the remote console. - */ -enum class Timers -{ - ACCUMULATE, /**< Character Accumulate Timer */ - RETRY, /**< Retry Interval Timer */ -}; - class EventLoop { public: @@ -53,22 +24,6 @@ class EventLoop EventLoop(EventLoop&&) = delete; EventLoop& operator=(EventLoop&&) = delete; - /** @brief Timer Map - * - * The key for the timer map is the timer type. There are two types of - * timers, character accumulate timer and retry interval timer. The - * entries in the values is the event source for the timer and the - * interval. - */ - using TimerMap = std::map<Timers, std::tuple<EventSource, IntervalType>>; - - /** @brief SOL Payload Map. - * - * The key for the payload map is the payload instance, the entries in - * the value are a map of timers. - */ - using PayloadMap = std::map<uint8_t, TimerMap>; - /** @brief Initialise the event loop and add the handler for incoming * IPMI packets. * @@ -76,56 +31,6 @@ class EventLoop */ int startEventLoop(); - /** @brief Initialize the timers for the SOL payload instance - * - * This API would add the Character accumulate interval timer event - * source and the retry interval timer event source for the SOL payload - * instance to the event loop. - * - * @param[in] payloadInst - SOL payload instance. - * @param[in] accumulateInterval - Character accumulate interval. - * @param[in] retryInterval - Retry interval. - */ - void startSOLPayloadInstance(uint8_t payloadInst, - IntervalType accumulateInterval, - IntervalType retryInterval); - - /** @brief Stop the timers for the SOL payload instance. - * - * This would remove the character accumulate interval timer event - * source and the retry interval timer event source from the event - * loop. - * - * @param[in] payloadInst - SOL payload instance - */ - void stopSOLPayloadInstance(uint8_t payloadInst); - - /** @brief Modify the timer event source to enable/disable. - * - * When the timer is enabled, the timer it set to fire again at - * timer's interval for the instance added to the event loop iteration - * timestamp. When the timer is disabled, the event source for the - * timer is disabled. - * - * @param[in] payloadInst - SOL payload instance. - * @param[in] type - Timer type. - * @param[in] status - on/off the event source. - */ - void switchTimer(uint8_t payloadInst, Timers type, bool status); - - /** @brief Modify the retry interval timer event source to enable/ - * disable - * - * When the timer is enabled, the timer it set to fire again at - * retry interval for the instance added to the event loop iteration - * timestamp. When the timer is disabled the event source for the - * retry interval timer is disabled. - * - * @param[in] payloadInst - SOL payload instance. - * @param[in] status - on/off the event source. - */ - void switchRetryTimer(uint8_t payloadInst, bool status); - /** @brief Event loop object. */ sd_event* event = nullptr; @@ -143,11 +48,6 @@ class EventLoop /** @brief boost::asio udp socket */ std::shared_ptr<boost::asio::ip::udp::socket> udpSocket = nullptr; - - /** @brief Map to keep information regarding IPMI payload instance and - * timers for character accumulate interval and retry interval. - */ - PayloadMap payloadInfo; }; } // namespace eventloop diff --git a/sol/sol_context.cpp b/sol/sol_context.cpp index fffb66f..215f725 100644 --- a/sol/sol_context.cpp +++ b/sol/sol_context.cpp @@ -8,9 +8,62 @@ namespace sol { - using namespace phosphor::logging; +Context::Context(std::shared_ptr<boost::asio::io_context> io, + uint8_t maxRetryCount, uint8_t sendThreshold, uint8_t instance, + session::SessionID sessionID) : + accumulateTimer(*io), + retryTimer(*io), maxRetryCount(maxRetryCount), retryCounter(maxRetryCount), + sendThreshold(sendThreshold), payloadInstance(instance), + sessionID(sessionID) +{ + session = std::get<session::Manager&>(singletonPool).getSession(sessionID); + enableAccumulateTimer(true); +} + +void Context::enableAccumulateTimer(bool enable) +{ + // fetch the timeout from the SOL manager + std::chrono::microseconds interval = + std::get<sol::Manager&>(singletonPool).accumulateInterval; + if (enable) + { + accumulateTimer.expires_after(interval); + accumulateTimer.async_wait([this](const boost::system::error_code& ec) { + if (!ec) + { + charAccTimerHandler(); + } + }); + } + else + { + accumulateTimer.cancel(); + } +} + +void Context::enableRetryTimer(bool enable) +{ + if (enable) + { + // fetch the timeout from the SOL manager + std::chrono::microseconds interval = + std::get<sol::Manager&>(singletonPool).retryInterval; + retryTimer.expires_after(interval); + retryTimer.async_wait([this](const boost::system::error_code& ec) { + if (!ec) + { + retryTimerHandler(); + } + }); + } + else + { + retryTimer.cancel(); + } +} + void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum, uint8_t count, bool status, const std::vector<uint8_t>& input) @@ -54,10 +107,8 @@ void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum, if (status || ((count != expectedCharCount) && ackSeqNum)) { resendPayload(noClear); - std::get<eventloop::EventLoop&>(singletonPool) - .switchTimer(payloadInstance, eventloop::Timers::RETRY, false); - std::get<eventloop::EventLoop&>(singletonPool) - .switchTimer(payloadInstance, eventloop::Timers::RETRY, true); + enableRetryTimer(false); + enableRetryTimer(true); return; } /* @@ -70,8 +121,7 @@ void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum, std::get<sol::Manager&>(singletonPool).dataBuffer.erase(count); // Once it is acknowledged stop the retry interval timer - std::get<eventloop::EventLoop&>(singletonPool) - .switchTimer(payloadInstance, eventloop::Timers::RETRY, false); + enableRetryTimer(false); retryCounter = maxRetryCount; expectedCharCount = 0; @@ -111,8 +161,7 @@ void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum, } else { - std::get<eventloop::EventLoop&>(singletonPool) - .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, true); + enableAccumulateTimer(true); } } @@ -123,8 +172,7 @@ void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack) /* Sent a ACK only response */ if (payloadCache.size() != 0 || (bufferSize < sendThreshold)) { - std::get<eventloop::EventLoop&>(singletonPool) - .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, true); + enableAccumulateTimer(true); std::vector<uint8_t> outPayload(sizeof(Payload)); auto response = reinterpret_cast<Payload*>(outPayload.data()); @@ -148,10 +196,8 @@ void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack) std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload)); expectedCharCount = readSize; - std::get<eventloop::EventLoop&>(singletonPool) - .switchTimer(payloadInstance, eventloop::Timers::RETRY, true); - std::get<eventloop::EventLoop&>(singletonPool) - .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, false); + enableRetryTimer(true); + enableAccumulateTimer(false); sendPayload(payloadCache); } @@ -160,8 +206,7 @@ int Context::sendOutboundPayload() { if (payloadCache.size() != 0) { - std::get<eventloop::EventLoop&>(singletonPool) - .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, true); + enableAccumulateTimer(true); return -1; } @@ -179,10 +224,8 @@ int Context::sendOutboundPayload() std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload)); expectedCharCount = readSize; - std::get<eventloop::EventLoop&>(singletonPool) - .switchTimer(payloadInstance, eventloop::Timers::RETRY, true); - std::get<eventloop::EventLoop&>(singletonPool) - .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, false); + enableRetryTimer(true); + enableAccumulateTimer(false); sendPayload(payloadCache); @@ -204,12 +247,54 @@ void Context::resendPayload(bool clear) void Context::sendPayload(const std::vector<uint8_t>& out) const { - auto session = - std::get<session::Manager&>(singletonPool).getSession(sessionID); - message::Handler msgHandler(session->channelPtr, sessionID); msgHandler.sendSOLPayload(out); } +void Context::charAccTimerHandler() +{ + auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size(); + + try + { + if (bufferSize > 0) + { + int rc = sendOutboundPayload(); + if (rc == 0) + { + return; + } + } + enableAccumulateTimer(true); + } + catch (std::exception& e) + { + log<level::ERR>(e.what()); + } +} + +void Context::retryTimerHandler() +{ + try + { + if (retryCounter) + { + --retryCounter; + enableRetryTimer(true); + resendPayload(sol::Context::noClear); + } + else + { + retryCounter = maxRetryCount; + resendPayload(sol::Context::clear); + enableRetryTimer(false); + enableAccumulateTimer(true); + } + } + catch (std::exception& e) + { + log<level::ERR>(e.what()); + } +} } // namespace sol diff --git a/sol/sol_context.hpp b/sol/sol_context.hpp index 220040b..8d0fc87 100644 --- a/sol/sol_context.hpp +++ b/sol/sol_context.hpp @@ -3,6 +3,9 @@ #include "console_buffer.hpp" #include "session.hpp" +#include <boost/asio/io_context.hpp> +#include <boost/asio/steady_timer.hpp> + namespace sol { @@ -149,34 +152,37 @@ struct SequenceNumbers class Context { public: - Context() = default; + Context() = delete; ~Context() = default; Context(const Context&) = delete; Context& operator=(const Context&) = delete; - Context(Context&&) = default; - Context& operator=(Context&&) = default; + Context(Context&&) = delete; + Context& operator=(Context&&) = delete; /** @brief Context Constructor. * * This is issued by the SOL Manager when a SOL payload instance is * started for the activate payload command. * + * @param[in] io - boost::asio io context for event scheduling. * @param[in] maxRetryCount - Retry count max value. * @param[in] sendThreshold - Character send threshold. * @param[in] instance - SOL payload instance. * @param[in] sessionID - BMC session ID. */ - Context(uint8_t maxRetryCount, uint8_t sendThreshold, uint8_t instance, - session::SessionID sessionID) : - maxRetryCount(maxRetryCount), - retryCounter(maxRetryCount), sendThreshold(sendThreshold), - payloadInstance(instance), sessionID(sessionID) - { - } + Context(std::shared_ptr<boost::asio::io_context> io, uint8_t maxRetryCount, + uint8_t sendThreshold, uint8_t instance, + session::SessionID sessionID); static constexpr auto clear = true; static constexpr auto noClear = false; + /** @brief accumulate timer */ + boost::asio::steady_timer accumulateTimer; + + /** @brief retry timer */ + boost::asio::steady_timer retryTimer; + /** @brief Retry count max value. */ const uint8_t maxRetryCount = 0; @@ -192,6 +198,28 @@ class Context /** @brief Session ID. */ const session::SessionID sessionID = 0; + /** @brief session pointer + */ + std::shared_ptr<session::Session> session; + + /** @brief enable/disable accumulate timer + * + * The timeout interval is managed by the SOL Manager; + * this function only enables or disable the timer + * + * @param[in] enable - enable(true) or disable(false) accumulation timer + */ + void enableAccumulateTimer(bool enable); + + /** @brief enable/disable retry timer + * + * The timeout interval is managed by the SOL Manager; + * this function only enables or disable the timer + * + * @param[in] enable - enable(true) or disable(false) retry timer + */ + void enableRetryTimer(bool enable); + /** @brief Process the Inbound SOL payload. * * The SOL payload from the remote console is processed and the @@ -253,6 +281,12 @@ class Context * @param[in] out - buffer containing the SOL payload. */ void sendPayload(const std::vector<uint8_t>& out) const; + + /** @brief accumlate timer handler called by timer */ + void charAccTimerHandler(); + + /** @brief retry timer handler called by timer */ + void retryTimerHandler(); }; } // namespace sol diff --git a/sol/sol_manager.cpp b/sol/sol_manager.cpp index fc0efe9..2046fe4 100644 --- a/sol/sol_manager.cpp +++ b/sol/sol_manager.cpp @@ -102,16 +102,9 @@ void Manager::startPayloadInstance(uint8_t payloadInstance, } // Create the SOL Context data for payload instance - auto context = std::make_unique<Context>(retryCount, sendThreshold, + auto context = std::make_unique<Context>(io, retryCount, sendThreshold, payloadInstance, sessionID); - std::get<eventloop::EventLoop&>(singletonPool) - .startSOLPayloadInstance( - payloadInstance, - std::chrono::duration_cast<eventloop::IntervalType>( - accumulateInterval), - std::chrono::duration_cast<eventloop::IntervalType>(retryInterval)); - payloadMap.emplace(payloadInstance, std::move(context)); } @@ -125,9 +118,6 @@ void Manager::stopPayloadInstance(uint8_t payloadInstance) payloadMap.erase(iter); - std::get<eventloop::EventLoop&>(singletonPool) - .stopSOLPayloadInstance(payloadInstance); - if (payloadMap.empty()) { stopHostConsole(); |