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