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