blob: cada34855b5e48b60a5a1eca02b6cc5a2856d40a [file] [log] [blame]
Matt Spinler69b0cf02020-10-14 10:59:03 -05001#pragma once
2
3#include "logging.hpp"
4#include "power_interface.hpp"
Mike Capps683a96c2022-04-27 16:46:06 -04005#include "sdbusplus.hpp"
Matt Spinler69b0cf02020-10-14 10:59:03 -05006
7#include <fmt/format.h>
8
9#include <sdeventplus/clock.hpp>
10#include <sdeventplus/event.hpp>
11#include <sdeventplus/utility/timer.hpp>
12
13#include <chrono>
14
15namespace phosphor::fan::monitor
16{
17
18/**
19 * @class PowerOffAction
20 *
21 * This is the base class for a power off action, which is
22 * used by the PowerOffRule class to do different types of
23 * power offs based on fan failures.
24 *
25 * The power off is started with the start() method, and the
26 * derived class may or may not allow it to be stopped with
27 * the cancel() method, which is really only useful when
28 * there is a delay before the power off.
29 *
30 * It uses the PowerInterfaceBase object pointer to perform
31 * the D-Bus call to do the power off, so it can be mocked
32 * for testing.
33 */
34class PowerOffAction
35{
36 public:
Matt Spinlerac1efc12020-10-27 10:20:11 -050037 using PrePowerOffFunc = std::function<void()>;
38
Matt Spinler69b0cf02020-10-14 10:59:03 -050039 PowerOffAction() = delete;
40 virtual ~PowerOffAction() = default;
41 PowerOffAction(const PowerOffAction&) = delete;
42 PowerOffAction& operator=(const PowerOffAction&) = delete;
43 PowerOffAction(PowerOffAction&&) = delete;
44 PowerOffAction& operator=(PowerOffAction&&) = delete;
45
46 /**
47 * @brief Constructor
48 *
49 * @param[in] name - The action name. Used for tracing.
Matt Spinlerac1efc12020-10-27 10:20:11 -050050 * @param[in] powerInterface - The object used to invoke the power off.
51 * @param[in] powerOffFunc - A function to call right before the power
52 * off occurs (after any delays). May be
53 * empty if no function is necessary.
Matt Spinler69b0cf02020-10-14 10:59:03 -050054 */
55 PowerOffAction(const std::string& name,
Matt Spinlerac1efc12020-10-27 10:20:11 -050056 std::shared_ptr<PowerInterfaceBase> powerInterface,
57 PrePowerOffFunc& powerOffFunc) :
Matt Spinler69b0cf02020-10-14 10:59:03 -050058 _name(name),
59 _powerIface(std::move(powerInterface)),
Matt Spinlerac1efc12020-10-27 10:20:11 -050060 _event(sdeventplus::Event::get_default()),
61 _prePowerOffFunc(powerOffFunc)
Matt Spinler69b0cf02020-10-14 10:59:03 -050062 {}
63
64 /**
65 * @brief Starts the power off.
66 *
67 * Though this occurs in the child class, usually this
68 * involves starting a timer and then powering off when it
69 * times out.
70 */
71 virtual void start() = 0;
72
73 /**
74 * @brief Attempts to cancel the power off, if the derived
75 * class allows it, and assuming the power off hasn't
76 * already happened.
77 *
78 * The 'force' parameter is mainly for use when something else
79 * powered off the system so this action doesn't need to run
80 * anymore even if it isn't usually cancelable.
81 *
82 * @param[in] force - If the cancel should be forced
83 *
84 * @return bool - If the cancel was allowed/successful
85 */
86 virtual bool cancel(bool force) = 0;
87
88 /**
Matt Spinler69b0cf02020-10-14 10:59:03 -050089 * @brief Returns the name of the action
90 *
91 * @return const std::string& - The name
92 */
93 const std::string& name() const
94 {
95 return _name;
96 }
97
98 protected:
99 /**
Mike Capps683a96c2022-04-27 16:46:06 -0400100 * @brief Create a BMC Dump
101 */
102 void createBmcDump() const
103 {
104 try
105 {
106 util::SDBusPlus::callMethod(
107 "xyz.openbmc_project.Dump.Manager",
108 "/xyz/openbmc_project/dump/bmc",
109 "xyz.openbmc_project.Dump.Create", "CreateDump",
110 std::vector<std::pair<std::string,
111 std::variant<std::string, uint64_t>>>());
112 }
113 catch (const sdbusplus::exception::exception&)
114 {}
115 }
116
117 /**
Matt Spinler69b0cf02020-10-14 10:59:03 -0500118 * @brief The name of the action, which is set by the
119 * derived class.
120 */
121 const std::string _name;
122
123 /**
Matt Spinler69b0cf02020-10-14 10:59:03 -0500124 * @brief The object used to invoke the power off with.
125 */
126 std::shared_ptr<PowerInterfaceBase> _powerIface;
127
128 /**
129 * @brief The event loop object. Needed by timers.
130 */
131 sdeventplus::Event _event;
Matt Spinlerac1efc12020-10-27 10:20:11 -0500132
133 /**
134 * @brief A function that will be called right before
135 * the power off.
136 */
137 PrePowerOffFunc _prePowerOffFunc;
Matt Spinler69b0cf02020-10-14 10:59:03 -0500138};
139
140/**
141 * @class HardPowerOff
142 *
143 * This class is derived from the PowerOffAction class
144 * and will execute a hard power off after some delay.
145 */
146class HardPowerOff : public PowerOffAction
147{
148 public:
149 HardPowerOff() = delete;
150 ~HardPowerOff() = default;
151 HardPowerOff(const HardPowerOff&) = delete;
152 HardPowerOff& operator=(const HardPowerOff&) = delete;
153 HardPowerOff(HardPowerOff&&) = delete;
154 HardPowerOff& operator=(HardPowerOff&&) = delete;
155
156 /**
157 * @brief Constructor
158 *
159 * @param[in] delay - The amount of time in seconds to wait before
160 * doing the power off
161 * @param[in] powerInterface - The object to use to do the power off
Matt Spinlerac1efc12020-10-27 10:20:11 -0500162 * @param[in] func - A function to call right before the power
163 * off occurs (after the delay). May be
164 * empty if no function is necessary.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500165 */
166 HardPowerOff(uint32_t delay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500167 std::shared_ptr<PowerInterfaceBase> powerInterface,
168 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500169 PowerOffAction("Hard Power Off: " + std::to_string(delay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500170 powerInterface, func),
Matt Spinler69b0cf02020-10-14 10:59:03 -0500171 _delay(delay),
172 _timer(_event, std::bind(std::mem_fn(&HardPowerOff::powerOff), this))
173 {}
174
175 /**
176 * @brief Starts a timer upon the expiration of which the
177 * hard power off will be done.
178 */
179 void start() override
180 {
181 _timer.restartOnce(_delay);
182 }
183
184 /**
185 * @brief Cancels the timer. This is always allowed.
186 *
187 * @param[in] force - If the cancel should be forced or not
188 * (not checked in this case)
189 * @return bool - Always returns true
190 */
191 bool cancel(bool) override
192 {
193 if (_timer.isEnabled())
194 {
195 _timer.setEnabled(false);
196 }
197
198 // Can always be canceled
199 return true;
200 }
201
202 /**
203 * @brief Performs the hard power off.
204 */
205 void powerOff()
206 {
Matt Spinlerac1efc12020-10-27 10:20:11 -0500207 if (_prePowerOffFunc)
208 {
209 _prePowerOffFunc();
210 }
211
Matt Spinler69b0cf02020-10-14 10:59:03 -0500212 getLogger().log(
213 fmt::format("Action '{}' executing hard power off", name()));
214 _powerIface->hardPowerOff();
Mike Capps683a96c2022-04-27 16:46:06 -0400215
216 createBmcDump();
Matt Spinler69b0cf02020-10-14 10:59:03 -0500217 }
218
219 private:
220 /**
221 * @brief The number of seconds to wait between starting the
222 * action and doing the power off.
223 */
224 std::chrono::seconds _delay;
225
226 /**
227 * @brief The Timer object used to handle the delay.
228 */
229 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
230};
231
232/**
233 * @class SoftPowerOff
234 *
235 * This class is derived from the PowerOffAction class
236 * and will execute a soft power off after some delay.
237 */
238class SoftPowerOff : public PowerOffAction
239{
240 public:
241 SoftPowerOff() = delete;
242 ~SoftPowerOff() = default;
243 SoftPowerOff(const SoftPowerOff&) = delete;
244 SoftPowerOff& operator=(const SoftPowerOff&) = delete;
245 SoftPowerOff(SoftPowerOff&&) = delete;
246 SoftPowerOff& operator=(SoftPowerOff&&) = delete;
247
248 /**
249 * @brief Constructor
250 *
251 * @param[in] delay - The amount of time in seconds to wait before
252 * doing the power off
253 * @param[in] powerInterface - The object to use to do the power off
Matt Spinlerac1efc12020-10-27 10:20:11 -0500254 * @param[in] func - A function to call right before the power
255 * off occurs (after the delay). May be
256 * empty if no function is necessary.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500257 */
258 SoftPowerOff(uint32_t delay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500259 std::shared_ptr<PowerInterfaceBase> powerInterface,
260 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500261 PowerOffAction("Soft Power Off: " + std::to_string(delay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500262 powerInterface, func),
Matt Spinler69b0cf02020-10-14 10:59:03 -0500263 _delay(delay),
264 _timer(_event, std::bind(std::mem_fn(&SoftPowerOff::powerOff), this))
265 {}
266
267 /**
268 * @brief Starts a timer upon the expiration of which the
269 * soft power off will be done.
270 */
271 void start() override
272 {
273 _timer.restartOnce(_delay);
274 }
275
276 /**
277 * @brief Cancels the timer. This is always allowed.
278 *
279 * @param[in] force - If the cancel should be forced or not
280 * (not checked in this case)
281 * @return bool - Always returns true
282 */
283 bool cancel(bool) override
284 {
285 if (_timer.isEnabled())
286 {
287 _timer.setEnabled(false);
288 }
289
290 // Can always be canceled
291 return true;
292 }
293
294 /**
295 * @brief Performs the soft power off.
296 */
297 void powerOff()
298 {
Matt Spinlerac1efc12020-10-27 10:20:11 -0500299 if (_prePowerOffFunc)
300 {
301 _prePowerOffFunc();
302 }
303
Matt Spinler69b0cf02020-10-14 10:59:03 -0500304 getLogger().log(
305 fmt::format("Action '{}' executing soft power off", name()));
306 _powerIface->softPowerOff();
Mike Capps683a96c2022-04-27 16:46:06 -0400307
308 createBmcDump();
Matt Spinler69b0cf02020-10-14 10:59:03 -0500309 }
310
311 private:
312 /**
313 * @brief The number of seconds to wait between starting the
314 * action and doing the power off.
315 */
316 std::chrono::seconds _delay;
317
318 /**
319 * @brief The Timer object used to handle the delay.
320 */
321 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
322};
323
324/**
325 * @class EpowPowerOff
326 *
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600327 * This class is derived from the PowerOffAction class and does the following:
328 * 1) On start, the service mode timer is started. This timer can be
329 * canceled if the cause is no longer satisfied (fans work again).
330 * 2) When this timer expires:
331 * a) The thermal alert D-Bus property is set, this can be used as
332 * an EPOW alert to the host that a power off is imminent.
333 * b) The meltdown timer is started. This timer cannot be canceled,
334 * and on expiration a hard power off occurs.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500335 */
336class EpowPowerOff : public PowerOffAction
337{
338 public:
339 EpowPowerOff() = delete;
340 ~EpowPowerOff() = default;
341 EpowPowerOff(const EpowPowerOff&) = delete;
342 EpowPowerOff& operator=(const EpowPowerOff&) = delete;
343 EpowPowerOff(EpowPowerOff&&) = delete;
344 EpowPowerOff& operator=(EpowPowerOff&&) = delete;
345
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600346 /**
347 * @brief Constructor
348 *
349 * @param[in] serviceModeDelay - The service mode timeout.
350 * @param[in] meltdownDelay - The meltdown delay timeout.
351 * @param[in] powerInterface - The object to use to do the power off
352 * @param[in] func - A function to call right before the power
353 * off occurs (after the delay). May be
354 * empty if no function is necessary.
355 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500356 EpowPowerOff(uint32_t serviceModeDelay, uint32_t meltdownDelay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500357 std::shared_ptr<PowerInterfaceBase> powerInterface,
358 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500359 PowerOffAction("EPOW Power Off: " + std::to_string(serviceModeDelay) +
360 "s/" + std::to_string(meltdownDelay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500361 powerInterface, func),
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600362 _serviceModeDelay(serviceModeDelay), _meltdownDelay(meltdownDelay),
363 _serviceModeTimer(
364 _event,
365 std::bind(std::mem_fn(&EpowPowerOff::serviceModeTimerExpired),
366 this)),
367 _meltdownTimer(
368 _event,
369 std::bind(std::mem_fn(&EpowPowerOff::meltdownTimerExpired), this))
Matt Spinler69b0cf02020-10-14 10:59:03 -0500370 {}
371
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600372 /**
373 * @brief Starts the service mode timer.
374 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500375 void start() override
376 {
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600377 getLogger().log(
378 fmt::format("Action {}: Starting service mode timer", name()));
379
380 _serviceModeTimer.restartOnce(_serviceModeDelay);
Matt Spinler69b0cf02020-10-14 10:59:03 -0500381 }
382
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600383 /**
384 * @brief Called when the service mode timer expires.
385 *
386 * Sets the thermal alert D-Bus property and starts the
387 * meltdown timer.
388 */
389 void serviceModeTimerExpired()
Matt Spinler69b0cf02020-10-14 10:59:03 -0500390 {
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600391 getLogger().log(fmt::format(
392 "Action {}: Service mode timer expired, starting meltdown timer",
393 name()));
394
395 _powerIface->thermalAlert(true);
396 _meltdownTimer.restartOnce(_meltdownDelay);
397 }
398
399 /**
400 * @brief Called when the meltdown timer expires.
401 *
402 * Executes a hard power off.
403 */
404 void meltdownTimerExpired()
405 {
406 getLogger().log(fmt::format(
407 "Action {}: Meltdown timer expired, executing hard power off",
408 name()));
409
410 if (_prePowerOffFunc)
411 {
412 _prePowerOffFunc();
413 }
414
415 _powerIface->hardPowerOff();
Mike Capps683a96c2022-04-27 16:46:06 -0400416 createBmcDump();
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600417 }
418
419 /**
420 * @brief Attempts to cancel the action
421 *
422 * The service mode timer can be canceled. The meltdown
423 * timer cannot.
424 *
425 * @param[in] force - To force the cancel (like if the
426 * system powers off).
427 *
428 * @return bool - If the cancel was successful
429 */
430 bool cancel(bool force) override
431 {
432 if (_serviceModeTimer.isEnabled())
433 {
434 _serviceModeTimer.setEnabled(false);
435 }
436
437 if (_meltdownTimer.isEnabled())
438 {
439 if (force)
440 {
441 _meltdownTimer.setEnabled(false);
442 }
443 else
444 {
445 getLogger().log("Cannot cancel running meltdown timer");
446 return false;
447 }
448 }
Matt Spinler69b0cf02020-10-14 10:59:03 -0500449 return true;
450 }
451
452 private:
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600453 /**
454 * @brief The number of seconds to wait until starting the uncancelable
455 * meltdown timer.
456 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500457 std::chrono::seconds _serviceModeDelay;
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600458
459 /**
460 * @brief The number of seconds to wait after the service mode
461 * timer expires before a hard power off will occur.
462 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500463 std::chrono::seconds _meltdownDelay;
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600464
465 /**
466 * @brief The service mode timer.
467 */
468 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>
469 _serviceModeTimer;
470
471 /**
472 * @brief The meltdown timer.
473 */
474 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _meltdownTimer;
Matt Spinler69b0cf02020-10-14 10:59:03 -0500475};
476} // namespace phosphor::fan::monitor