From a7d1d3770a650df8fe61d594885fcb388ac2ca42 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 6. Add NMI Pre-Interrupt support for IPMI watchdog timer. 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 Signed-off-by: Sunita Kumari %% original patch: 0001-Customize-phosphor-watchdog-for-Intel-platforms.patch --- src/watchdog.cpp | 229 ++++++++++++++++++++++++++++++++++++++++++++--- src/watchdog.hpp | 22 ++++- 2 files changed, 240 insertions(+), 11 deletions(-) diff --git a/src/watchdog.cpp b/src/watchdog.cpp index 7de98ae3e70f..f96faebc7368 100644 --- a/src/watchdog.cpp +++ b/src/watchdog.cpp @@ -5,8 +5,10 @@ #include #include #include +#include #include #include +#include namespace phosphor { @@ -19,10 +21,86 @@ 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 + +namespace nmi +{ +static constexpr const char* busName = "xyz.openbmc_project.Control.Host.NMI"; +static constexpr const char* path = "/xyz/openbmc_project/control/host0/nmi"; +static constexpr const char* interface = "xyz.openbmc_project.Control.Host.NMI"; +static constexpr const char* request = "NMI"; + +} // namespace nmi + +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) { @@ -108,13 +186,111 @@ 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); + + if (preTimeoutInterruptAction == + Watchdog::PreTimeoutInterruptAction::NMI) + { + sdbusplus::message::message preTimeoutInterruptHandler; + preTimeoutInterruptHandler = bus.new_method_call( + nmi::busName, nmi::path, nmi::interface, nmi::request); + bus.call_noreply(preTimeoutInterruptHandler); + } + } auto target = actionTargetMap.find(action); if (target == actionTargetMap.end()) @@ -147,12 +323,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/src/watchdog.hpp b/src/watchdog.hpp index 736e71f68fcc..79158f192f40 100644 --- a/src/watchdog.hpp +++ b/src/watchdog.hpp @@ -73,7 +73,17 @@ class Watchdog : public WatchdogInherits bus(bus), actionTargetMap(std::move(actionTargetMap)), fallback(std::move(fallback)), minInterval(minInterval), timer(event, std::bind(&Watchdog::timeOutHandler, this)), - objPath(objPath) + objPath(objPath), 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); + }) { // Use default if passed in otherwise just use default that comes // with object @@ -90,6 +100,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. * @@ -178,6 +194,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.17.1