blob: c7bd6c93375a82590b88e79317403d517b5e326c [file]
#ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_COLLECTOR_THERMAL_COLLECTOR_H_
#define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_COLLECTOR_THERMAL_COLLECTOR_H_
#include <atomic>
#include <cstdint>
#include <memory>
#include <string>
#include <tuple>
#include <vector>
#include "absl/base/nullability.h"
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "boost/asio.hpp" //NOLINT: boost::asio is commonly used in BMC
#include "thread/thread.h"
#include "time/clock.h"
#include <nlohmann/json.hpp>
#include "tlbmc/collector/collector.h"
#include "tlbmc/collector/fru_collector.h"
#include "tlbmc/collector/sensor_collector_aggregator.h"
#include "tlbmc/configs/entity_config.h"
#include "fan_controller_config.pb.h"
#include "fan_pwm_config.pb.h"
#include "fan_tach_config.pb.h"
#include "hwmon_temp_sensor_config.pb.h"
#include "intel_cpu_sensor_config.pb.h"
#include "nic_telemetry_config.pb.h"
#include "psu_sensor_config.pb.h"
#include "shared_mem_sensor_config.pb.h"
#include "thermal_config.pb.h"
#include "virtual_sensor_config.pb.h"
#include "sensor.pb.h"
#include "tlbmc/scheduler/scheduler.h"
#include "tlbmc/thermal/controller/dff_controller.h"
#include "tlbmc/thermal/controller/eat_controller.h"
#include "tlbmc/thermal/controller/fan_pid_controller.h"
#include "tlbmc/thermal/controller/first_order_adrc_controller.h"
#include "tlbmc/thermal/controller/optimizer/pid/pid_optimizer.h"
#include "tlbmc/thermal/controller/pid_controller.h"
#include "tlbmc/thermal/controller/second_order_adrc_controller.h"
#include "tlbmc/thermal/controller/stepwise_controller.h"
#include "tlbmc/thermal/zone_manager.h"
namespace milotic_tlbmc {
struct ThermalThreadManager {
explicit ThermalThreadManager(ecclesia::Clock* clock)
: task_scheduler(std::make_unique<TaskScheduler>(clock)) {}
~ThermalThreadManager() = default;
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;
// This is used to store the task IDs of thermal zones to allow managing the
// lifetime of the zone.
absl::Mutex thermal_zone_id_to_task_id_mutex;
absl::flat_hash_map<std::string, int> thermal_zone_id_to_task_id
ABSL_GUARDED_BY(thermal_zone_id_to_task_id_mutex);
};
// TODO - b/459843950: `ConfigureCollection` will be needed for tuning mode.
class ThermalCollector : public Collector {
public:
// Parameters for creating a thermal collector.
struct Params {
// Thermal control related configs.
ZoneManagerConfigs zone_manager_configs;
FirstOrderAdrcControllerConfigs first_order_adrc_controller_configs;
SecondOrderAdrcControllerConfigs second_order_adrc_controller_configs;
DffControllerConfigs dff_controller_configs;
PidControllerConfigs pid_controller_configs;
StepwiseControllerConfigs stepwise_controller_configs;
FanPidControllerConfigs fan_pid_controller_configs;
// Supportive thermal control configs.
SupportiveZoneManagerConfigs supportive_zone_manager_configs;
EatControllerConfigs eat_controller_configs;
// Failsafe logger config.
FailsafeLoggerConfig failsafe_logger_config;
// Tuning related configs.
bool tuning_enabled = false;
std::string sample_data_file_prefix = "/tmp/tlbmc_thermal_data";
// 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();
};
~ThermalCollector() override;
// Creates a thermal collector.
static absl::StatusOr<std::unique_ptr<ThermalCollector>> Create(
const Params& params, SensorCollectorAggregator* absl_nonnull aggregator,
FruCollector* absl_nonnull fru_collector,
EntityConfig* absl_nonnull entity_config);
// Returns the list of thermal zones sorted by their IDs.
virtual std::vector<const thermal::ZoneManager*> GetAllThermalZones() const;
// Returns the list of 1st-order ADRC controllers sorted by their IDs.
virtual std::vector<const thermal::FirstOrderAdrcController*>
GetAllFirstOrderAdrcControllers() const;
// Returns the list of 2nd-order ADRC controllers sorted by their IDs.
virtual std::vector<const thermal::SecondOrderAdrcController*>
GetAllSecondOrderAdrcControllers() const;
// Returns the list of PID controllers sorted by their IDs.
virtual std::vector<const thermal::PidController*> GetAllPidControllers()
const;
// Returns the list of stepwise controllers sorted by their IDs.
virtual std::vector<const thermal::StepwiseController*>
GetAllStepwiseControllers() const;
// Returns the list of fan PID controllers sorted by their IDs.
virtual std::vector<const thermal::FanPidController*>
GetAllFanPidControllers() const;
// Returns the list of DFF controllers sorted by their IDs.
virtual std::vector<const thermal::DffController*> GetAllDffControllers()
const;
// Returns the list of EAT controllers sorted by their IDs.
virtual std::vector<const thermal::EatController*> GetAllEatControllers()
const;
virtual absl::StatusOr<const thermal::ZoneManager*> GetThermalZoneById(
int zone_id) const;
virtual absl::StatusOr<const thermal::FirstOrderAdrcController*>
GetFirstOrderAdrcControllerById(absl::string_view controller_id) const;
virtual absl::StatusOr<const thermal::SecondOrderAdrcController*>
GetSecondOrderAdrcControllerById(absl::string_view controller_id) const;
virtual absl::StatusOr<const thermal::PidController*> GetPidControllerById(
absl::string_view controller_id) const;
virtual absl::StatusOr<const thermal::StepwiseController*>
GetStepwiseControllerById(absl::string_view controller_id) const;
virtual absl::StatusOr<const thermal::FanPidController*>
GetFanPidControllerById(absl::string_view controller_id) const;
virtual absl::StatusOr<const thermal::DffController*> GetDffControllerById(
absl::string_view controller_id) const;
virtual absl::StatusOr<const thermal::EatController*> GetEatControllerById(
absl::string_view controller_id) const;
nlohmann::json GetSchedulerStats() const override {
return thermal_thread_manager_->task_scheduler->ToJson();
}
nlohmann::json ToJson() const override;
virtual void StartCollection();
// `StartThermalControl` will trigger infinite thermal control loops for every
// zone manager.
void StartThermalControl();
void StopThermalControl() { stop_thermal_control_ = true; }
// Enable/Disable fan manual mode for all zones.
virtual void SetManualMode(bool enable);
// Returns the map of zone ID to manual mode status.
virtual absl::flat_hash_map<int, bool> GetZoneIdToManualModes() const;
virtual void SetManualPwm(absl::string_view fan_name, double pwm);
// Tuning request functions
virtual absl::Status RequestPidControllerCoefficientsTuning(
absl::string_view controller_id,
const thermal::PidController::PidControllerCoefficients& coefficients);
virtual absl::Status RequestFanPidControllerCoefficientsTuning(
absl::string_view controller_id,
const thermal::FanPidController::FanPidControllerCoefficients&
coefficients);
virtual bool HasPendingTuningRequest(absl::string_view controller_id);
protected:
// Protected constructor to allow mocking.
ThermalCollector() = default;
thermal::ZoneManager* GetNonConstThermalZoneById(int zone_id);
// `ProcessOneThermalLoop` will perform one thermal control calculation for
// all the thermal loops controlled by the specified zone.
static void ProcessOneThermalLoop(thermal::ZoneManager* zone_manager);
// `ProcessThermalControl` will continuously perform thermal loops, including
// fan speed update for the specified zone.
void ProcessThermalControl(
thermal::ZoneManager* zone_manager,
const std::shared_ptr<boost::asio::steady_timer>& timer,
bool first_run = false, uint64_t ms_elapsed_in_current_cycle = 0);
// Setters (e.g., for tuning purposes).
virtual void ApplyPendingTuningRequests(int zone_id);
// Creates a PID auto-tuner based on the PID controller config.
static absl::StatusOr<thermal::PidOptimizerInfo> CreatePidAutoTuner(
const PidControllerConfig& pid_controller_config);
private:
struct ControllerIdToController {
absl::flat_hash_map<std::string, thermal::FirstOrderAdrcController*>
controller_id_to_first_order_adrc_controller;
absl::flat_hash_map<std::string, thermal::SecondOrderAdrcController*>
controller_id_to_second_order_adrc_controller;
absl::flat_hash_map<std::string, thermal::PidController*>
controller_id_to_pid_controller;
absl::flat_hash_map<std::string, thermal::StepwiseController*>
controller_id_to_stepwise_controller;
absl::flat_hash_map<std::string, thermal::FanPidController*>
controller_id_to_fan_pid_controller;
absl::flat_hash_map<std::string, thermal::DffController*>
controller_id_to_dff_controller;
absl::flat_hash_map<std::string, thermal::EatController*>
controller_id_to_eat_controller;
};
ThermalCollector(
absl::flat_hash_map<
std::tuple</*zone_id*/ int, /*controller_id*/ std::string>,
std::unique_ptr<thermal::FirstOrderAdrcController>>&&
zone_id_and_controller_id_to_first_order_adrc_controller,
absl::flat_hash_map<
std::tuple</*zone_id*/ int, /*controller_id*/ std::string>,
std::unique_ptr<thermal::SecondOrderAdrcController>>&&
zone_id_and_controller_id_to_second_order_adrc_controller,
absl::flat_hash_map<int, std::unique_ptr<thermal::ZoneManager>>&&
zone_id_to_zone_manager,
absl::flat_hash_map<
std::tuple</*zone_id*/ int, /*controller_id*/ std::string>,
std::unique_ptr<thermal::PidController>>&&
zone_id_and_controller_id_to_pid_controller,
absl::flat_hash_map<
std::tuple</*zone_id*/ int, /*controller_id*/ std::string>,
std::unique_ptr<thermal::StepwiseController>>&&
zone_id_and_controller_id_to_stepwise_controller,
absl::flat_hash_map<
std::tuple</*zone_id*/ int, /*controller_id*/ std::string>,
std::unique_ptr<thermal::FanPidController>>&&
zone_id_and_controller_id_to_fan_pid_controller,
absl::flat_hash_map<
std::tuple</*zone_id*/ int, /*controller_id*/ std::string>,
std::unique_ptr<thermal::DffController>>&&
zone_id_and_controller_id_to_dff_controller,
absl::flat_hash_map<std::string, std::vector<int>>&&
controller_id_to_zone_ids,
absl::flat_hash_map<int, std::unique_ptr<thermal::ZoneManager>>&&
zone_id_to_supportive_zone_manager,
absl::flat_hash_map<
std::tuple</*zone_id*/ int, /*controller_id*/ std::string>,
std::unique_ptr<thermal::EatController>>&&
zone_id_and_controller_id_to_eat_controller,
std::unique_ptr<ThermalThreadManager> thermal_thread_manager,
ecclesia::ThreadFactoryInterface* thread_factory);
// `Link<ControllerType>ToZoneAndCreateIdToControllerMap` will link the
// controllers to their zone managers via `ZoneManager::AddThermalController`
// and return a map of controller ID to controller for handling future Redfish
// requests.
absl::flat_hash_map<std::string, thermal::FirstOrderAdrcController*>
LinkFirstOrderAdrcControllerToZoneAndCreateIdToControllerMap();
absl::flat_hash_map<std::string, thermal::SecondOrderAdrcController*>
LinkSecondOrderAdrcControllerToZoneAndCreateIdToControllerMap();
absl::flat_hash_map<std::string, thermal::PidController*>
LinkPidControllerToZoneAndCreateIdToControllerMap();
absl::flat_hash_map<std::string, thermal::StepwiseController*>
LinkStepwiseControllerToZoneAndCreateIdToControllerMap();
absl::flat_hash_map<std::string, thermal::FanPidController*>
LinkFanPidControllerToZoneAndCreateIdToControllerMap();
absl::flat_hash_map<std::string, thermal::DffController*>
LinkDffControllerToZoneAndCreateIdToControllerMap();
absl::flat_hash_map<std::string, thermal::EatController*>
LinkEatControllerToZoneAndCreateIdToControllerMap();
// Make sure the accessor is destroyed after the thread manager.
// In today's use case, we will never have dynamic set of thermal zones. All
// zones are created during daemon start-up.
absl::flat_hash_map<int, std::unique_ptr<thermal::ZoneManager>>
zone_id_to_zone_manager_;
absl::flat_hash_map<std::tuple<int, std::string>,
std::unique_ptr<thermal::FirstOrderAdrcController>>
zone_id_and_controller_id_to_first_order_adrc_controller_;
absl::flat_hash_map<std::tuple<int, std::string>,
std::unique_ptr<thermal::SecondOrderAdrcController>>
zone_id_and_controller_id_to_second_order_adrc_controller_;
absl::flat_hash_map<std::tuple<int, std::string>,
std::unique_ptr<thermal::PidController>>
zone_id_and_controller_id_to_pid_controller_;
const absl::flat_hash_map<std::tuple<int, std::string>,
std::unique_ptr<thermal::StepwiseController>>
zone_id_and_controller_id_to_stepwise_controller_;
absl::flat_hash_map<std::tuple<int, std::string>,
std::unique_ptr<thermal::FanPidController>>
zone_id_and_controller_id_to_fan_pid_controller_;
const absl::flat_hash_map<std::tuple<int, std::string>,
std::unique_ptr<thermal::DffController>>
zone_id_and_controller_id_to_dff_controller_;
const absl::flat_hash_map<std::string, std::vector<int>>
controller_id_to_zone_ids_;
// We may need to add `controller_id_to_supportive_zone_manager` in
// the future, but not now, as the only controller type owned by such zones
// is the EAT controller, which is expected to be unique (i.e., only one
// instance).
const absl::flat_hash_map<int, std::unique_ptr<thermal::ZoneManager>>
zone_id_to_supportive_zone_manager_;
const absl::flat_hash_map<std::tuple<int, std::string>,
std::unique_ptr<thermal::EatController>>
zone_id_and_controller_id_to_eat_controller_;
std::atomic<bool> stop_thermal_control_ = false;
const ControllerIdToController controller_id_to_controller_;
// Tuning mode related members.
absl::Mutex tuning_mutex_;
absl::flat_hash_set<int> zone_ids_with_pending_tuning_request_
ABSL_GUARDED_BY(tuning_mutex_);
// {<Controller ID> := <pending thermal coefficient>}
absl::flat_hash_map<std::string,
thermal::PidController::PidControllerCoefficients>
controller_id_to_pending_pid_coefficient_ ABSL_GUARDED_BY(tuning_mutex_);
absl::flat_hash_map<std::string,
thermal::FanPidController::FanPidControllerCoefficients>
controller_id_to_pending_fan_pid_coefficient_
ABSL_GUARDED_BY(tuning_mutex_);
std::vector<std::shared_ptr<boost::asio::steady_timer>>
thermal_control_timers_;
std::unique_ptr<ThermalThreadManager> thermal_thread_manager_;
ecclesia::ThreadFactoryInterface* thread_factory_;
};
class EmptyThermalCollector final : public ThermalCollector {
public:
static std::unique_ptr<EmptyThermalCollector> Create();
// Returns the list of thermal zones sorted by their IDs.
std::vector<const thermal::ZoneManager*> GetAllThermalZones() const override;
// Returns the list of 1st-order ADRC controllers sorted by their IDs.
std::vector<const thermal::FirstOrderAdrcController*>
GetAllFirstOrderAdrcControllers() const override;
// Returns the list of 2nd-order ADRC controllers sorted by their IDs.
std::vector<const thermal::SecondOrderAdrcController*>
GetAllSecondOrderAdrcControllers() const override;
// Returns the list of PID controllers sorted by their IDs.
std::vector<const thermal::PidController*> GetAllPidControllers()
const override;
// Returns the list of stepwise controllers sorted by their IDs.
std::vector<const thermal::StepwiseController*> GetAllStepwiseControllers()
const override;
// Returns the list of fan PID controllers sorted by their IDs.
std::vector<const thermal::FanPidController*> GetAllFanPidControllers()
const override;
// Returns the list of DFF controllers sorted by their IDs.
std::vector<const thermal::DffController*> GetAllDffControllers()
const override;
// Returns the list of EAT controllers sorted by their IDs.
std::vector<const thermal::EatController*> GetAllEatControllers()
const override;
absl::StatusOr<const thermal::ZoneManager*> GetThermalZoneById(
int zone_id) const override;
absl::StatusOr<const thermal::FirstOrderAdrcController*>
GetFirstOrderAdrcControllerById(
absl::string_view controller_id) const override;
absl::StatusOr<const thermal::SecondOrderAdrcController*>
GetSecondOrderAdrcControllerById(
absl::string_view controller_id) const override;
absl::StatusOr<const thermal::PidController*> GetPidControllerById(
absl::string_view controller_id) const override;
absl::StatusOr<const thermal::StepwiseController*> GetStepwiseControllerById(
absl::string_view controller_id) const override;
absl::StatusOr<const thermal::FanPidController*> GetFanPidControllerById(
absl::string_view controller_id) const override;
absl::StatusOr<const thermal::DffController*> GetDffControllerById(
absl::string_view controller_id) const override;
absl::StatusOr<const thermal::EatController*> GetEatControllerById(
absl::string_view controller_id) const override;
nlohmann::json GetSchedulerStats() const override;
nlohmann::json ToJson() const override;
void StartCollection();
void SetManualMode(bool enable) override;
absl::flat_hash_map<int, bool> GetZoneIdToManualModes() const override;
void SetManualPwm(absl::string_view fan_name, double pwm) override;
absl::Status RequestPidControllerCoefficientsTuning(
absl::string_view controller_id,
const thermal::PidController::PidControllerCoefficients& coefficients)
override;
absl::Status RequestFanPidControllerCoefficientsTuning(
absl::string_view controller_id,
const thermal::FanPidController::FanPidControllerCoefficients&
coefficients) override;
};
} // namespace milotic_tlbmc
#endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_COLLECTOR_THERMAL_COLLECTOR_H_