blob: 22670d3fc78bf449226e034086b9d2b28fcda4e4 [file] [log] [blame]
From ac0c216ac2c273c620579fd1308c8c225e0cca36 Mon Sep 17 00:00:00 2001
From: James Feist <james.feist@linux.intel.com>
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
Signed-off-by: James Feist <james.feist@linux.intel.com>
Signed-off-by: Ren Yu <yux.ren@intel.com>
Signed-off-by: Yong Li <yong.b.li@linux.intel.com>
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
---
watchdog.cpp | 193 ++++++++++++++++++++++++++++++++++++++++++++++++---
watchdog.hpp | 23 +++++-
2 files changed, 206 insertions(+), 10 deletions(-)
diff --git a/watchdog.cpp b/watchdog.cpp
index 57e9050..3b5356f 100644
--- a/watchdog.cpp
+++ b/watchdog.cpp
@@ -1,11 +1,14 @@
#include "watchdog.hpp"
+#include <systemd/sd-journal.h>
+
#include <algorithm>
#include <chrono>
#include <phosphor-logging/elog.hpp>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/exception.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
+#include <xyz/openbmc_project/State/Host/server.hpp>
namespace phosphor
{
@@ -18,10 +21,69 @@ 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
+
+void Watchdog::powerStateChangedHandler(
+ const std::map<std::string, std::variant<std::string>>& props)
+{
+ const auto iter = props.find(currentHostState);
+ if (iter != props.end())
+ {
+ const std::string* powerState = std::get_if<std::string>(&iter->second);
+ if (powerState && (*powerState == hostStatusOff))
+ {
+ if (timerEnabled())
+ {
+ enabled(false);
+ }
+ }
+ }
+}
void Watchdog::resetTimeRemaining(bool enableWatchdog)
{
@@ -107,13 +169,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())
@@ -133,10 +284,11 @@ void Watchdog::timeOutHandler()
try
{
- auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
- SYSTEMD_INTERFACE, "StartUnit");
- method.append(target->second);
- method.append("replace");
+ auto method =
+ bus.new_method_call(chassis::busName, chassis::path,
+ "org.freedesktop.DBus.Properties", "Set");
+ method.append(chassis::interface, chassis::request,
+ std::variant<std::string>(target->second));
bus.call_noreply(method);
}
@@ -147,6 +299,29 @@ void Watchdog::timeOutHandler()
entry("ERROR=%s", e.what()));
commit<InternalFailure>();
}
+
+ // set restart cause for watchdog HardReset & PowerCycle actions
+ if ((action == Watchdog::Action::HardReset) ||
+ (action == Watchdog::Action::PowerCycle))
+ {
+ try
+ {
+ auto method = bus.new_method_call(
+ restart::busName, restart::path,
+ "org.freedesktop.DBus.Properties", "Set");
+ method.append(
+ restart::interface, restart::property,
+ std::variant<std::string>("xyz.openbmc_project.State.Host."
+ "RestartCause.WatchdogTimer"));
+ bus.call(method);
+ }
+ catch (sdbusplus::exception_t& e)
+ {
+ log<level::ERR>("Failed to set HostRestartCause property",
+ entry("ERROR=%s", e.what()));
+ commit<InternalFailure>();
+ }
+ }
}
tryFallbackOrDisable();
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<std::string, std::variant<std::string>> 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<std::string, std::variant<std::string>>& 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<sdeventplus::ClockId::Monotonic> 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();