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