| #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_ |