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