1 #pragma once
2 
3 #include "logging.hpp"
4 #include "power_interface.hpp"
5 #include "sdbusplus.hpp"
6 
7 #include <sdeventplus/clock.hpp>
8 #include <sdeventplus/event.hpp>
9 #include <sdeventplus/utility/timer.hpp>
10 
11 #include <chrono>
12 #include <format>
13 
14 namespace 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  */
33 class PowerOffAction
34 {
35   public:
36     using PrePowerOffFunc = std::function<void()>;
37 
38     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.
49      * @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.
53      */
PowerOffAction(const std::string & name,std::shared_ptr<PowerInterfaceBase> powerInterface,PrePowerOffFunc & powerOffFunc)54     PowerOffAction(const std::string& name,
55                    std::shared_ptr<PowerInterfaceBase> powerInterface,
56                    PrePowerOffFunc& powerOffFunc) :
57         _name(name), _powerIface(std::move(powerInterface)),
58         _event(sdeventplus::Event::get_default()),
59         _prePowerOffFunc(powerOffFunc)
60     {}
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     /**
87      * @brief Returns the name of the action
88      *
89      * @return const std::string& - The name
90      */
name() const91     const std::string& name() const
92     {
93         return _name;
94     }
95 
96   protected:
97     /**
98      * @brief Create a BMC Dump
99      */
createBmcDump() const100     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         }
111         catch (const std::exception& e)
112         {
113             getLogger().log(
114                 std::format("Caught exception while creating BMC dump: {}",
115                             e.what()),
116                 Logger::error);
117         }
118     }
119 
120     /**
121      * @brief The name of the action, which is set by the
122      *        derived class.
123      */
124     const std::string _name;
125 
126     /**
127      * @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;
135 
136     /**
137      * @brief A function that will be called right before
138      *        the power off.
139      */
140     PrePowerOffFunc _prePowerOffFunc;
141 };
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  */
149 class 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
165      * @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.
168      */
HardPowerOff(uint32_t delay,std::shared_ptr<PowerInterfaceBase> powerInterface,PrePowerOffFunc func)169     HardPowerOff(uint32_t delay,
170                  std::shared_ptr<PowerInterfaceBase> powerInterface,
171                  PrePowerOffFunc func) :
172         PowerOffAction("Hard Power Off: " + std::to_string(delay) + "s",
173                        powerInterface, func),
174         _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      */
start()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      */
cancel(bool)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      */
powerOff()208     void powerOff()
209     {
210         if (_prePowerOffFunc)
211         {
212             _prePowerOffFunc();
213         }
214 
215         getLogger().log(
216             std::format("Action '{}' executing hard power off", name()));
217         _powerIface->hardPowerOff();
218 
219         createBmcDump();
220     }
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  */
241 class 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
257      * @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.
260      */
SoftPowerOff(uint32_t delay,std::shared_ptr<PowerInterfaceBase> powerInterface,PrePowerOffFunc func)261     SoftPowerOff(uint32_t delay,
262                  std::shared_ptr<PowerInterfaceBase> powerInterface,
263                  PrePowerOffFunc func) :
264         PowerOffAction("Soft Power Off: " + std::to_string(delay) + "s",
265                        powerInterface, func),
266         _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      */
start()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      */
cancel(bool)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      */
powerOff()300     void powerOff()
301     {
302         if (_prePowerOffFunc)
303         {
304             _prePowerOffFunc();
305         }
306 
307         getLogger().log(
308             std::format("Action '{}' executing soft power off", name()));
309         _powerIface->softPowerOff();
310 
311         createBmcDump();
312     }
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  *
330  * 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.
338  */
339 class 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 
349     /**
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      */
EpowPowerOff(uint32_t serviceModeDelay,uint32_t meltdownDelay,std::shared_ptr<PowerInterfaceBase> powerInterface,PrePowerOffFunc func)359     EpowPowerOff(uint32_t serviceModeDelay, uint32_t meltdownDelay,
360                  std::shared_ptr<PowerInterfaceBase> powerInterface,
361                  PrePowerOffFunc func) :
362         PowerOffAction("EPOW Power Off: " + std::to_string(serviceModeDelay) +
363                            "s/" + std::to_string(meltdownDelay) + "s",
364                        powerInterface, func),
365         _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))
373     {}
374 
375     /**
376      * @brief Starts the service mode timer.
377      */
start()378     void start() override
379     {
380         getLogger().log(
381             std::format("Action {}: Starting service mode timer", name()));
382 
383         _serviceModeTimer.restartOnce(_serviceModeDelay);
384     }
385 
386     /**
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      */
serviceModeTimerExpired()392     void serviceModeTimerExpired()
393     {
394         getLogger().log(std::format(
395             "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      */
meltdownTimerExpired()407     void meltdownTimerExpired()
408     {
409         getLogger().log(std::format(
410             "Action {}: Meltdown timer expired, executing hard power off",
411             name()));
412 
413         if (_prePowerOffFunc)
414         {
415             _prePowerOffFunc();
416         }
417 
418         _powerIface->hardPowerOff();
419         createBmcDump();
420     }
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      */
cancel(bool force)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         }
452         return true;
453     }
454 
455   private:
456     /**
457      * @brief The number of seconds to wait until starting the uncancelable
458      *        meltdown timer.
459      */
460     std::chrono::seconds _serviceModeDelay;
461 
462     /**
463      * @brief The number of seconds to wait after the service mode
464      *        timer expires before a hard power off will occur.
465      */
466     std::chrono::seconds _meltdownDelay;
467 
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;
478 };
479 } // namespace phosphor::fan::monitor
480