1 #pragma once
2 
3 #include "logging.hpp"
4 #include "power_interface.hpp"
5 #include "sdbusplus.hpp"
6 
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 
15 namespace 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  */
34 class PowerOffAction
35 {
36   public:
37     using PrePowerOffFunc = std::function<void()>;
38 
39     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.
50      * @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.
54      */
55     PowerOffAction(const std::string& name,
56                    std::shared_ptr<PowerInterfaceBase> powerInterface,
57                    PrePowerOffFunc& powerOffFunc) :
58         _name(name),
59         _powerIface(std::move(powerInterface)),
60         _event(sdeventplus::Event::get_default()),
61         _prePowerOffFunc(powerOffFunc)
62     {}
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     /**
89      * @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     /**
100      * @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         }
113         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         }
120     }
121 
122     /**
123      * @brief The name of the action, which is set by the
124      *        derived class.
125      */
126     const std::string _name;
127 
128     /**
129      * @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;
137 
138     /**
139      * @brief A function that will be called right before
140      *        the power off.
141      */
142     PrePowerOffFunc _prePowerOffFunc;
143 };
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  */
151 class 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
167      * @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.
170      */
171     HardPowerOff(uint32_t delay,
172                  std::shared_ptr<PowerInterfaceBase> powerInterface,
173                  PrePowerOffFunc func) :
174         PowerOffAction("Hard Power Off: " + std::to_string(delay) + "s",
175                        powerInterface, func),
176         _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     {
212         if (_prePowerOffFunc)
213         {
214             _prePowerOffFunc();
215         }
216 
217         getLogger().log(
218             fmt::format("Action '{}' executing hard power off", name()));
219         _powerIface->hardPowerOff();
220 
221         createBmcDump();
222     }
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  */
243 class 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
259      * @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.
262      */
263     SoftPowerOff(uint32_t delay,
264                  std::shared_ptr<PowerInterfaceBase> powerInterface,
265                  PrePowerOffFunc func) :
266         PowerOffAction("Soft Power Off: " + std::to_string(delay) + "s",
267                        powerInterface, func),
268         _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     {
304         if (_prePowerOffFunc)
305         {
306             _prePowerOffFunc();
307         }
308 
309         getLogger().log(
310             fmt::format("Action '{}' executing soft power off", name()));
311         _powerIface->softPowerOff();
312 
313         createBmcDump();
314     }
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  *
332  * 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.
340  */
341 class 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 
351     /**
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      */
361     EpowPowerOff(uint32_t serviceModeDelay, uint32_t meltdownDelay,
362                  std::shared_ptr<PowerInterfaceBase> powerInterface,
363                  PrePowerOffFunc func) :
364         PowerOffAction("EPOW Power Off: " + std::to_string(serviceModeDelay) +
365                            "s/" + std::to_string(meltdownDelay) + "s",
366                        powerInterface, func),
367         _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))
375     {}
376 
377     /**
378      * @brief Starts the service mode timer.
379      */
380     void start() override
381     {
382         getLogger().log(
383             fmt::format("Action {}: Starting service mode timer", name()));
384 
385         _serviceModeTimer.restartOnce(_serviceModeDelay);
386     }
387 
388     /**
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()
395     {
396         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();
421         createBmcDump();
422     }
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         }
454         return true;
455     }
456 
457   private:
458     /**
459      * @brief The number of seconds to wait until starting the uncancelable
460      *        meltdown timer.
461      */
462     std::chrono::seconds _serviceModeDelay;
463 
464     /**
465      * @brief The number of seconds to wait after the service mode
466      *        timer expires before a hard power off will occur.
467      */
468     std::chrono::seconds _meltdownDelay;
469 
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;
480 };
481 } // namespace phosphor::fan::monitor
482