blob: 90a840e1137c7b95833e94a9173efe4b58aa4ffb [file] [log] [blame]
Patrick Venture8f6c5152018-09-11 17:45:33 -07001#include "watchdog.hpp"
2
William A. Kennington III7036c562018-10-03 23:36:16 -07003#include <phosphor-logging/elog.hpp>
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +05304#include <phosphor-logging/log.hpp>
William A. Kennington III7036c562018-10-03 23:36:16 -07005#include <sdbusplus/exception.hpp>
6#include <xyz/openbmc_project/Common/error.hpp>
7
Patrick Williams6ac6a342023-05-10 07:51:12 -05008#include <algorithm>
9#include <chrono>
10#include <string_view>
11
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +053012namespace phosphor
13{
14namespace watchdog
15{
16using namespace std::chrono;
17using namespace std::chrono_literals;
18using namespace phosphor::logging;
19
William A. Kennington III7036c562018-10-03 23:36:16 -070020using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
21
Vishwanatha Subbanna3473d702017-05-30 16:38:50 +053022// systemd service to kick start a target.
Patrick Venture8f6c5152018-09-11 17:45:33 -070023constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
24constexpr auto SYSTEMD_ROOT = "/org/freedesktop/systemd1";
25constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
Vishwanatha Subbanna3473d702017-05-30 16:38:50 +053026
William A. Kennington III1726df62018-04-23 10:28:28 -070027void Watchdog::resetTimeRemaining(bool enableWatchdog)
28{
29 timeRemaining(interval());
30 if (enableWatchdog)
31 {
32 enabled(true);
33 }
34}
35
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +053036// Enable or disable watchdog
37bool Watchdog::enabled(bool value)
38{
William A. Kennington III825f4982018-02-27 19:10:56 -080039 if (!value)
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +053040 {
William A. Kennington IIId1331082018-02-27 18:47:05 -080041 // Make sure we accurately reflect our enabled state to the
42 // tryFallbackOrDisable() call
43 WatchdogInherits::enabled(value);
44
45 // Attempt to fallback or disable our timer if needed
46 tryFallbackOrDisable();
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +053047
William A. Kennington III825f4982018-02-27 19:10:56 -080048 return false;
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +053049 }
William A. Kennington III825f4982018-02-27 19:10:56 -080050 else if (!this->enabled())
51 {
William A. Kennington IIIf505fc02018-09-12 18:30:09 -070052 auto interval_ms = this->interval();
53 timer.restart(milliseconds(interval_ms));
William A. Kennington III825f4982018-02-27 19:10:56 -080054 log<level::INFO>("watchdog: enabled and started",
William A. Kennington IIIf505fc02018-09-12 18:30:09 -070055 entry("INTERVAL=%llu", interval_ms));
William A. Kennington III825f4982018-02-27 19:10:56 -080056 }
57
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +053058 return WatchdogInherits::enabled(value);
59}
60
61// Get the remaining time before timer expires.
62// If the timer is disabled, returns 0
63uint64_t Watchdog::timeRemaining() const
64{
William A. Kennington III1c9515f2018-02-27 18:54:36 -080065 // timer may have already expired and disabled
William A. Kennington IIIf505fc02018-09-12 18:30:09 -070066 if (!timerEnabled())
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +053067 {
William A. Kennington IIIf505fc02018-09-12 18:30:09 -070068 return 0;
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +053069 }
William A. Kennington IIIf505fc02018-09-12 18:30:09 -070070
71 return duration_cast<milliseconds>(timer.getRemaining()).count();
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +053072}
73
74// Reset the timer to a new expiration value
75uint64_t Watchdog::timeRemaining(uint64_t value)
76{
William A. Kennington III0650a3f2018-03-01 10:53:25 -080077 if (!timerEnabled())
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +053078 {
William A. Kennington III5e3f8772018-02-27 18:59:58 -080079 // We don't need to update the timer because it is off
80 return 0;
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +053081 }
William A. Kennington III5e3f8772018-02-27 18:59:58 -080082
Kun Yief824702019-12-04 18:58:20 -080083 if (this->enabled())
84 {
85 // Update interval to minInterval if applicable
86 value = std::max(value, minInterval);
87 }
88 else
William A. Kennington IIId1331082018-02-27 18:47:05 -080089 {
90 // Having a timer but not displaying an enabled value means we
91 // are inside of the fallback
92 value = fallback->interval;
93 }
94
William A. Kennington III5e3f8772018-02-27 18:59:58 -080095 // Update new expiration
William A. Kennington IIIf505fc02018-09-12 18:30:09 -070096 timer.setRemaining(milliseconds(value));
William A. Kennington III5e3f8772018-02-27 18:59:58 -080097
98 // Update Base class data.
99 return WatchdogInherits::timeRemaining(value);
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +0530100}
101
Ofer Yehiellic35135d2019-06-14 11:30:25 -0700102// Set value of Interval
103uint64_t Watchdog::interval(uint64_t value)
104{
105 return WatchdogInherits::interval(std::max(value, minInterval));
106}
107
Vishwanatha Subbanna8c5a2292017-05-30 15:34:23 +0530108// Optional callback function on timer expiration
109void Watchdog::timeOutHandler()
110{
William A. Kennington IIId1331082018-02-27 18:47:05 -0800111 Action action = expireAction();
112 if (!this->enabled())
113 {
114 action = fallback->action;
115 }
Vishwanatha Subbanna3473d702017-05-30 16:38:50 +0530116
William A. Kennington III47a9ec42019-04-18 15:42:16 -0700117 expiredTimerUse(currentTimerUse());
Yong Li50ae4b12019-03-20 17:08:24 +0800118
William A. Kennington III3bb2f402018-09-13 00:35:47 -0700119 auto target = actionTargetMap.find(action);
120 if (target == actionTargetMap.end())
William A. Kennington III1232a152018-02-02 15:57:34 -0800121 {
122 log<level::INFO>("watchdog: Timed out with no target",
William A. Kennington III47a9ec42019-04-18 15:42:16 -0700123 entry("ACTION=%s", convertForMessage(action).c_str()),
124 entry("TIMER_USE=%s",
125 convertForMessage(expiredTimerUse()).c_str()));
William A. Kennington III825f4982018-02-27 19:10:56 -0800126 }
127 else
128 {
William A. Kennington III47a9ec42019-04-18 15:42:16 -0700129 log<level::INFO>(
130 "watchdog: Timed out",
131 entry("ACTION=%s", convertForMessage(action).c_str()),
132 entry("TIMER_USE=%s", convertForMessage(expiredTimerUse()).c_str()),
133 entry("TARGET=%s", target->second.c_str()));
William A. Kennington III7036c562018-10-03 23:36:16 -0700134
135 try
136 {
137 auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_ROOT,
138 SYSTEMD_INTERFACE, "StartUnit");
139 method.append(target->second);
140 method.append("replace");
141
142 bus.call_noreply(method);
143 }
Patrick Williams73bd5272022-07-22 19:26:57 -0500144 catch (const sdbusplus::exception_t& e)
William A. Kennington III7036c562018-10-03 23:36:16 -0700145 {
146 log<level::ERR>("watchdog: Failed to start unit",
147 entry("TARGET=%s", target->second.c_str()),
148 entry("ERROR=%s", e.what()));
149 commit<InternalFailure>();
150 }
Vishwanatha Subbanna3473d702017-05-30 16:38:50 +0530151 }
selvaganapathifcc00392022-11-14 23:09:44 +0530152 try
153 {
154 auto signal = bus.new_signal(objPath.data(),
155 "xyz.openbmc_project.Watchdog", "Timeout");
156 signal.append(convertForMessage(action).c_str());
157 signal.signal_send();
158 }
159 catch (const sdbusplus::exception_t& e)
160 {
161 log<level::ERR>("watchdog: failed to send timeout signal",
162 entry("ERROR=%s", e.what()));
163 }
William A. Kennington III1232a152018-02-02 15:57:34 -0800164
William A. Kennington IIId1b1e792022-11-21 18:29:11 -0800165 if (exitAfterTimeout)
166 {
167 timer.get_event().exit(0);
168 }
169
William A. Kennington IIId1331082018-02-27 18:47:05 -0800170 tryFallbackOrDisable();
William A. Kennington III825f4982018-02-27 19:10:56 -0800171}
William A. Kennington III1232a152018-02-02 15:57:34 -0800172
William A. Kennington IIId1331082018-02-27 18:47:05 -0800173void Watchdog::tryFallbackOrDisable()
William A. Kennington III825f4982018-02-27 19:10:56 -0800174{
William A. Kennington IIId1331082018-02-27 18:47:05 -0800175 // We only re-arm the watchdog if we were already enabled and have
176 // a possible fallback
William A. Kennington III22352192018-02-27 18:51:44 -0800177 if (fallback && (fallback->always || this->enabled()))
William A. Kennington IIId1331082018-02-27 18:47:05 -0800178 {
179 auto interval_ms = fallback->interval;
William A. Kennington IIIf505fc02018-09-12 18:30:09 -0700180 timer.restart(milliseconds(interval_ms));
William A. Kennington IIId1331082018-02-27 18:47:05 -0800181 log<level::INFO>("watchdog: falling back",
Patrick Venture8f6c5152018-09-11 17:45:33 -0700182 entry("INTERVAL=%llu", interval_ms));
William A. Kennington IIId1331082018-02-27 18:47:05 -0800183 }
184 else if (timerEnabled())
William A. Kennington III825f4982018-02-27 19:10:56 -0800185 {
William A. Kennington IIIf505fc02018-09-12 18:30:09 -0700186 timer.setEnabled(false);
William A. Kennington III825f4982018-02-27 19:10:56 -0800187
188 log<level::INFO>("watchdog: disabled");
189 }
190
191 // Make sure we accurately reflect our enabled state to the
192 // dbus interface.
193 WatchdogInherits::enabled(false);
Vishwanatha Subbanna8c5a2292017-05-30 15:34:23 +0530194}
195
Vishwanatha Subbannad7a3f132017-05-29 19:39:08 +0530196} // namespace watchdog
Patrick Venture8f6c5152018-09-11 17:45:33 -0700197} // namespace phosphor