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