diff options
-rw-r--r-- | chassis_state_manager.cpp | 92 | ||||
-rw-r--r-- | chassis_state_manager.hpp | 31 | ||||
-rw-r--r-- | configure.ac | 6 |
3 files changed, 129 insertions, 0 deletions
diff --git a/chassis_state_manager.cpp b/chassis_state_manager.cpp index a723d8f..a99aec9 100644 --- a/chassis_state_manager.cpp +++ b/chassis_state_manager.cpp @@ -95,6 +95,22 @@ void Chassis::determineInitialState() server::Chassis::requestedPowerTransition(Transition::On); return; } + else + { + // The system is off. If we think it should be on then + // we probably lost AC while up, so set a new state + // change time. + uint64_t lastTime; + PowerState lastState; + + if (deserializeStateChangeTime(lastTime, lastState)) + { + if (lastState == PowerState::On) + { + setStateChangeTime(); + } + } + } } catch (const SdBusError& e) { @@ -220,6 +236,7 @@ int Chassis::sysStateChange(sdbusplus::message::message& msg) { log<level::INFO>("Received signal that power OFF is complete"); this->currentPowerState(server::Chassis::PowerState::Off); + this->setStateChangeTime(); } else if ((newStateUnit == CHASSIS_STATE_POWERON_TGT) && (newStateResult == "done") && @@ -227,6 +244,7 @@ int Chassis::sysStateChange(sdbusplus::message::message& msg) { log<level::INFO>("Received signal that power ON is complete"); this->currentPowerState(server::Chassis::PowerState::On); + this->setStateChangeTime(); } return 0; @@ -367,6 +385,80 @@ void Chassis::startPOHCounter() } } +void Chassis::serializeStateChangeTime() +{ + fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH}; + std::ofstream os(path.c_str(), std::ios::binary); + cereal::JSONOutputArchive oarchive(os); + + oarchive(ChassisInherit::lastStateChangeTime(), + ChassisInherit::currentPowerState()); +} + +bool Chassis::deserializeStateChangeTime(uint64_t& time, PowerState& state) +{ + fs::path path{CHASSIS_STATE_CHANGE_PERSIST_PATH}; + + try + { + if (fs::exists(path)) + { + std::ifstream is(path.c_str(), std::ios::in | std::ios::binary); + cereal::JSONInputArchive iarchive(is); + iarchive(time, state); + return true; + } + } + catch (std::exception& e) + { + log<level::ERR>(e.what()); + fs::remove(path); + } + + return false; +} + +void Chassis::restoreChassisStateChangeTime() +{ + uint64_t time; + PowerState state; + + if (!deserializeStateChangeTime(time, state)) + { + ChassisInherit::lastStateChangeTime(0); + } + else + { + ChassisInherit::lastStateChangeTime(time); + } +} + +void Chassis::setStateChangeTime() +{ + using namespace std::chrono; + uint64_t lastTime; + PowerState lastState; + + auto now = + duration_cast<milliseconds>(system_clock::now().time_since_epoch()) + .count(); + + // If power is on when the BMC is rebooted, this function will get called + // because sysStateChange() runs. Since the power state didn't change + // in this case, neither should the state change time, so check that + // the power state actually did change here. + if (deserializeStateChangeTime(lastTime, lastState)) + { + if (lastState == ChassisInherit::currentPowerState()) + { + return; + } + } + + ChassisInherit::lastStateChangeTime(now); + serializeStateChangeTime(); +} + } // namespace manager } // namespace state } // namepsace phosphor diff --git a/chassis_state_manager.hpp b/chassis_state_manager.hpp index 5532949..9a6b647 100644 --- a/chassis_state_manager.hpp +++ b/chassis_state_manager.hpp @@ -61,6 +61,8 @@ class Chassis : public ChassisInherit { subscribeToSystemdSignals(); + restoreChassisStateChangeTime(); + determineInitialState(); restorePOHCounter(); // restore POHCounter from persisted file @@ -157,6 +159,35 @@ class Chassis : public ChassisInherit */ bool deserializePOH(const fs::path& path, uint32_t& retCounter); + /** @brief Sets the LastStateChangeTime property and persists it. */ + void setStateChangeTime(); + + /** @brief Serialize the last power state change time. + * + * Save the time the state changed and the state itself. + * The state needs to be saved as well so that during rediscovery + * on reboots there's a way to know not to update the time again. + */ + void serializeStateChangeTime(); + + /** @brief Deserialize the last power state change time. + * + * @param[out] time - Deserialized time + * @param[out] state - Deserialized power state + * + * @return bool - true if successful, false otherwise. + */ + bool deserializeStateChangeTime(uint64_t& time, PowerState& state); + + /** @brief Restores the power state change time. + * + * The time is loaded into the LastStateChangeTime D-Bus property. + * On the very first start after this code has been applied but + * before the state has changed, the LastStateChangeTime value + * will be zero. + */ + void restoreChassisStateChangeTime(); + /** @brief Timer */ std::unique_ptr<phosphor::state::manager::Timer> timer; }; diff --git a/configure.ac b/configure.ac index 838aaf2..f1d3a2f 100644 --- a/configure.ac +++ b/configure.ac @@ -66,6 +66,12 @@ AS_IF([test "x$POH_COUNTER_PERSIST_PATH" == "x"], \ AC_DEFINE_UNQUOTED([POH_COUNTER_PERSIST_PATH], ["$POH_COUNTER_PERSIST_PATH"], \ [Path of file for storing POH counter.]) +AC_ARG_VAR(CHASSIS_STATE_CHANGE_PERSIST_PATH, [Path of file for storing the state change time.]) +AS_IF([test "x$CHASSIS_STATE_CHANGE_PERSIST_PATH" == "x"], \ + [CHASSIS_STATE_CHANGE_PERSIST_PATH="/var/lib/phosphor-state-manager/chassisStateChangeTime"]) +AC_DEFINE_UNQUOTED([CHASSIS_STATE_CHANGE_PERSIST_PATH], ["$CHASSIS_STATE_CHANGE_PERSIST_PATH"], \ + [Path of file for storing the state change time.]) + AC_ARG_VAR(BOOT_COUNT_MAX_ALLOWED, [The maximum allowed reboot count]) AS_IF([test "x$BOOT_COUNT_MAX_ALLOWED" == "x"], [BOOT_COUNT_MAX_ALLOWED=3]) AC_DEFINE_UNQUOTED([BOOT_COUNT_MAX_ALLOWED], [$BOOT_COUNT_MAX_ALLOWED], [The maximum allowed reboot count]) |