blob: c833038a972bb35f2d381d4da9a1a505e19f9c57 [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 }
Mike Capps477b13b2022-07-11 10:45:46 -0400113 catch (const std::exception& e)
114 {
115 getLogger().log(
116 fmt::format("Caught exception while creating BMC dump: {}",
117 e.what()),
118 Logger::error);
119 }
Mike Capps683a96c2022-04-27 16:46:06 -0400120 }
121
122 /**
Matt Spinler69b0cf02020-10-14 10:59:03 -0500123 * @brief The name of the action, which is set by the
124 * derived class.
125 */
126 const std::string _name;
127
128 /**
Matt Spinler69b0cf02020-10-14 10:59:03 -0500129 * @brief The object used to invoke the power off with.
130 */
131 std::shared_ptr<PowerInterfaceBase> _powerIface;
132
133 /**
134 * @brief The event loop object. Needed by timers.
135 */
136 sdeventplus::Event _event;
Matt Spinlerac1efc12020-10-27 10:20:11 -0500137
138 /**
139 * @brief A function that will be called right before
140 * the power off.
141 */
142 PrePowerOffFunc _prePowerOffFunc;
Matt Spinler69b0cf02020-10-14 10:59:03 -0500143};
144
145/**
146 * @class HardPowerOff
147 *
148 * This class is derived from the PowerOffAction class
149 * and will execute a hard power off after some delay.
150 */
151class HardPowerOff : public PowerOffAction
152{
153 public:
154 HardPowerOff() = delete;
155 ~HardPowerOff() = default;
156 HardPowerOff(const HardPowerOff&) = delete;
157 HardPowerOff& operator=(const HardPowerOff&) = delete;
158 HardPowerOff(HardPowerOff&&) = delete;
159 HardPowerOff& operator=(HardPowerOff&&) = delete;
160
161 /**
162 * @brief Constructor
163 *
164 * @param[in] delay - The amount of time in seconds to wait before
165 * doing the power off
166 * @param[in] powerInterface - The object to use to do the power off
Matt Spinlerac1efc12020-10-27 10:20:11 -0500167 * @param[in] func - A function to call right before the power
168 * off occurs (after the delay). May be
169 * empty if no function is necessary.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500170 */
171 HardPowerOff(uint32_t delay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500172 std::shared_ptr<PowerInterfaceBase> powerInterface,
173 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500174 PowerOffAction("Hard Power Off: " + std::to_string(delay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500175 powerInterface, func),
Matt Spinler69b0cf02020-10-14 10:59:03 -0500176 _delay(delay),
177 _timer(_event, std::bind(std::mem_fn(&HardPowerOff::powerOff), this))
178 {}
179
180 /**
181 * @brief Starts a timer upon the expiration of which the
182 * hard power off will be done.
183 */
184 void start() override
185 {
186 _timer.restartOnce(_delay);
187 }
188
189 /**
190 * @brief Cancels the timer. This is always allowed.
191 *
192 * @param[in] force - If the cancel should be forced or not
193 * (not checked in this case)
194 * @return bool - Always returns true
195 */
196 bool cancel(bool) override
197 {
198 if (_timer.isEnabled())
199 {
200 _timer.setEnabled(false);
201 }
202
203 // Can always be canceled
204 return true;
205 }
206
207 /**
208 * @brief Performs the hard power off.
209 */
210 void powerOff()
211 {
Matt Spinlerac1efc12020-10-27 10:20:11 -0500212 if (_prePowerOffFunc)
213 {
214 _prePowerOffFunc();
215 }
216
Matt Spinler69b0cf02020-10-14 10:59:03 -0500217 getLogger().log(
218 fmt::format("Action '{}' executing hard power off", name()));
219 _powerIface->hardPowerOff();
Mike Capps683a96c2022-04-27 16:46:06 -0400220
221 createBmcDump();
Matt Spinler69b0cf02020-10-14 10:59:03 -0500222 }
223
224 private:
225 /**
226 * @brief The number of seconds to wait between starting the
227 * action and doing the power off.
228 */
229 std::chrono::seconds _delay;
230
231 /**
232 * @brief The Timer object used to handle the delay.
233 */
234 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
235};
236
237/**
238 * @class SoftPowerOff
239 *
240 * This class is derived from the PowerOffAction class
241 * and will execute a soft power off after some delay.
242 */
243class SoftPowerOff : public PowerOffAction
244{
245 public:
246 SoftPowerOff() = delete;
247 ~SoftPowerOff() = default;
248 SoftPowerOff(const SoftPowerOff&) = delete;
249 SoftPowerOff& operator=(const SoftPowerOff&) = delete;
250 SoftPowerOff(SoftPowerOff&&) = delete;
251 SoftPowerOff& operator=(SoftPowerOff&&) = delete;
252
253 /**
254 * @brief Constructor
255 *
256 * @param[in] delay - The amount of time in seconds to wait before
257 * doing the power off
258 * @param[in] powerInterface - The object to use to do the power off
Matt Spinlerac1efc12020-10-27 10:20:11 -0500259 * @param[in] func - A function to call right before the power
260 * off occurs (after the delay). May be
261 * empty if no function is necessary.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500262 */
263 SoftPowerOff(uint32_t delay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500264 std::shared_ptr<PowerInterfaceBase> powerInterface,
265 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500266 PowerOffAction("Soft Power Off: " + std::to_string(delay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500267 powerInterface, func),
Matt Spinler69b0cf02020-10-14 10:59:03 -0500268 _delay(delay),
269 _timer(_event, std::bind(std::mem_fn(&SoftPowerOff::powerOff), this))
270 {}
271
272 /**
273 * @brief Starts a timer upon the expiration of which the
274 * soft power off will be done.
275 */
276 void start() override
277 {
278 _timer.restartOnce(_delay);
279 }
280
281 /**
282 * @brief Cancels the timer. This is always allowed.
283 *
284 * @param[in] force - If the cancel should be forced or not
285 * (not checked in this case)
286 * @return bool - Always returns true
287 */
288 bool cancel(bool) override
289 {
290 if (_timer.isEnabled())
291 {
292 _timer.setEnabled(false);
293 }
294
295 // Can always be canceled
296 return true;
297 }
298
299 /**
300 * @brief Performs the soft power off.
301 */
302 void powerOff()
303 {
Matt Spinlerac1efc12020-10-27 10:20:11 -0500304 if (_prePowerOffFunc)
305 {
306 _prePowerOffFunc();
307 }
308
Matt Spinler69b0cf02020-10-14 10:59:03 -0500309 getLogger().log(
310 fmt::format("Action '{}' executing soft power off", name()));
311 _powerIface->softPowerOff();
Mike Capps683a96c2022-04-27 16:46:06 -0400312
313 createBmcDump();
Matt Spinler69b0cf02020-10-14 10:59:03 -0500314 }
315
316 private:
317 /**
318 * @brief The number of seconds to wait between starting the
319 * action and doing the power off.
320 */
321 std::chrono::seconds _delay;
322
323 /**
324 * @brief The Timer object used to handle the delay.
325 */
326 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
327};
328
329/**
330 * @class EpowPowerOff
331 *
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600332 * This class is derived from the PowerOffAction class and does the following:
333 * 1) On start, the service mode timer is started. This timer can be
334 * canceled if the cause is no longer satisfied (fans work again).
335 * 2) When this timer expires:
336 * a) The thermal alert D-Bus property is set, this can be used as
337 * an EPOW alert to the host that a power off is imminent.
338 * b) The meltdown timer is started. This timer cannot be canceled,
339 * and on expiration a hard power off occurs.
Matt Spinler69b0cf02020-10-14 10:59:03 -0500340 */
341class EpowPowerOff : public PowerOffAction
342{
343 public:
344 EpowPowerOff() = delete;
345 ~EpowPowerOff() = default;
346 EpowPowerOff(const EpowPowerOff&) = delete;
347 EpowPowerOff& operator=(const EpowPowerOff&) = delete;
348 EpowPowerOff(EpowPowerOff&&) = delete;
349 EpowPowerOff& operator=(EpowPowerOff&&) = delete;
350
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600351 /**
352 * @brief Constructor
353 *
354 * @param[in] serviceModeDelay - The service mode timeout.
355 * @param[in] meltdownDelay - The meltdown delay timeout.
356 * @param[in] powerInterface - The object to use to do the power off
357 * @param[in] func - A function to call right before the power
358 * off occurs (after the delay). May be
359 * empty if no function is necessary.
360 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500361 EpowPowerOff(uint32_t serviceModeDelay, uint32_t meltdownDelay,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500362 std::shared_ptr<PowerInterfaceBase> powerInterface,
363 PrePowerOffFunc func) :
Matt Spinler69b0cf02020-10-14 10:59:03 -0500364 PowerOffAction("EPOW Power Off: " + std::to_string(serviceModeDelay) +
365 "s/" + std::to_string(meltdownDelay) + "s",
Matt Spinlerac1efc12020-10-27 10:20:11 -0500366 powerInterface, func),
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600367 _serviceModeDelay(serviceModeDelay), _meltdownDelay(meltdownDelay),
368 _serviceModeTimer(
369 _event,
370 std::bind(std::mem_fn(&EpowPowerOff::serviceModeTimerExpired),
371 this)),
372 _meltdownTimer(
373 _event,
374 std::bind(std::mem_fn(&EpowPowerOff::meltdownTimerExpired), this))
Matt Spinler69b0cf02020-10-14 10:59:03 -0500375 {}
376
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600377 /**
378 * @brief Starts the service mode timer.
379 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500380 void start() override
381 {
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600382 getLogger().log(
383 fmt::format("Action {}: Starting service mode timer", name()));
384
385 _serviceModeTimer.restartOnce(_serviceModeDelay);
Matt Spinler69b0cf02020-10-14 10:59:03 -0500386 }
387
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600388 /**
389 * @brief Called when the service mode timer expires.
390 *
391 * Sets the thermal alert D-Bus property and starts the
392 * meltdown timer.
393 */
394 void serviceModeTimerExpired()
Matt Spinler69b0cf02020-10-14 10:59:03 -0500395 {
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600396 getLogger().log(fmt::format(
397 "Action {}: Service mode timer expired, starting meltdown timer",
398 name()));
399
400 _powerIface->thermalAlert(true);
401 _meltdownTimer.restartOnce(_meltdownDelay);
402 }
403
404 /**
405 * @brief Called when the meltdown timer expires.
406 *
407 * Executes a hard power off.
408 */
409 void meltdownTimerExpired()
410 {
411 getLogger().log(fmt::format(
412 "Action {}: Meltdown timer expired, executing hard power off",
413 name()));
414
415 if (_prePowerOffFunc)
416 {
417 _prePowerOffFunc();
418 }
419
420 _powerIface->hardPowerOff();
Mike Capps683a96c2022-04-27 16:46:06 -0400421 createBmcDump();
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600422 }
423
424 /**
425 * @brief Attempts to cancel the action
426 *
427 * The service mode timer can be canceled. The meltdown
428 * timer cannot.
429 *
430 * @param[in] force - To force the cancel (like if the
431 * system powers off).
432 *
433 * @return bool - If the cancel was successful
434 */
435 bool cancel(bool force) override
436 {
437 if (_serviceModeTimer.isEnabled())
438 {
439 _serviceModeTimer.setEnabled(false);
440 }
441
442 if (_meltdownTimer.isEnabled())
443 {
444 if (force)
445 {
446 _meltdownTimer.setEnabled(false);
447 }
448 else
449 {
450 getLogger().log("Cannot cancel running meltdown timer");
451 return false;
452 }
453 }
Matt Spinler69b0cf02020-10-14 10:59:03 -0500454 return true;
455 }
456
457 private:
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600458 /**
459 * @brief The number of seconds to wait until starting the uncancelable
460 * meltdown timer.
461 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500462 std::chrono::seconds _serviceModeDelay;
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600463
464 /**
465 * @brief The number of seconds to wait after the service mode
466 * timer expires before a hard power off will occur.
467 */
Matt Spinler69b0cf02020-10-14 10:59:03 -0500468 std::chrono::seconds _meltdownDelay;
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600469
470 /**
471 * @brief The service mode timer.
472 */
473 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>
474 _serviceModeTimer;
475
476 /**
477 * @brief The meltdown timer.
478 */
479 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _meltdownTimer;
Matt Spinler69b0cf02020-10-14 10:59:03 -0500480};
481} // namespace phosphor::fan::monitor