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