blob: f0e7531cce4940effd6d43aff118106f55cfcb83 [file] [log] [blame]
Matt Spinler69b0cf02020-10-14 10:59:03 -05001#pragma once
2
3#include "logging.hpp"
4#include "power_interface.hpp"
5
6#include <fmt/format.h>
7
8#include <sdeventplus/clock.hpp>
9#include <sdeventplus/event.hpp>
10#include <sdeventplus/utility/timer.hpp>
11
12#include <chrono>
13
14namespace phosphor::fan::monitor
15{
16
17/**
18 * @class PowerOffAction
19 *
20 * This is the base class for a power off action, which is
21 * used by the PowerOffRule class to do different types of
22 * power offs based on fan failures.
23 *
24 * The power off is started with the start() method, and the
25 * derived class may or may not allow it to be stopped with
26 * the cancel() method, which is really only useful when
27 * there is a delay before the power off.
28 *
29 * It uses the PowerInterfaceBase object pointer to perform
30 * the D-Bus call to do the power off, so it can be mocked
31 * for testing.
32 */
33class PowerOffAction
34{
35 public:
Matt Spinlerac1efc12020-10-27 10:20:11 -050036 using PrePowerOffFunc = std::function<void()>;
37
Matt Spinler69b0cf02020-10-14 10:59:03 -050038 PowerOffAction() = delete;
39 virtual ~PowerOffAction() = default;
40 PowerOffAction(const PowerOffAction&) = delete;
41 PowerOffAction& operator=(const PowerOffAction&) = delete;
42 PowerOffAction(PowerOffAction&&) = delete;
43 PowerOffAction& operator=(PowerOffAction&&) = delete;
44
45 /**
46 * @brief Constructor
47 *
48 * @param[in] name - The action name. Used for tracing.
Matt Spinlerac1efc12020-10-27 10:20:11 -050049 * @param[in] powerInterface - The object used to invoke the power off.
50 * @param[in] powerOffFunc - A function to call right before the power
51 * off occurs (after any delays). May be
52 * empty if no function is necessary.
Matt Spinler69b0cf02020-10-14 10:59:03 -050053 */
54 PowerOffAction(const std::string& name,
Matt Spinlerac1efc12020-10-27 10:20:11 -050055 std::shared_ptr<PowerInterfaceBase> powerInterface,
56 PrePowerOffFunc& powerOffFunc) :
Matt Spinler69b0cf02020-10-14 10:59:03 -050057 _name(name),
58 _powerIface(std::move(powerInterface)),
Matt Spinlerac1efc12020-10-27 10:20:11 -050059 _event(sdeventplus::Event::get_default()),
60 _prePowerOffFunc(powerOffFunc)
Matt Spinler69b0cf02020-10-14 10:59:03 -050061 {}
62
63 /**
64 * @brief Starts the power off.
65 *
66 * Though this occurs in the child class, usually this
67 * involves starting a timer and then powering off when it
68 * times out.
69 */
70 virtual void start() = 0;
71
72 /**
73 * @brief Attempts to cancel the power off, if the derived
74 * class allows it, and assuming the power off hasn't
75 * already happened.
76 *
77 * The 'force' parameter is mainly for use when something else
78 * powered off the system so this action doesn't need to run
79 * anymore even if it isn't usually cancelable.
80 *
81 * @param[in] force - If the cancel should be forced
82 *
83 * @return bool - If the cancel was allowed/successful
84 */
85 virtual bool cancel(bool force) = 0;
86
87 /**
Matt Spinler69b0cf02020-10-14 10:59:03 -050088 * @brief Returns the name of the action
89 *
90 * @return const std::string& - The name
91 */
92 const std::string& name() const
93 {
94 return _name;
95 }
96
97 protected:
98 /**
99 * @brief The name of the action, which is set by the
100 * derived class.
101 */
102 const std::string _name;
103
104 /**
Matt Spinler69b0cf02020-10-14 10:59:03 -0500105 * @brief The object used to invoke the power off with.
106 */
107 std::shared_ptr<PowerInterfaceBase> _powerIface;
108
109 /**
110 * @brief The event loop object. Needed by timers.
111 */
112 sdeventplus::Event _event;
Matt Spinlerac1efc12020-10-27 10:20:11 -0500113
114 /**
115 * @brief A function that will be called right before
116 * the power off.
117 */
118 PrePowerOffFunc _prePowerOffFunc;
Matt Spinler69b0cf02020-10-14 10:59:03 -0500119};
120
121/**
122 * @class HardPowerOff
123 *
124 * This class is derived from the PowerOffAction class
125 * and will execute a hard power off after some delay.
126 */
127class HardPowerOff : public PowerOffAction
128{
129 public:
130 HardPowerOff() = delete;
131 ~HardPowerOff() = default;
132 HardPowerOff(const HardPowerOff&) = delete;
133 HardPowerOff& operator=(const HardPowerOff&) = delete;
134 HardPowerOff(HardPowerOff&&) = delete;
135 HardPowerOff& operator=(HardPowerOff&&) = delete;
136
137 /**
138 * @brief Constructor
139 *
140 * @param[in] delay - The amount of time in seconds to wait before
141 * doing the power off
142 * @param[in] powerInterface - The object to use to do the power off
Matt Spinlerac1efc12020-10-27 10:20:11 -0500143 * @param[in] func - A function to call right before the power
144 * off occurs (after the delay). May be
145 * empty if no function is necessary.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500146 */
147 HardPowerOff(uint32_t delay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500148 std::shared_ptr<PowerInterfaceBase> powerInterface,
149 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500150 PowerOffAction("Hard Power Off: " + std::to_string(delay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500151 powerInterface, func),
Matt Spinler69b0cf02020-10-14 10:59:03 -0500152 _delay(delay),
153 _timer(_event, std::bind(std::mem_fn(&HardPowerOff::powerOff), this))
154 {}
155
156 /**
157 * @brief Starts a timer upon the expiration of which the
158 * hard power off will be done.
159 */
160 void start() override
161 {
162 _timer.restartOnce(_delay);
163 }
164
165 /**
166 * @brief Cancels the timer. This is always allowed.
167 *
168 * @param[in] force - If the cancel should be forced or not
169 * (not checked in this case)
170 * @return bool - Always returns true
171 */
172 bool cancel(bool) override
173 {
174 if (_timer.isEnabled())
175 {
176 _timer.setEnabled(false);
177 }
178
179 // Can always be canceled
180 return true;
181 }
182
183 /**
184 * @brief Performs the hard power off.
185 */
186 void powerOff()
187 {
Matt Spinlerac1efc12020-10-27 10:20:11 -0500188
189 if (_prePowerOffFunc)
190 {
191 _prePowerOffFunc();
192 }
193
Matt Spinler69b0cf02020-10-14 10:59:03 -0500194 getLogger().log(
195 fmt::format("Action '{}' executing hard power off", name()));
196 _powerIface->hardPowerOff();
197 }
198
199 private:
200 /**
201 * @brief The number of seconds to wait between starting the
202 * action and doing the power off.
203 */
204 std::chrono::seconds _delay;
205
206 /**
207 * @brief The Timer object used to handle the delay.
208 */
209 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
210};
211
212/**
213 * @class SoftPowerOff
214 *
215 * This class is derived from the PowerOffAction class
216 * and will execute a soft power off after some delay.
217 */
218class SoftPowerOff : public PowerOffAction
219{
220 public:
221 SoftPowerOff() = delete;
222 ~SoftPowerOff() = default;
223 SoftPowerOff(const SoftPowerOff&) = delete;
224 SoftPowerOff& operator=(const SoftPowerOff&) = delete;
225 SoftPowerOff(SoftPowerOff&&) = delete;
226 SoftPowerOff& operator=(SoftPowerOff&&) = delete;
227
228 /**
229 * @brief Constructor
230 *
231 * @param[in] delay - The amount of time in seconds to wait before
232 * doing the power off
233 * @param[in] powerInterface - The object to use to do the power off
Matt Spinlerac1efc12020-10-27 10:20:11 -0500234 * @param[in] func - A function to call right before the power
235 * off occurs (after the delay). May be
236 * empty if no function is necessary.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500237 */
238 SoftPowerOff(uint32_t delay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500239 std::shared_ptr<PowerInterfaceBase> powerInterface,
240 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500241 PowerOffAction("Soft Power Off: " + std::to_string(delay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500242 powerInterface, func),
Matt Spinler69b0cf02020-10-14 10:59:03 -0500243 _delay(delay),
244 _timer(_event, std::bind(std::mem_fn(&SoftPowerOff::powerOff), this))
245 {}
246
247 /**
248 * @brief Starts a timer upon the expiration of which the
249 * soft power off will be done.
250 */
251 void start() override
252 {
253 _timer.restartOnce(_delay);
254 }
255
256 /**
257 * @brief Cancels the timer. This is always allowed.
258 *
259 * @param[in] force - If the cancel should be forced or not
260 * (not checked in this case)
261 * @return bool - Always returns true
262 */
263 bool cancel(bool) override
264 {
265 if (_timer.isEnabled())
266 {
267 _timer.setEnabled(false);
268 }
269
270 // Can always be canceled
271 return true;
272 }
273
274 /**
275 * @brief Performs the soft power off.
276 */
277 void powerOff()
278 {
Matt Spinlerac1efc12020-10-27 10:20:11 -0500279 if (_prePowerOffFunc)
280 {
281 _prePowerOffFunc();
282 }
283
Matt Spinler69b0cf02020-10-14 10:59:03 -0500284 getLogger().log(
285 fmt::format("Action '{}' executing soft power off", name()));
286 _powerIface->softPowerOff();
287 }
288
289 private:
290 /**
291 * @brief The number of seconds to wait between starting the
292 * action and doing the power off.
293 */
294 std::chrono::seconds _delay;
295
296 /**
297 * @brief The Timer object used to handle the delay.
298 */
299 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
300};
301
302/**
303 * @class EpowPowerOff
304 *
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600305 * This class is derived from the PowerOffAction class and does the following:
306 * 1) On start, the service mode timer is started. This timer can be
307 * canceled if the cause is no longer satisfied (fans work again).
308 * 2) When this timer expires:
309 * a) The thermal alert D-Bus property is set, this can be used as
310 * an EPOW alert to the host that a power off is imminent.
311 * b) The meltdown timer is started. This timer cannot be canceled,
312 * and on expiration a hard power off occurs.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500313 */
314class EpowPowerOff : public PowerOffAction
315{
316 public:
317 EpowPowerOff() = delete;
318 ~EpowPowerOff() = default;
319 EpowPowerOff(const EpowPowerOff&) = delete;
320 EpowPowerOff& operator=(const EpowPowerOff&) = delete;
321 EpowPowerOff(EpowPowerOff&&) = delete;
322 EpowPowerOff& operator=(EpowPowerOff&&) = delete;
323
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600324 /**
325 * @brief Constructor
326 *
327 * @param[in] serviceModeDelay - The service mode timeout.
328 * @param[in] meltdownDelay - The meltdown delay timeout.
329 * @param[in] powerInterface - The object to use to do the power off
330 * @param[in] func - A function to call right before the power
331 * off occurs (after the delay). May be
332 * empty if no function is necessary.
333 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500334 EpowPowerOff(uint32_t serviceModeDelay, uint32_t meltdownDelay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500335 std::shared_ptr<PowerInterfaceBase> powerInterface,
336 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500337 PowerOffAction("EPOW Power Off: " + std::to_string(serviceModeDelay) +
338 "s/" + std::to_string(meltdownDelay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500339 powerInterface, func),
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600340 _serviceModeDelay(serviceModeDelay), _meltdownDelay(meltdownDelay),
341 _serviceModeTimer(
342 _event,
343 std::bind(std::mem_fn(&EpowPowerOff::serviceModeTimerExpired),
344 this)),
345 _meltdownTimer(
346 _event,
347 std::bind(std::mem_fn(&EpowPowerOff::meltdownTimerExpired), this))
Matt Spinler69b0cf02020-10-14 10:59:03 -0500348 {}
349
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600350 /**
351 * @brief Starts the service mode timer.
352 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500353 void start() override
354 {
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600355 getLogger().log(
356 fmt::format("Action {}: Starting service mode timer", name()));
357
358 _serviceModeTimer.restartOnce(_serviceModeDelay);
Matt Spinler69b0cf02020-10-14 10:59:03 -0500359 }
360
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600361 /**
362 * @brief Called when the service mode timer expires.
363 *
364 * Sets the thermal alert D-Bus property and starts the
365 * meltdown timer.
366 */
367 void serviceModeTimerExpired()
Matt Spinler69b0cf02020-10-14 10:59:03 -0500368 {
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600369 getLogger().log(fmt::format(
370 "Action {}: Service mode timer expired, starting meltdown timer",
371 name()));
372
373 _powerIface->thermalAlert(true);
374 _meltdownTimer.restartOnce(_meltdownDelay);
375 }
376
377 /**
378 * @brief Called when the meltdown timer expires.
379 *
380 * Executes a hard power off.
381 */
382 void meltdownTimerExpired()
383 {
384 getLogger().log(fmt::format(
385 "Action {}: Meltdown timer expired, executing hard power off",
386 name()));
387
388 if (_prePowerOffFunc)
389 {
390 _prePowerOffFunc();
391 }
392
393 _powerIface->hardPowerOff();
394 }
395
396 /**
397 * @brief Attempts to cancel the action
398 *
399 * The service mode timer can be canceled. The meltdown
400 * timer cannot.
401 *
402 * @param[in] force - To force the cancel (like if the
403 * system powers off).
404 *
405 * @return bool - If the cancel was successful
406 */
407 bool cancel(bool force) override
408 {
409 if (_serviceModeTimer.isEnabled())
410 {
411 _serviceModeTimer.setEnabled(false);
412 }
413
414 if (_meltdownTimer.isEnabled())
415 {
416 if (force)
417 {
418 _meltdownTimer.setEnabled(false);
419 }
420 else
421 {
422 getLogger().log("Cannot cancel running meltdown timer");
423 return false;
424 }
425 }
Matt Spinler69b0cf02020-10-14 10:59:03 -0500426 return true;
427 }
428
429 private:
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600430 /**
431 * @brief The number of seconds to wait until starting the uncancelable
432 * meltdown timer.
433 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500434 std::chrono::seconds _serviceModeDelay;
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600435
436 /**
437 * @brief The number of seconds to wait after the service mode
438 * timer expires before a hard power off will occur.
439 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500440 std::chrono::seconds _meltdownDelay;
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600441
442 /**
443 * @brief The service mode timer.
444 */
445 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>
446 _serviceModeTimer;
447
448 /**
449 * @brief The meltdown timer.
450 */
451 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _meltdownTimer;
Matt Spinler69b0cf02020-10-14 10:59:03 -0500452};
453} // namespace phosphor::fan::monitor