From 0e426ce8bcde3b6fb131405ef265250a96aa7e0a Mon Sep 17 00:00:00 2001 From: James Feist Date: Mon, 17 Jun 2019 12:00:58 -0700 Subject: [PATCH] Customize phosphor-watchdog for Intel platforms This patch adds various changes to phosphor-watchdog that are required for compatibility with Intel platforms. 1. Add Redfish messages for watchdog timeout and pre-interrupt 2. Use dbus properties for power control insted of service files 3. Use host status to enable/disable watchdog 4. Set preTimeoutInterruptOccurFlag 5. Assign watchdog cause for correct reset cause reporting Signed-off-by: James Feist Signed-off-by: Ren Yu Signed-off-by: Yong Li Signed-off-by: Jason M. Bills Signed-off-by: Johnathan Mantey --- watchdog.cpp | 212 ++++++++++++++++++++++++++++++++++++++++++++++++--- watchdog.hpp | 23 +++++- 2 files changed, 224 insertions(+), 11 deletions(-) diff --git a/watchdog.cpp b/watchdog.cpp index 9090760..2685401 100644 --- a/watchdog.cpp +++ b/watchdog.cpp @@ -1,11 +1,14 @@ #include "watchdog.hpp" +#include + #include #include #include #include #include #include +#include namespace phosphor { @@ -18,10 +21,77 @@ using namespace phosphor::logging; using sdbusplus::exception::SdBusError; using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; -// systemd service to kick start a target. -constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1"; -constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1"; -constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager"; +const static constexpr char* currentHostState = "CurrentHostState"; +const static constexpr char* hostStatusOff = + "xyz.openbmc_project.State.Host.HostState.Off"; + +const static constexpr char* actionDescription = " due to Watchdog timeout"; +const static constexpr char* hardResetDescription = "Hard Reset - System reset"; +const static constexpr char* powerOffDescription = + "Power Down - System power down"; +const static constexpr char* powerCycleDescription = + "Power Cycle - System power cycle"; +const static constexpr char* timerExpiredDescription = "Timer expired"; + +const static constexpr char* preInterruptActionNone = + "xyz.openbmc_project.State.Watchdog.PreTimeoutInterruptAction.None"; + +const static constexpr char* preInterruptDescriptionSMI = "SMI"; +const static constexpr char* preInterruptDescriptionNMI = "NMI"; +const static constexpr char* preInterruptDescriptionMI = "Messaging Interrupt"; + +const static constexpr char* reservedDescription = "Reserved"; + +const static constexpr char* timerUseDescriptionBIOSFRB2 = "BIOS FRB2"; +const static constexpr char* timerUseDescriptionBIOSPOST = "BIOS/POST"; +const static constexpr char* timerUseDescriptionOSLoad = "OSLoad"; +const static constexpr char* timerUseDescriptionSMSOS = "SMS/OS"; +const static constexpr char* timerUseDescriptionOEM = "OEM"; + +namespace restart +{ +static constexpr const char* busName = + "xyz.openbmc_project.Control.Host.RestartCause"; +static constexpr const char* path = + "/xyz/openbmc_project/control/host0/restart_cause"; +static constexpr const char* interface = + "xyz.openbmc_project.Control.Host.RestartCause"; +static constexpr const char* property = "RequestedRestartCause"; +} // namespace restart + +// chassis state manager service +namespace chassis +{ +static constexpr const char* busName = "xyz.openbmc_project.State.Chassis"; +static constexpr const char* path = "/xyz/openbmc_project/state/chassis0"; +static constexpr const char* interface = "xyz.openbmc_project.State.Chassis"; +static constexpr const char* request = "RequestedPowerTransition"; +} // namespace chassis + +namespace host +{ +static constexpr const char* busName = "xyz.openbmc_project.State.Host"; +static constexpr const char* path = "/xyz/openbmc_project/state/host0"; +static constexpr const char* interface = "xyz.openbmc_project.State.Host"; +static constexpr const char* request = "RequestedHostTransition"; +} // namespace host + +void Watchdog::powerStateChangedHandler( + const std::map>& props) +{ + const auto iter = props.find(currentHostState); + if (iter != props.end()) + { + const std::string* powerState = std::get_if(&iter->second); + if (powerState && (*powerState == hostStatusOff)) + { + if (timerEnabled()) + { + enabled(false); + } + } + } +} void Watchdog::resetTimeRemaining(bool enableWatchdog) { @@ -102,13 +172,102 @@ uint64_t Watchdog::interval(uint64_t value) // Optional callback function on timer expiration void Watchdog::timeOutHandler() { + PreTimeoutInterruptAction preTimeoutInterruptAction = preTimeoutInterrupt(); + std::string preInterruptActionMessageArgs{}; + Action action = expireAction(); + std::string actionMessageArgs{}; + + expiredTimerUse(currentTimerUse()); + + TimerUse timeUser = expiredTimerUse(); + std::string timeUserMessage{}; + if (!this->enabled()) { action = fallback->action; } - expiredTimerUse(currentTimerUse()); + switch (timeUser) + { + case Watchdog::TimerUse::BIOSFRB2: + timeUserMessage = timerUseDescriptionBIOSFRB2; + break; + case Watchdog::TimerUse::BIOSPOST: + timeUserMessage = timerUseDescriptionBIOSPOST; + break; + case Watchdog::TimerUse::OSLoad: + timeUserMessage = timerUseDescriptionOSLoad; + break; + case Watchdog::TimerUse::SMSOS: + timeUserMessage = timerUseDescriptionSMSOS; + break; + case Watchdog::TimerUse::OEM: + timeUserMessage = timerUseDescriptionOEM; + break; + default: + timeUserMessage = reservedDescription; + break; + } + + switch (action) + { + case Watchdog::Action::HardReset: + actionMessageArgs = std::string(hardResetDescription) + + std::string(actionDescription); + break; + case Watchdog::Action::PowerOff: + actionMessageArgs = std::string(powerOffDescription) + + std::string(actionDescription); + break; + case Watchdog::Action::PowerCycle: + actionMessageArgs = std::string(powerCycleDescription) + + std::string(actionDescription); + break; + case Watchdog::Action::None: + actionMessageArgs = timerExpiredDescription; + break; + default: + actionMessageArgs = reservedDescription; + break; + } + + // Log into redfish event log + sd_journal_send("MESSAGE=IPMIWatchdog: Timed out ACTION=%s", + convertForMessage(action).c_str(), "PRIORITY=%i", LOG_INFO, + "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.IPMIWatchdog", + "REDFISH_MESSAGE_ARGS=%s. timer use: %s", + actionMessageArgs.c_str(), timeUserMessage.c_str(), NULL); + + switch (preTimeoutInterruptAction) + { + case Watchdog::PreTimeoutInterruptAction::SMI: + preInterruptActionMessageArgs = preInterruptDescriptionSMI; + break; + case Watchdog::PreTimeoutInterruptAction::NMI: + preInterruptActionMessageArgs = preInterruptDescriptionNMI; + break; + case Watchdog::PreTimeoutInterruptAction::MI: + preInterruptActionMessageArgs = preInterruptDescriptionMI; + break; + default: + preInterruptActionMessageArgs = reservedDescription; + break; + } + + if (preInterruptActionNone != convertForMessage(preTimeoutInterruptAction)) + { + preTimeoutInterruptOccurFlag(true); + + sd_journal_send("MESSAGE=IPMIWatchdog: Pre Timed out Interrupt=%s", + convertForMessage(preTimeoutInterruptAction).c_str(), + "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s", + "OpenBMC.0.1.IPMIWatchdog", + "REDFISH_MESSAGE_ARGS=Timer interrupt - %s due to " + "Watchdog timeout. timer use: %s", + preInterruptActionMessageArgs.c_str(), + timeUserMessage.c_str(), NULL); + } auto target = actionTargetMap.find(action); if (target == actionTargetMap.end()) @@ -128,12 +287,45 @@ void Watchdog::timeOutHandler() try { - auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT, - SYSTEMD_INTERFACE, "StartUnit"); - method.append(target->second); - method.append("replace"); + sdbusplus::message::message method; + if (action == Watchdog::Action::HardReset) + { + auto method = bus.new_method_call( + restart::busName, restart::path, + "org.freedesktop.DBus.Properties", "Set"); + method.append( + restart::interface, restart::property, + std::variant("xyz.openbmc_project.State.Host." + "RestartCause.WatchdogTimer")); + bus.call_noreply(method); - bus.call_noreply(method); + method = bus.new_method_call(host::busName, host::path, + "org.freedesktop.DBus.Properties", + "Set"); + method.append(host::interface, host::request, + std::variant(target->second)); + bus.call_noreply(method); + } + else + { + if (action == Watchdog::Action::PowerCycle) + { + auto method = bus.new_method_call( + restart::busName, restart::path, + "org.freedesktop.DBus.Properties", "Set"); + method.append(restart::interface, restart::property, + std::variant( + "xyz.openbmc_project.State.Host." + "RestartCause.WatchdogTimer")); + bus.call_noreply(method); + } + method = bus.new_method_call(chassis::busName, chassis::path, + "org.freedesktop.DBus.Properties", + "Set"); + method.append(chassis::interface, chassis::request, + std::variant(target->second)); + bus.call_noreply(method); + } } catch (const SdBusError& e) { diff --git a/watchdog.hpp b/watchdog.hpp index 7de9bb3..b004b7a 100644 --- a/watchdog.hpp +++ b/watchdog.hpp @@ -68,7 +68,18 @@ class Watchdog : public WatchdogInherits WatchdogInherits(bus, objPath), bus(bus), actionTargetMap(std::move(actionTargetMap)), fallback(std::move(fallback)), minInterval(minInterval), - timer(event, std::bind(&Watchdog::timeOutHandler, this)) + timer(event, std::bind(&Watchdog::timeOutHandler, this)), + powerStateChangedSignal( + bus, + sdbusplus::bus::match::rules::propertiesChanged( + "/xyz/openbmc_project/state/host0", + "xyz.openbmc_project.State.Host"), + [this](sdbusplus::message::message& msg) { + std::string objectName; + std::map> props; + msg.read(objectName, props); + powerStateChangedHandler(props); + }) { // We set the watchdog interval with the default value. interval(interval()); @@ -77,6 +88,12 @@ class Watchdog : public WatchdogInherits tryFallbackOrDisable(); } + /** @brief Disable watchdog when power status change meet + * the specific requirement + */ + void powerStateChangedHandler( + const std::map>& props); + /** @brief Resets the TimeRemaining to the configured Interval * Optionally enables the watchdog. * @@ -165,6 +182,10 @@ class Watchdog : public WatchdogInherits /** @brief Contained timer object */ sdeventplus::utility::Timer timer; + /** @brief Optional Callback handler when power status change meet + * the specific requirement */ + sdbusplus::bus::match_t powerStateChangedSignal; + /** @brief Optional Callback handler on timer expirartion */ void timeOutHandler(); -- 2.29.2