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      */
54     PowerOffAction(const std::string& name,
55                    std::shared_ptr<PowerInterfaceBase> powerInterface,
56                    PrePowerOffFunc& powerOffFunc) :
57         _name(name),
58         _powerIface(std::move(powerInterface)),
59         _event(sdeventplus::Event::get_default()),
60         _prePowerOffFunc(powerOffFunc)
61     {}
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     /**
88      * @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     /**
99      * @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         }
112         catch (const std::exception& e)
113         {
114             getLogger().log(
115                 std::format("Caught exception while creating BMC dump: {}",
116                             e.what()),
117                 Logger::error);
118         }
119     }
120 
121     /**
122      * @brief The name of the action, which is set by the
123      *        derived class.
124      */
125     const std::string _name;
126 
127     /**
128      * @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;
136 
137     /**
138      * @brief A function that will be called right before
139      *        the power off.
140      */
141     PrePowerOffFunc _prePowerOffFunc;
142 };
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  */
150 class 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
166      * @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.
169      */
170     HardPowerOff(uint32_t delay,
171                  std::shared_ptr<PowerInterfaceBase> powerInterface,
172                  PrePowerOffFunc func) :
173         PowerOffAction("Hard Power Off: " + std::to_string(delay) + "s",
174                        powerInterface, func),
175         _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     {
211         if (_prePowerOffFunc)
212         {
213             _prePowerOffFunc();
214         }
215 
216         getLogger().log(
217             std::format("Action '{}' executing hard power off", name()));
218         _powerIface->hardPowerOff();
219 
220         createBmcDump();
221     }
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  */
242 class 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
258      * @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.
261      */
262     SoftPowerOff(uint32_t delay,
263                  std::shared_ptr<PowerInterfaceBase> powerInterface,
264                  PrePowerOffFunc func) :
265         PowerOffAction("Soft Power Off: " + std::to_string(delay) + "s",
266                        powerInterface, func),
267         _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     {
303         if (_prePowerOffFunc)
304         {
305             _prePowerOffFunc();
306         }
307 
308         getLogger().log(
309             std::format("Action '{}' executing soft power off", name()));
310         _powerIface->softPowerOff();
311 
312         createBmcDump();
313     }
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  *
331  * 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.
339  */
340 class 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 
350     /**
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      */
360     EpowPowerOff(uint32_t serviceModeDelay, uint32_t meltdownDelay,
361                  std::shared_ptr<PowerInterfaceBase> powerInterface,
362                  PrePowerOffFunc func) :
363         PowerOffAction("EPOW Power Off: " + std::to_string(serviceModeDelay) +
364                            "s/" + std::to_string(meltdownDelay) + "s",
365                        powerInterface, func),
366         _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))
374     {}
375 
376     /**
377      * @brief Starts the service mode timer.
378      */
379     void start() override
380     {
381         getLogger().log(
382             std::format("Action {}: Starting service mode timer", name()));
383 
384         _serviceModeTimer.restartOnce(_serviceModeDelay);
385     }
386 
387     /**
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()
394     {
395         getLogger().log(std::format(
396             "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     {
410         getLogger().log(std::format(
411             "Action {}: Meltdown timer expired, executing hard power off",
412             name()));
413 
414         if (_prePowerOffFunc)
415         {
416             _prePowerOffFunc();
417         }
418 
419         _powerIface->hardPowerOff();
420         createBmcDump();
421     }
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         }
453         return true;
454     }
455 
456   private:
457     /**
458      * @brief The number of seconds to wait until starting the uncancelable
459      *        meltdown timer.
460      */
461     std::chrono::seconds _serviceModeDelay;
462 
463     /**
464      * @brief The number of seconds to wait after the service mode
465      *        timer expires before a hard power off will occur.
466      */
467     std::chrono::seconds _meltdownDelay;
468 
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;
479 };
480 } // namespace phosphor::fan::monitor
481