blob: 348173b4c19dd30e5c24a1090c882d082fac10de [file] [log] [blame]
#pragma once
#include "logging.hpp"
#include "power_interface.hpp"
#include "sdbusplus.hpp"
#include <sdeventplus/clock.hpp>
#include <sdeventplus/event.hpp>
#include <sdeventplus/utility/timer.hpp>
#include <chrono>
#include <format>
namespace phosphor::fan::monitor
{
/**
* @class PowerOffAction
*
* This is the base class for a power off action, which is
* used by the PowerOffRule class to do different types of
* power offs based on fan failures.
*
* The power off is started with the start() method, and the
* derived class may or may not allow it to be stopped with
* the cancel() method, which is really only useful when
* there is a delay before the power off.
*
* It uses the PowerInterfaceBase object pointer to perform
* the D-Bus call to do the power off, so it can be mocked
* for testing.
*/
class PowerOffAction
{
public:
using PrePowerOffFunc = std::function<void()>;
PowerOffAction() = delete;
virtual ~PowerOffAction() = default;
PowerOffAction(const PowerOffAction&) = delete;
PowerOffAction& operator=(const PowerOffAction&) = delete;
PowerOffAction(PowerOffAction&&) = delete;
PowerOffAction& operator=(PowerOffAction&&) = delete;
/**
* @brief Constructor
*
* @param[in] name - The action name. Used for tracing.
* @param[in] powerInterface - The object used to invoke the power off.
* @param[in] powerOffFunc - A function to call right before the power
* off occurs (after any delays). May be
* empty if no function is necessary.
*/
PowerOffAction(const std::string& name,
std::shared_ptr<PowerInterfaceBase> powerInterface,
PrePowerOffFunc& powerOffFunc) :
_name(name), _powerIface(std::move(powerInterface)),
_event(sdeventplus::Event::get_default()),
_prePowerOffFunc(powerOffFunc)
{}
/**
* @brief Starts the power off.
*
* Though this occurs in the child class, usually this
* involves starting a timer and then powering off when it
* times out.
*/
virtual void start() = 0;
/**
* @brief Attempts to cancel the power off, if the derived
* class allows it, and assuming the power off hasn't
* already happened.
*
* The 'force' parameter is mainly for use when something else
* powered off the system so this action doesn't need to run
* anymore even if it isn't usually cancelable.
*
* @param[in] force - If the cancel should be forced
*
* @return bool - If the cancel was allowed/successful
*/
virtual bool cancel(bool force) = 0;
/**
* @brief Returns the name of the action
*
* @return const std::string& - The name
*/
const std::string& name() const
{
return _name;
}
protected:
/**
* @brief Create a BMC Dump
*/
void createBmcDump() const
{
try
{
util::SDBusPlus::callMethod(
"xyz.openbmc_project.Dump.Manager",
"/xyz/openbmc_project/dump/bmc",
"xyz.openbmc_project.Dump.Create", "CreateDump",
std::vector<std::pair<std::string,
std::variant<std::string, uint64_t>>>());
}
catch (const std::exception& e)
{
getLogger().log(
std::format("Caught exception while creating BMC dump: {}",
e.what()),
Logger::error);
}
}
/**
* @brief The name of the action, which is set by the
* derived class.
*/
const std::string _name;
/**
* @brief The object used to invoke the power off with.
*/
std::shared_ptr<PowerInterfaceBase> _powerIface;
/**
* @brief The event loop object. Needed by timers.
*/
sdeventplus::Event _event;
/**
* @brief A function that will be called right before
* the power off.
*/
PrePowerOffFunc _prePowerOffFunc;
};
/**
* @class HardPowerOff
*
* This class is derived from the PowerOffAction class
* and will execute a hard power off after some delay.
*/
class HardPowerOff : public PowerOffAction
{
public:
HardPowerOff() = delete;
~HardPowerOff() = default;
HardPowerOff(const HardPowerOff&) = delete;
HardPowerOff& operator=(const HardPowerOff&) = delete;
HardPowerOff(HardPowerOff&&) = delete;
HardPowerOff& operator=(HardPowerOff&&) = delete;
/**
* @brief Constructor
*
* @param[in] delay - The amount of time in seconds to wait before
* doing the power off
* @param[in] powerInterface - The object to use to do the power off
* @param[in] func - A function to call right before the power
* off occurs (after the delay). May be
* empty if no function is necessary.
*/
HardPowerOff(uint32_t delay,
std::shared_ptr<PowerInterfaceBase> powerInterface,
PrePowerOffFunc func) :
PowerOffAction("Hard Power Off: " + std::to_string(delay) + "s",
powerInterface, func),
_delay(delay),
_timer(_event, std::bind(std::mem_fn(&HardPowerOff::powerOff), this))
{}
/**
* @brief Starts a timer upon the expiration of which the
* hard power off will be done.
*/
void start() override
{
_timer.restartOnce(_delay);
}
/**
* @brief Cancels the timer. This is always allowed.
*
* @param[in] force - If the cancel should be forced or not
* (not checked in this case)
* @return bool - Always returns true
*/
bool cancel(bool) override
{
if (_timer.isEnabled())
{
_timer.setEnabled(false);
}
// Can always be canceled
return true;
}
/**
* @brief Performs the hard power off.
*/
void powerOff()
{
if (_prePowerOffFunc)
{
_prePowerOffFunc();
}
getLogger().log(
std::format("Action '{}' executing hard power off", name()));
_powerIface->hardPowerOff();
createBmcDump();
}
private:
/**
* @brief The number of seconds to wait between starting the
* action and doing the power off.
*/
std::chrono::seconds _delay;
/**
* @brief The Timer object used to handle the delay.
*/
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
};
/**
* @class SoftPowerOff
*
* This class is derived from the PowerOffAction class
* and will execute a soft power off after some delay.
*/
class SoftPowerOff : public PowerOffAction
{
public:
SoftPowerOff() = delete;
~SoftPowerOff() = default;
SoftPowerOff(const SoftPowerOff&) = delete;
SoftPowerOff& operator=(const SoftPowerOff&) = delete;
SoftPowerOff(SoftPowerOff&&) = delete;
SoftPowerOff& operator=(SoftPowerOff&&) = delete;
/**
* @brief Constructor
*
* @param[in] delay - The amount of time in seconds to wait before
* doing the power off
* @param[in] powerInterface - The object to use to do the power off
* @param[in] func - A function to call right before the power
* off occurs (after the delay). May be
* empty if no function is necessary.
*/
SoftPowerOff(uint32_t delay,
std::shared_ptr<PowerInterfaceBase> powerInterface,
PrePowerOffFunc func) :
PowerOffAction("Soft Power Off: " + std::to_string(delay) + "s",
powerInterface, func),
_delay(delay),
_timer(_event, std::bind(std::mem_fn(&SoftPowerOff::powerOff), this))
{}
/**
* @brief Starts a timer upon the expiration of which the
* soft power off will be done.
*/
void start() override
{
_timer.restartOnce(_delay);
}
/**
* @brief Cancels the timer. This is always allowed.
*
* @param[in] force - If the cancel should be forced or not
* (not checked in this case)
* @return bool - Always returns true
*/
bool cancel(bool) override
{
if (_timer.isEnabled())
{
_timer.setEnabled(false);
}
// Can always be canceled
return true;
}
/**
* @brief Performs the soft power off.
*/
void powerOff()
{
if (_prePowerOffFunc)
{
_prePowerOffFunc();
}
getLogger().log(
std::format("Action '{}' executing soft power off", name()));
_powerIface->softPowerOff();
createBmcDump();
}
private:
/**
* @brief The number of seconds to wait between starting the
* action and doing the power off.
*/
std::chrono::seconds _delay;
/**
* @brief The Timer object used to handle the delay.
*/
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
};
/**
* @class EpowPowerOff
*
* This class is derived from the PowerOffAction class and does the following:
* 1) On start, the service mode timer is started. This timer can be
* canceled if the cause is no longer satisfied (fans work again).
* 2) When this timer expires:
* a) The thermal alert D-Bus property is set, this can be used as
* an EPOW alert to the host that a power off is imminent.
* b) The meltdown timer is started. This timer cannot be canceled,
* and on expiration a hard power off occurs.
*/
class EpowPowerOff : public PowerOffAction
{
public:
EpowPowerOff() = delete;
~EpowPowerOff() = default;
EpowPowerOff(const EpowPowerOff&) = delete;
EpowPowerOff& operator=(const EpowPowerOff&) = delete;
EpowPowerOff(EpowPowerOff&&) = delete;
EpowPowerOff& operator=(EpowPowerOff&&) = delete;
/**
* @brief Constructor
*
* @param[in] serviceModeDelay - The service mode timeout.
* @param[in] meltdownDelay - The meltdown delay timeout.
* @param[in] powerInterface - The object to use to do the power off
* @param[in] func - A function to call right before the power
* off occurs (after the delay). May be
* empty if no function is necessary.
*/
EpowPowerOff(uint32_t serviceModeDelay, uint32_t meltdownDelay,
std::shared_ptr<PowerInterfaceBase> powerInterface,
PrePowerOffFunc func) :
PowerOffAction("EPOW Power Off: " + std::to_string(serviceModeDelay) +
"s/" + std::to_string(meltdownDelay) + "s",
powerInterface, func),
_serviceModeDelay(serviceModeDelay), _meltdownDelay(meltdownDelay),
_serviceModeTimer(
_event,
std::bind(std::mem_fn(&EpowPowerOff::serviceModeTimerExpired),
this)),
_meltdownTimer(
_event,
std::bind(std::mem_fn(&EpowPowerOff::meltdownTimerExpired), this))
{}
/**
* @brief Starts the service mode timer.
*/
void start() override
{
getLogger().log(
std::format("Action {}: Starting service mode timer", name()));
_serviceModeTimer.restartOnce(_serviceModeDelay);
}
/**
* @brief Called when the service mode timer expires.
*
* Sets the thermal alert D-Bus property and starts the
* meltdown timer.
*/
void serviceModeTimerExpired()
{
getLogger().log(std::format(
"Action {}: Service mode timer expired, starting meltdown timer",
name()));
_powerIface->thermalAlert(true);
_meltdownTimer.restartOnce(_meltdownDelay);
}
/**
* @brief Called when the meltdown timer expires.
*
* Executes a hard power off.
*/
void meltdownTimerExpired()
{
getLogger().log(std::format(
"Action {}: Meltdown timer expired, executing hard power off",
name()));
if (_prePowerOffFunc)
{
_prePowerOffFunc();
}
_powerIface->hardPowerOff();
createBmcDump();
}
/**
* @brief Attempts to cancel the action
*
* The service mode timer can be canceled. The meltdown
* timer cannot.
*
* @param[in] force - To force the cancel (like if the
* system powers off).
*
* @return bool - If the cancel was successful
*/
bool cancel(bool force) override
{
if (_serviceModeTimer.isEnabled())
{
_serviceModeTimer.setEnabled(false);
}
if (_meltdownTimer.isEnabled())
{
if (force)
{
_meltdownTimer.setEnabled(false);
}
else
{
getLogger().log("Cannot cancel running meltdown timer");
return false;
}
}
return true;
}
private:
/**
* @brief The number of seconds to wait until starting the uncancelable
* meltdown timer.
*/
std::chrono::seconds _serviceModeDelay;
/**
* @brief The number of seconds to wait after the service mode
* timer expires before a hard power off will occur.
*/
std::chrono::seconds _meltdownDelay;
/**
* @brief The service mode timer.
*/
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>
_serviceModeTimer;
/**
* @brief The meltdown timer.
*/
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _meltdownTimer;
};
} // namespace phosphor::fan::monitor