blob: efefdbe3f97addbfe94fadc90568e9d32e6aa570 [file] [edit]
#ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_THERMAL_FAILSAFE_LOGGER_H_
#define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_THERMAL_FAILSAFE_LOGGER_H_
#include <cstdint>
#include <deque>
#include <memory>
#include <string>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "time/clock.h"
#include "tlbmc/thermal/thermal_control_utils.h"
namespace milotic_tlbmc {
namespace thermal {
constexpr uint32_t kDefaultMaxFailsafeLogCountPerSecond = 20;
/*
* `FailsafeLoggerCore` is a helper class that logs the failsafe mode events.
* It is designed to be used by `FailsafeLogger`.
*
* `FailsafeLoggerCore` is thread-safe.
*
* This class is created based on `failsafe_logger.hpp` and
* `failsafe_logger.cpp` at
* https://source.corp.google.com/piper///depot/google3/third_party/openbmc_phosphor_pid_control/failsafeloggers/
*/
class FailsafeLoggerCore {
public:
FailsafeLoggerCore() = default;
explicit FailsafeLoggerCore(
uint32_t max_log_count_per_second = kDefaultMaxFailsafeLogCountPerSecond,
FailsafeState current_failsafe_state = FailsafeState::NonFailsafe,
ecclesia::Clock* clock = ecclesia::Clock::RealClock())
: max_log_count_per_second_(max_log_count_per_second),
current_failsafe_state_(current_failsafe_state),
clock_(clock) {}
~FailsafeLoggerCore() = default;
// `OutputFailsafeLog` will attempt to output a failsafe log.
void OutputFailsafeLog(int zone_id, FailsafeState new_failsafe_state,
absl::string_view location, absl::string_view reason);
private:
absl::Mutex mutex_;
// The maximum number of log entries to be output within 1 second.
// It is recommended to be `20 * thermal_control_iteration_frequency_per_sec`.
// E.g., if a thermal control iteration takes 500ms, the value is recommended
// to be `20 * (1 / 0.5) = 40`.
// However, if the count is set too high, there may be a risk of spamming the
// log. Therefore, it is also recommended not to set a value larger than `50`.
const uint32_t max_log_count_per_second_;
// Whether the zone is currently in the failsafe mode.
FailsafeState current_failsafe_state_ ABSL_GUARDED_BY(mutex_) =
FailsafeState::NonFailsafe;
// The timestamps of the previous log entries within the last second.
// The max size of `log_time_stamps_` is `max_log_count_per_second_`.
std::deque<absl::Time> log_time_stamps_ ABSL_GUARDED_BY(mutex_);
// The logs that was output in the current failsafe state.
absl::flat_hash_set<std::string> current_logs_ ABSL_GUARDED_BY(mutex_);
// The clock used to to get the time.
ecclesia::Clock* clock_;
};
/*
* `FailsafeLogger` wraps the `FailsafeLoggerCore` for the thermal control
* service to output failsafe logs with rate control to avoid log spamming.
*
* `FailsafeLogger` is conditionally thread-safe:
* - If tuning mode is enabled, it is thread-safe at all times.
* - Otherwise, `sensor_name_to_zone_names_` is read only after the
* initialization, and is not protected by its mutex.
*
* Note that `zone_name_to_failsafe_logger_` manipulates `FailsafeLoggerCore`
* directly, so it is thread-safe at all times.
*
* This class is created based on `failsafe_logger_utility.hpp`,
* `failsafe_logger_utility.cpp`, `builder.hpp`, and `builder.cpp` at
* https://source.corp.google.com/piper///depot/google3/third_party/openbmc_phosphor_pid_control/failsafeloggers/
*/
class FailsafeLogger {
public:
FailsafeLogger() = default;
explicit FailsafeLogger(
uint32_t max_log_count_per_second = kDefaultMaxFailsafeLogCountPerSecond)
: max_log_count_per_second_(max_log_count_per_second) {}
~FailsafeLogger() = default;
// `LinkSensorToZone` creates the dependency topology between a sensor and an
// owner zone.
void LinkSensorToZone(absl::string_view sensor_name, int zone_id);
// `LinkSensorToZones` creates the dependency topology between a sensor and
// its owner zones. Note that `LinkSensorToZones` overwrites the existing
// zones that the sensor is linked to, and should only be used during the
// initialization.
void LinkSensorToZones(absl::string_view sensor_name,
const std::vector<int>& zone_ids);
// `CreateFailsafeLoggerForZone` creates a failsafe logger core instance for a
// specific zone.
void CreateFailsafeLoggerForZone(
int zone_id,
FailsafeState current_failsafe_state = FailsafeState::NonFailsafe);
// `OutputFailsafeLogFromZone` outputs failsafe logs for a specific zone.
void OutputFailsafeLogFromZone(int zone_id, FailsafeState new_failsafe_state,
absl::string_view location,
absl::string_view reason);
// `TryOutputFailsafeLogFromSensor` outputs failsafe logs for all owner zones
// given a sensor name.
void TryOutputFailsafeLogFromSensor(
absl::string_view sensor_name, FailsafeState new_failsafe_state,
absl::string_view location, absl::string_view reason,
ThreadSafetyMode thread_safety_mode =
ThreadSafetyMode::DisableThreadSafety);
// Returns the maximum number of log entries that can be output within 1
// second.
uint32_t GetMaxLogCountPerSecond() const { return max_log_count_per_second_; }
private:
void OutputFailsafeLogFromSensor(absl::string_view sensor_name,
FailsafeState new_failsafe_state,
absl::string_view location,
absl::string_view reason);
const uint32_t max_log_count_per_second_;
absl::Mutex sensor_name_to_zone_ids_mutex_;
// `sensor_name_to_zone_ids_` maps a sensor name to the names of all zones
// that own the sensor.
// It is conditionally thread-safe: If tuning mode is enabled, it is
// thread-safe at all times. Otherwise, it is read only after the
// initialization.
absl::flat_hash_map<std::string, std::vector<int>> sensor_name_to_zone_ids_;
absl::Mutex zone_id_to_failsafe_logger_mutex_;
// `zone_id_to_failsafe_logger_` maps a zone name to its failsafe logger.
absl::flat_hash_map<int, std::unique_ptr<FailsafeLoggerCore>>
zone_id_to_failsafe_logger_;
};
} // namespace thermal
} // namespace milotic_tlbmc
#endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_THERMAL_FAILSAFE_LOGGER_H_