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