blob: 348173b4c19dd30e5c24a1090c882d082fac10de [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) :
Patrick Williamsdfddd642024-08-16 15:21:51 -040057 _name(name), _powerIface(std::move(powerInterface)),
Matt Spinlerac1efc12020-10-27 10:20:11 -050058 _event(sdeventplus::Event::get_default()),
59 _prePowerOffFunc(powerOffFunc)
Matt Spinler69b0cf02020-10-14 10:59:03 -050060 {}
61
62 /**
63 * @brief Starts the power off.
64 *
65 * Though this occurs in the child class, usually this
66 * involves starting a timer and then powering off when it
67 * times out.
68 */
69 virtual void start() = 0;
70
71 /**
72 * @brief Attempts to cancel the power off, if the derived
73 * class allows it, and assuming the power off hasn't
74 * already happened.
75 *
76 * The 'force' parameter is mainly for use when something else
77 * powered off the system so this action doesn't need to run
78 * anymore even if it isn't usually cancelable.
79 *
80 * @param[in] force - If the cancel should be forced
81 *
82 * @return bool - If the cancel was allowed/successful
83 */
84 virtual bool cancel(bool force) = 0;
85
86 /**
Matt Spinler69b0cf02020-10-14 10:59:03 -050087 * @brief Returns the name of the action
88 *
89 * @return const std::string& - The name
90 */
91 const std::string& name() const
92 {
93 return _name;
94 }
95
96 protected:
97 /**
Mike Capps683a96c2022-04-27 16:46:06 -040098 * @brief Create a BMC Dump
99 */
100 void createBmcDump() const
101 {
102 try
103 {
104 util::SDBusPlus::callMethod(
105 "xyz.openbmc_project.Dump.Manager",
106 "/xyz/openbmc_project/dump/bmc",
107 "xyz.openbmc_project.Dump.Create", "CreateDump",
108 std::vector<std::pair<std::string,
109 std::variant<std::string, uint64_t>>>());
110 }
Mike Capps477b13b2022-07-11 10:45:46 -0400111 catch (const std::exception& e)
112 {
113 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500114 std::format("Caught exception while creating BMC dump: {}",
Mike Capps477b13b2022-07-11 10:45:46 -0400115 e.what()),
116 Logger::error);
117 }
Mike Capps683a96c2022-04-27 16:46:06 -0400118 }
119
120 /**
Matt Spinler69b0cf02020-10-14 10:59:03 -0500121 * @brief The name of the action, which is set by the
122 * derived class.
123 */
124 const std::string _name;
125
126 /**
Matt Spinler69b0cf02020-10-14 10:59:03 -0500127 * @brief The object used to invoke the power off with.
128 */
129 std::shared_ptr<PowerInterfaceBase> _powerIface;
130
131 /**
132 * @brief The event loop object. Needed by timers.
133 */
134 sdeventplus::Event _event;
Matt Spinlerac1efc12020-10-27 10:20:11 -0500135
136 /**
137 * @brief A function that will be called right before
138 * the power off.
139 */
140 PrePowerOffFunc _prePowerOffFunc;
Matt Spinler69b0cf02020-10-14 10:59:03 -0500141};
142
143/**
144 * @class HardPowerOff
145 *
146 * This class is derived from the PowerOffAction class
147 * and will execute a hard power off after some delay.
148 */
149class HardPowerOff : public PowerOffAction
150{
151 public:
152 HardPowerOff() = delete;
153 ~HardPowerOff() = default;
154 HardPowerOff(const HardPowerOff&) = delete;
155 HardPowerOff& operator=(const HardPowerOff&) = delete;
156 HardPowerOff(HardPowerOff&&) = delete;
157 HardPowerOff& operator=(HardPowerOff&&) = delete;
158
159 /**
160 * @brief Constructor
161 *
162 * @param[in] delay - The amount of time in seconds to wait before
163 * doing the power off
164 * @param[in] powerInterface - The object to use to do the power off
Matt Spinlerac1efc12020-10-27 10:20:11 -0500165 * @param[in] func - A function to call right before the power
166 * off occurs (after the delay). May be
167 * empty if no function is necessary.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500168 */
169 HardPowerOff(uint32_t delay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500170 std::shared_ptr<PowerInterfaceBase> powerInterface,
171 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500172 PowerOffAction("Hard Power Off: " + std::to_string(delay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500173 powerInterface, func),
Matt Spinler69b0cf02020-10-14 10:59:03 -0500174 _delay(delay),
175 _timer(_event, std::bind(std::mem_fn(&HardPowerOff::powerOff), this))
176 {}
177
178 /**
179 * @brief Starts a timer upon the expiration of which the
180 * hard power off will be done.
181 */
182 void start() override
183 {
184 _timer.restartOnce(_delay);
185 }
186
187 /**
188 * @brief Cancels the timer. This is always allowed.
189 *
190 * @param[in] force - If the cancel should be forced or not
191 * (not checked in this case)
192 * @return bool - Always returns true
193 */
194 bool cancel(bool) override
195 {
196 if (_timer.isEnabled())
197 {
198 _timer.setEnabled(false);
199 }
200
201 // Can always be canceled
202 return true;
203 }
204
205 /**
206 * @brief Performs the hard power off.
207 */
208 void powerOff()
209 {
Matt Spinlerac1efc12020-10-27 10:20:11 -0500210 if (_prePowerOffFunc)
211 {
212 _prePowerOffFunc();
213 }
214
Matt Spinler69b0cf02020-10-14 10:59:03 -0500215 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500216 std::format("Action '{}' executing hard power off", name()));
Matt Spinler69b0cf02020-10-14 10:59:03 -0500217 _powerIface->hardPowerOff();
Mike Capps683a96c2022-04-27 16:46:06 -0400218
219 createBmcDump();
Matt Spinler69b0cf02020-10-14 10:59:03 -0500220 }
221
222 private:
223 /**
224 * @brief The number of seconds to wait between starting the
225 * action and doing the power off.
226 */
227 std::chrono::seconds _delay;
228
229 /**
230 * @brief The Timer object used to handle the delay.
231 */
232 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
233};
234
235/**
236 * @class SoftPowerOff
237 *
238 * This class is derived from the PowerOffAction class
239 * and will execute a soft power off after some delay.
240 */
241class SoftPowerOff : public PowerOffAction
242{
243 public:
244 SoftPowerOff() = delete;
245 ~SoftPowerOff() = default;
246 SoftPowerOff(const SoftPowerOff&) = delete;
247 SoftPowerOff& operator=(const SoftPowerOff&) = delete;
248 SoftPowerOff(SoftPowerOff&&) = delete;
249 SoftPowerOff& operator=(SoftPowerOff&&) = delete;
250
251 /**
252 * @brief Constructor
253 *
254 * @param[in] delay - The amount of time in seconds to wait before
255 * doing the power off
256 * @param[in] powerInterface - The object to use to do the power off
Matt Spinlerac1efc12020-10-27 10:20:11 -0500257 * @param[in] func - A function to call right before the power
258 * off occurs (after the delay). May be
259 * empty if no function is necessary.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500260 */
261 SoftPowerOff(uint32_t delay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500262 std::shared_ptr<PowerInterfaceBase> powerInterface,
263 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500264 PowerOffAction("Soft Power Off: " + std::to_string(delay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500265 powerInterface, func),
Matt Spinler69b0cf02020-10-14 10:59:03 -0500266 _delay(delay),
267 _timer(_event, std::bind(std::mem_fn(&SoftPowerOff::powerOff), this))
268 {}
269
270 /**
271 * @brief Starts a timer upon the expiration of which the
272 * soft power off will be done.
273 */
274 void start() override
275 {
276 _timer.restartOnce(_delay);
277 }
278
279 /**
280 * @brief Cancels the timer. This is always allowed.
281 *
282 * @param[in] force - If the cancel should be forced or not
283 * (not checked in this case)
284 * @return bool - Always returns true
285 */
286 bool cancel(bool) override
287 {
288 if (_timer.isEnabled())
289 {
290 _timer.setEnabled(false);
291 }
292
293 // Can always be canceled
294 return true;
295 }
296
297 /**
298 * @brief Performs the soft power off.
299 */
300 void powerOff()
301 {
Matt Spinlerac1efc12020-10-27 10:20:11 -0500302 if (_prePowerOffFunc)
303 {
304 _prePowerOffFunc();
305 }
306
Matt Spinler69b0cf02020-10-14 10:59:03 -0500307 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500308 std::format("Action '{}' executing soft power off", name()));
Matt Spinler69b0cf02020-10-14 10:59:03 -0500309 _powerIface->softPowerOff();
Mike Capps683a96c2022-04-27 16:46:06 -0400310
311 createBmcDump();
Matt Spinler69b0cf02020-10-14 10:59:03 -0500312 }
313
314 private:
315 /**
316 * @brief The number of seconds to wait between starting the
317 * action and doing the power off.
318 */
319 std::chrono::seconds _delay;
320
321 /**
322 * @brief The Timer object used to handle the delay.
323 */
324 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
325};
326
327/**
328 * @class EpowPowerOff
329 *
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600330 * This class is derived from the PowerOffAction class and does the following:
331 * 1) On start, the service mode timer is started. This timer can be
332 * canceled if the cause is no longer satisfied (fans work again).
333 * 2) When this timer expires:
334 * a) The thermal alert D-Bus property is set, this can be used as
335 * an EPOW alert to the host that a power off is imminent.
336 * b) The meltdown timer is started. This timer cannot be canceled,
337 * and on expiration a hard power off occurs.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500338 */
339class EpowPowerOff : public PowerOffAction
340{
341 public:
342 EpowPowerOff() = delete;
343 ~EpowPowerOff() = default;
344 EpowPowerOff(const EpowPowerOff&) = delete;
345 EpowPowerOff& operator=(const EpowPowerOff&) = delete;
346 EpowPowerOff(EpowPowerOff&&) = delete;
347 EpowPowerOff& operator=(EpowPowerOff&&) = delete;
348
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600349 /**
350 * @brief Constructor
351 *
352 * @param[in] serviceModeDelay - The service mode timeout.
353 * @param[in] meltdownDelay - The meltdown delay timeout.
354 * @param[in] powerInterface - The object to use to do the power off
355 * @param[in] func - A function to call right before the power
356 * off occurs (after the delay). May be
357 * empty if no function is necessary.
358 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500359 EpowPowerOff(uint32_t serviceModeDelay, uint32_t meltdownDelay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500360 std::shared_ptr<PowerInterfaceBase> powerInterface,
361 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500362 PowerOffAction("EPOW Power Off: " + std::to_string(serviceModeDelay) +
363 "s/" + std::to_string(meltdownDelay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500364 powerInterface, func),
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600365 _serviceModeDelay(serviceModeDelay), _meltdownDelay(meltdownDelay),
366 _serviceModeTimer(
367 _event,
368 std::bind(std::mem_fn(&EpowPowerOff::serviceModeTimerExpired),
369 this)),
370 _meltdownTimer(
371 _event,
372 std::bind(std::mem_fn(&EpowPowerOff::meltdownTimerExpired), this))
Matt Spinler69b0cf02020-10-14 10:59:03 -0500373 {}
374
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600375 /**
376 * @brief Starts the service mode timer.
377 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500378 void start() override
379 {
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600380 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500381 std::format("Action {}: Starting service mode timer", name()));
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600382
383 _serviceModeTimer.restartOnce(_serviceModeDelay);
Matt Spinler69b0cf02020-10-14 10:59:03 -0500384 }
385
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600386 /**
387 * @brief Called when the service mode timer expires.
388 *
389 * Sets the thermal alert D-Bus property and starts the
390 * meltdown timer.
391 */
392 void serviceModeTimerExpired()
Matt Spinler69b0cf02020-10-14 10:59:03 -0500393 {
Patrick Williamsfbf47032023-07-17 12:27:34 -0500394 getLogger().log(std::format(
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600395 "Action {}: Service mode timer expired, starting meltdown timer",
396 name()));
397
398 _powerIface->thermalAlert(true);
399 _meltdownTimer.restartOnce(_meltdownDelay);
400 }
401
402 /**
403 * @brief Called when the meltdown timer expires.
404 *
405 * Executes a hard power off.
406 */
407 void meltdownTimerExpired()
408 {
Patrick Williamsfbf47032023-07-17 12:27:34 -0500409 getLogger().log(std::format(
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600410 "Action {}: Meltdown timer expired, executing hard power off",
411 name()));
412
413 if (_prePowerOffFunc)
414 {
415 _prePowerOffFunc();
416 }
417
418 _powerIface->hardPowerOff();
Mike Capps683a96c2022-04-27 16:46:06 -0400419 createBmcDump();
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600420 }
421
422 /**
423 * @brief Attempts to cancel the action
424 *
425 * The service mode timer can be canceled. The meltdown
426 * timer cannot.
427 *
428 * @param[in] force - To force the cancel (like if the
429 * system powers off).
430 *
431 * @return bool - If the cancel was successful
432 */
433 bool cancel(bool force) override
434 {
435 if (_serviceModeTimer.isEnabled())
436 {
437 _serviceModeTimer.setEnabled(false);
438 }
439
440 if (_meltdownTimer.isEnabled())
441 {
442 if (force)
443 {
444 _meltdownTimer.setEnabled(false);
445 }
446 else
447 {
448 getLogger().log("Cannot cancel running meltdown timer");
449 return false;
450 }
451 }
Matt Spinler69b0cf02020-10-14 10:59:03 -0500452 return true;
453 }
454
455 private:
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600456 /**
457 * @brief The number of seconds to wait until starting the uncancelable
458 * meltdown timer.
459 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500460 std::chrono::seconds _serviceModeDelay;
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600461
462 /**
463 * @brief The number of seconds to wait after the service mode
464 * timer expires before a hard power off will occur.
465 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500466 std::chrono::seconds _meltdownDelay;
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600467
468 /**
469 * @brief The service mode timer.
470 */
471 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>
472 _serviceModeTimer;
473
474 /**
475 * @brief The meltdown timer.
476 */
477 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _meltdownTimer;
Matt Spinler69b0cf02020-10-14 10:59:03 -0500478};
479} // namespace phosphor::fan::monitor