blob: 6d7580b5f3d1a1beb70f1e1e03922f9ae0a32e2d [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
Matt Spinler69b0cf02020-10-14 10:59:03 -05007#include <sdeventplus/clock.hpp>
8#include <sdeventplus/event.hpp>
9#include <sdeventplus/utility/timer.hpp>
10
11#include <chrono>
Patrick Williamsfbf47032023-07-17 12:27:34 -050012#include <format>
Matt Spinler69b0cf02020-10-14 10:59:03 -050013
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 /**
Mike Capps683a96c2022-04-27 16:46:06 -040099 * @brief Create a BMC Dump
100 */
101 void createBmcDump() const
102 {
103 try
104 {
105 util::SDBusPlus::callMethod(
106 "xyz.openbmc_project.Dump.Manager",
107 "/xyz/openbmc_project/dump/bmc",
108 "xyz.openbmc_project.Dump.Create", "CreateDump",
109 std::vector<std::pair<std::string,
110 std::variant<std::string, uint64_t>>>());
111 }
Mike Capps477b13b2022-07-11 10:45:46 -0400112 catch (const std::exception& e)
113 {
114 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500115 std::format("Caught exception while creating BMC dump: {}",
Mike Capps477b13b2022-07-11 10:45:46 -0400116 e.what()),
117 Logger::error);
118 }
Mike Capps683a96c2022-04-27 16:46:06 -0400119 }
120
121 /**
Matt Spinler69b0cf02020-10-14 10:59:03 -0500122 * @brief The name of the action, which is set by the
123 * derived class.
124 */
125 const std::string _name;
126
127 /**
Matt Spinler69b0cf02020-10-14 10:59:03 -0500128 * @brief The object used to invoke the power off with.
129 */
130 std::shared_ptr<PowerInterfaceBase> _powerIface;
131
132 /**
133 * @brief The event loop object. Needed by timers.
134 */
135 sdeventplus::Event _event;
Matt Spinlerac1efc12020-10-27 10:20:11 -0500136
137 /**
138 * @brief A function that will be called right before
139 * the power off.
140 */
141 PrePowerOffFunc _prePowerOffFunc;
Matt Spinler69b0cf02020-10-14 10:59:03 -0500142};
143
144/**
145 * @class HardPowerOff
146 *
147 * This class is derived from the PowerOffAction class
148 * and will execute a hard power off after some delay.
149 */
150class HardPowerOff : public PowerOffAction
151{
152 public:
153 HardPowerOff() = delete;
154 ~HardPowerOff() = default;
155 HardPowerOff(const HardPowerOff&) = delete;
156 HardPowerOff& operator=(const HardPowerOff&) = delete;
157 HardPowerOff(HardPowerOff&&) = delete;
158 HardPowerOff& operator=(HardPowerOff&&) = delete;
159
160 /**
161 * @brief Constructor
162 *
163 * @param[in] delay - The amount of time in seconds to wait before
164 * doing the power off
165 * @param[in] powerInterface - The object to use to do the power off
Matt Spinlerac1efc12020-10-27 10:20:11 -0500166 * @param[in] func - A function to call right before the power
167 * off occurs (after the delay). May be
168 * empty if no function is necessary.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500169 */
170 HardPowerOff(uint32_t delay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500171 std::shared_ptr<PowerInterfaceBase> powerInterface,
172 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500173 PowerOffAction("Hard Power Off: " + std::to_string(delay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500174 powerInterface, func),
Matt Spinler69b0cf02020-10-14 10:59:03 -0500175 _delay(delay),
176 _timer(_event, std::bind(std::mem_fn(&HardPowerOff::powerOff), this))
177 {}
178
179 /**
180 * @brief Starts a timer upon the expiration of which the
181 * hard power off will be done.
182 */
183 void start() override
184 {
185 _timer.restartOnce(_delay);
186 }
187
188 /**
189 * @brief Cancels the timer. This is always allowed.
190 *
191 * @param[in] force - If the cancel should be forced or not
192 * (not checked in this case)
193 * @return bool - Always returns true
194 */
195 bool cancel(bool) override
196 {
197 if (_timer.isEnabled())
198 {
199 _timer.setEnabled(false);
200 }
201
202 // Can always be canceled
203 return true;
204 }
205
206 /**
207 * @brief Performs the hard power off.
208 */
209 void powerOff()
210 {
Matt Spinlerac1efc12020-10-27 10:20:11 -0500211 if (_prePowerOffFunc)
212 {
213 _prePowerOffFunc();
214 }
215
Matt Spinler69b0cf02020-10-14 10:59:03 -0500216 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500217 std::format("Action '{}' executing hard power off", name()));
Matt Spinler69b0cf02020-10-14 10:59:03 -0500218 _powerIface->hardPowerOff();
Mike Capps683a96c2022-04-27 16:46:06 -0400219
220 createBmcDump();
Matt Spinler69b0cf02020-10-14 10:59:03 -0500221 }
222
223 private:
224 /**
225 * @brief The number of seconds to wait between starting the
226 * action and doing the power off.
227 */
228 std::chrono::seconds _delay;
229
230 /**
231 * @brief The Timer object used to handle the delay.
232 */
233 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
234};
235
236/**
237 * @class SoftPowerOff
238 *
239 * This class is derived from the PowerOffAction class
240 * and will execute a soft power off after some delay.
241 */
242class SoftPowerOff : public PowerOffAction
243{
244 public:
245 SoftPowerOff() = delete;
246 ~SoftPowerOff() = default;
247 SoftPowerOff(const SoftPowerOff&) = delete;
248 SoftPowerOff& operator=(const SoftPowerOff&) = delete;
249 SoftPowerOff(SoftPowerOff&&) = delete;
250 SoftPowerOff& operator=(SoftPowerOff&&) = delete;
251
252 /**
253 * @brief Constructor
254 *
255 * @param[in] delay - The amount of time in seconds to wait before
256 * doing the power off
257 * @param[in] powerInterface - The object to use to do the power off
Matt Spinlerac1efc12020-10-27 10:20:11 -0500258 * @param[in] func - A function to call right before the power
259 * off occurs (after the delay). May be
260 * empty if no function is necessary.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500261 */
262 SoftPowerOff(uint32_t delay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500263 std::shared_ptr<PowerInterfaceBase> powerInterface,
264 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500265 PowerOffAction("Soft Power Off: " + std::to_string(delay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500266 powerInterface, func),
Matt Spinler69b0cf02020-10-14 10:59:03 -0500267 _delay(delay),
268 _timer(_event, std::bind(std::mem_fn(&SoftPowerOff::powerOff), this))
269 {}
270
271 /**
272 * @brief Starts a timer upon the expiration of which the
273 * soft power off will be done.
274 */
275 void start() override
276 {
277 _timer.restartOnce(_delay);
278 }
279
280 /**
281 * @brief Cancels the timer. This is always allowed.
282 *
283 * @param[in] force - If the cancel should be forced or not
284 * (not checked in this case)
285 * @return bool - Always returns true
286 */
287 bool cancel(bool) override
288 {
289 if (_timer.isEnabled())
290 {
291 _timer.setEnabled(false);
292 }
293
294 // Can always be canceled
295 return true;
296 }
297
298 /**
299 * @brief Performs the soft power off.
300 */
301 void powerOff()
302 {
Matt Spinlerac1efc12020-10-27 10:20:11 -0500303 if (_prePowerOffFunc)
304 {
305 _prePowerOffFunc();
306 }
307
Matt Spinler69b0cf02020-10-14 10:59:03 -0500308 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500309 std::format("Action '{}' executing soft power off", name()));
Matt Spinler69b0cf02020-10-14 10:59:03 -0500310 _powerIface->softPowerOff();
Mike Capps683a96c2022-04-27 16:46:06 -0400311
312 createBmcDump();
Matt Spinler69b0cf02020-10-14 10:59:03 -0500313 }
314
315 private:
316 /**
317 * @brief The number of seconds to wait between starting the
318 * action and doing the power off.
319 */
320 std::chrono::seconds _delay;
321
322 /**
323 * @brief The Timer object used to handle the delay.
324 */
325 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
326};
327
328/**
329 * @class EpowPowerOff
330 *
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600331 * This class is derived from the PowerOffAction class and does the following:
332 * 1) On start, the service mode timer is started. This timer can be
333 * canceled if the cause is no longer satisfied (fans work again).
334 * 2) When this timer expires:
335 * a) The thermal alert D-Bus property is set, this can be used as
336 * an EPOW alert to the host that a power off is imminent.
337 * b) The meltdown timer is started. This timer cannot be canceled,
338 * and on expiration a hard power off occurs.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500339 */
340class EpowPowerOff : public PowerOffAction
341{
342 public:
343 EpowPowerOff() = delete;
344 ~EpowPowerOff() = default;
345 EpowPowerOff(const EpowPowerOff&) = delete;
346 EpowPowerOff& operator=(const EpowPowerOff&) = delete;
347 EpowPowerOff(EpowPowerOff&&) = delete;
348 EpowPowerOff& operator=(EpowPowerOff&&) = delete;
349
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600350 /**
351 * @brief Constructor
352 *
353 * @param[in] serviceModeDelay - The service mode timeout.
354 * @param[in] meltdownDelay - The meltdown delay timeout.
355 * @param[in] powerInterface - The object to use to do the power off
356 * @param[in] func - A function to call right before the power
357 * off occurs (after the delay). May be
358 * empty if no function is necessary.
359 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500360 EpowPowerOff(uint32_t serviceModeDelay, uint32_t meltdownDelay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500361 std::shared_ptr<PowerInterfaceBase> powerInterface,
362 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500363 PowerOffAction("EPOW Power Off: " + std::to_string(serviceModeDelay) +
364 "s/" + std::to_string(meltdownDelay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500365 powerInterface, func),
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600366 _serviceModeDelay(serviceModeDelay), _meltdownDelay(meltdownDelay),
367 _serviceModeTimer(
368 _event,
369 std::bind(std::mem_fn(&EpowPowerOff::serviceModeTimerExpired),
370 this)),
371 _meltdownTimer(
372 _event,
373 std::bind(std::mem_fn(&EpowPowerOff::meltdownTimerExpired), this))
Matt Spinler69b0cf02020-10-14 10:59:03 -0500374 {}
375
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600376 /**
377 * @brief Starts the service mode timer.
378 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500379 void start() override
380 {
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600381 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500382 std::format("Action {}: Starting service mode timer", name()));
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600383
384 _serviceModeTimer.restartOnce(_serviceModeDelay);
Matt Spinler69b0cf02020-10-14 10:59:03 -0500385 }
386
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600387 /**
388 * @brief Called when the service mode timer expires.
389 *
390 * Sets the thermal alert D-Bus property and starts the
391 * meltdown timer.
392 */
393 void serviceModeTimerExpired()
Matt Spinler69b0cf02020-10-14 10:59:03 -0500394 {
Patrick Williamsfbf47032023-07-17 12:27:34 -0500395 getLogger().log(std::format(
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600396 "Action {}: Service mode timer expired, starting meltdown timer",
397 name()));
398
399 _powerIface->thermalAlert(true);
400 _meltdownTimer.restartOnce(_meltdownDelay);
401 }
402
403 /**
404 * @brief Called when the meltdown timer expires.
405 *
406 * Executes a hard power off.
407 */
408 void meltdownTimerExpired()
409 {
Patrick Williamsfbf47032023-07-17 12:27:34 -0500410 getLogger().log(std::format(
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600411 "Action {}: Meltdown timer expired, executing hard power off",
412 name()));
413
414 if (_prePowerOffFunc)
415 {
416 _prePowerOffFunc();
417 }
418
419 _powerIface->hardPowerOff();
Mike Capps683a96c2022-04-27 16:46:06 -0400420 createBmcDump();
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600421 }
422
423 /**
424 * @brief Attempts to cancel the action
425 *
426 * The service mode timer can be canceled. The meltdown
427 * timer cannot.
428 *
429 * @param[in] force - To force the cancel (like if the
430 * system powers off).
431 *
432 * @return bool - If the cancel was successful
433 */
434 bool cancel(bool force) override
435 {
436 if (_serviceModeTimer.isEnabled())
437 {
438 _serviceModeTimer.setEnabled(false);
439 }
440
441 if (_meltdownTimer.isEnabled())
442 {
443 if (force)
444 {
445 _meltdownTimer.setEnabled(false);
446 }
447 else
448 {
449 getLogger().log("Cannot cancel running meltdown timer");
450 return false;
451 }
452 }
Matt Spinler69b0cf02020-10-14 10:59:03 -0500453 return true;
454 }
455
456 private:
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600457 /**
458 * @brief The number of seconds to wait until starting the uncancelable
459 * meltdown timer.
460 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500461 std::chrono::seconds _serviceModeDelay;
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600462
463 /**
464 * @brief The number of seconds to wait after the service mode
465 * timer expires before a hard power off will occur.
466 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500467 std::chrono::seconds _meltdownDelay;
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600468
469 /**
470 * @brief The service mode timer.
471 */
472 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>
473 _serviceModeTimer;
474
475 /**
476 * @brief The meltdown timer.
477 */
478 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _meltdownTimer;
Matt Spinler69b0cf02020-10-14 10:59:03 -0500479};
480} // namespace phosphor::fan::monitor