blob: 52cf1a191e2abb7e5a8ee91f1ba55133ed65ea4a [file] [edit]
#ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HOST_STATE_POWER_CONTROL_H_
#define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HOST_STATE_POWER_CONTROL_H_
#include <memory>
#include <string>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/functional/any_invocable.h"
#include "absl/status/statusor.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "boost/asio/executor_work_guard.hpp" // NOLINT: boost is commonly used in BMC
#include "boost/asio/io_context.hpp" // NOLINT: boost is commonly used in BMC
#include "boost/asio/steady_timer.hpp" // NOLINT: boost is commonly used in BMC
#include "thread/thread.h"
#include "time/clock.h"
#include "nlohmann/json.hpp"
#include "tlbmc/collector/gpio_collector.h"
#include "gpio_config.pb.h"
#include "power_control.pb.h"
#include "tlbmc/scheduler/scheduler.h"
namespace milotic_tlbmc {
class PowerControl {
public:
enum class PowerControlType { kGpio, kFileBased };
struct RetryPolicy {
// Default retry policy is to retry 5 times with a 10 second interval
// between retries.
RetryPolicy() : max_retries(5), retry_interval(absl::Seconds(10)) {}
int max_retries;
absl::Duration retry_interval;
};
struct Params {
// Using a thread factory to allow us to switch to fibers in future when it
// gets open sourced.
ecclesia::ThreadFactoryInterface* thread_factory =
ecclesia::GetDefaultThreadFactory();
ecclesia::Clock* clock = ecclesia::Clock::RealClock();
PowerControlConfig power_control_config;
GpioCollector* gpio_collector;
PowerControlType power_control_type = PowerControlType::kGpio;
std::string host_power_file_path = "/var/lib/power-control/state.json";
std::string os_state_file_path = "/var/lib/power-control/os-state.json";
RetryPolicy retry_policy;
};
virtual ~PowerControl() = default;
// SystemResetRequest is called when the system reset is requested.
virtual void SystemResetRequest([[maybe_unused]] SystemResetType reset_type,
[[maybe_unused]] absl::Duration delay) {};
// Requests are called by client from SystemResetRequest
// For these requests, see go/power-control-graph for more details.
virtual void PowerCycleRequest() {};
virtual void PowerOnRequest() {};
virtual void PowerOffRequest() {};
virtual void ResetRequest() {};
virtual void GracefulPowerOffRequest() {};
virtual void GracefulPowerCycleRequest() {};
virtual HostState GetHostState() = 0;
virtual void Start() {}
virtual void Stop() {}
virtual void RegisterHostPowerOnToOffCallback(
[[maybe_unused]] absl::AnyInvocable<void(bool) const> callback) {}
virtual void RegisterHostPowerOffToOnCallback(
[[maybe_unused]] absl::AnyInvocable<void(bool) const> callback) {}
virtual void RegisterHostOSInactiveToStandbyCallback(
[[maybe_unused]] absl::AnyInvocable<void(bool) const> callback) {}
virtual void RegisterHostOSStandbyToInactiveCallback(
[[maybe_unused]] absl::AnyInvocable<void(bool) const> callback) {}
virtual nlohmann::json ToJson() { return nlohmann::json(); };
virtual absl::Time GetLastStateChangeTime() { return absl::InfinitePast(); }
};
struct PowerControlThreadManager {
explicit PowerControlThreadManager(ecclesia::Clock* clock)
: task_scheduler(std::make_unique<TaskScheduler>(clock)) {}
~PowerControlThreadManager();
std::vector<std::unique_ptr<ecclesia::ThreadInterface>> threads;
std::vector<std::shared_ptr<boost::asio::io_context>> io_contexts;
std::vector<
boost::asio::executor_work_guard<boost::asio::io_context::executor_type>>
work_guards;
std::unique_ptr<TaskScheduler> task_scheduler;
};
class PowerControlGpio : public PowerControl {
public:
static absl::StatusOr<std::unique_ptr<PowerControlGpio>> Create(
const Params& params);
~PowerControlGpio() override;
HostState GetHostState() override {
absl::MutexLock lock(host_state_mutex_);
return host_state_;
}
// PowerOkHandler is called when the power OK GPIO line changes.
void PowerOkHandler(GpioEventType event);
// PostCompleteHandler is called when the POST complete GPIO line changes.
void PostCompleteHandler(GpioEventType event);
void SystemResetRequest(SystemResetType reset_type,
absl::Duration delay) override;
void PowerOnRequest() override;
void PowerOffRequest() override;
void PowerCycleRequest() override;
void ResetRequest() override;
void GracefulPowerOffRequest() override;
void GracefulPowerCycleRequest() override;
nlohmann::json ToJson() override;
// For testing only. Provide ability to cancel all timers in unit tests.
void CancelAllTimers() {
power_cycle_timer_.cancel();
power_ok_watchdog_timer_.cancel();
graceful_power_off_timer_.cancel();
warm_reset_check_timer_.cancel();
}
absl::Time GetLastStateChangeTime() override;
void Start() override;
void RegisterHostPowerOnToOffCallback(
absl::AnyInvocable<void(bool) const> callback)
ABSL_LOCKS_EXCLUDED(power_state_callback_mutex_) override;
void RegisterHostPowerOffToOnCallback(
absl::AnyInvocable<void(bool) const> callback)
ABSL_LOCKS_EXCLUDED(power_state_callback_mutex_) override;
void RegisterHostOSInactiveToStandbyCallback(
absl::AnyInvocable<void(bool) const> callback)
ABSL_LOCKS_EXCLUDED(os_state_callback_mutex_) override;
void RegisterHostOSStandbyToInactiveCallback(
absl::AnyInvocable<void(bool) const> callback)
ABSL_LOCKS_EXCLUDED(os_state_callback_mutex_) override;
protected:
PowerControlGpio(HostState host_state,
PowerControlConfig power_control_config,
GpioCollector* gpio_collector,
std::unique_ptr<PowerControlThreadManager>&& thread_manager,
boost::asio::steady_timer&& power_cycle_timer,
boost::asio::steady_timer&& power_ok_watchdog_timer,
boost::asio::steady_timer&& graceful_power_off_timer,
boost::asio::steady_timer&& warm_reset_check_timer,
ecclesia::Clock* clock = ecclesia::Clock::RealClock());
// For testing only.
explicit PowerControlGpio(
const std::shared_ptr<boost::asio::io_context>& io_context)
: power_cycle_timer_(*io_context),
power_ok_watchdog_timer_(*io_context),
graceful_power_off_timer_(*io_context),
warm_reset_check_timer_(*io_context),
clock_(ecclesia::Clock::RealClock()) {}
private:
void SetPowerState(PowerState state)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(host_state_mutex_);
void StartPowerCycleTimer();
void PowerCycleTimerExpired();
void StartPowerOKWatchdogTimer();
void PowerOkWatchdogTimerExpired();
void StartGracefulPowerOffTimer();
void GracefulPowerOffTimerExpired();
void StartWarmResetCheckTimer();
void WarmResetCheckTimerExpired();
void PowerOn();
void ForcePowerOff();
void GracefulPowerOff();
void Reset();
absl::Mutex host_state_mutex_;
absl::Mutex power_state_callback_mutex_;
absl::Mutex os_state_callback_mutex_;
HostState host_state_ ABSL_GUARDED_BY(host_state_mutex_);
PowerControlConfig power_control_config_;
GpioCollector* gpio_collector_ = nullptr;
std::unique_ptr<PowerControlThreadManager> thread_manager_;
boost::asio::steady_timer power_cycle_timer_;
boost::asio::steady_timer power_ok_watchdog_timer_;
boost::asio::steady_timer graceful_power_off_timer_;
boost::asio::steady_timer warm_reset_check_timer_;
ecclesia::Clock* clock_;
absl::Time last_state_change_time_ ABSL_GUARDED_BY(host_state_mutex_) =
absl::InfinitePast();
std::vector<absl::AnyInvocable<void(bool) const>>
host_power_on_to_off_callbacks_
ABSL_GUARDED_BY(power_state_callback_mutex_);
std::vector<absl::AnyInvocable<void(bool) const>>
host_power_off_to_on_callbacks_
ABSL_GUARDED_BY(power_state_callback_mutex_);
std::vector<absl::AnyInvocable<void(bool) const>>
host_os_inactive_to_standby_callbacks_
ABSL_GUARDED_BY(os_state_callback_mutex_);
std::vector<absl::AnyInvocable<void(bool) const>>
host_os_standby_to_inactive_callbacks_
ABSL_GUARDED_BY(os_state_callback_mutex_);
};
} // namespace milotic_tlbmc
#endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HOST_STATE_POWER_CONTROL_H_