| #include "tlbmc/sensors/intel_cpu_sensor.h" |
| |
| #include <algorithm> |
| #include <array> |
| #include <cctype> |
| #include <cerrno> |
| #include <charconv> |
| #include <chrono> // NOLINT: chrono is commonly used in BMC |
| #include <cstdint> |
| #include <cstring> |
| #include <fstream> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <system_error> // NOLINT: system_error is commonly used in BMC |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/container/flat_hash_set.h" |
| #include "absl/functional/any_invocable.h" |
| #include "absl/log/log.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/ascii.h" |
| #include "absl/strings/match.h" |
| #include "absl/strings/numbers.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_replace.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/strings/substitute.h" |
| #include "g3/macros.h" |
| #include "entity_common_config.pb.h" |
| #include "intel_cpu_sensor_config.pb.h" |
| #include "reading_range_config.pb.h" |
| #include "reading_transform_config.pb.h" |
| #include "threshold_config.pb.h" |
| #include "tlbmc/hal/sysfs/peci.h" |
| #include "resource.pb.h" |
| #include "sensor.pb.h" |
| #include "tlbmc/sensors/peci_hwmon_based_sensor.h" |
| #include "tlbmc/sensors/sensor.h" |
| #include "tlbmc/time/time.h" |
| #include "re2/re2.h" |
| |
| namespace milotic_tlbmc { |
| |
| template <class TypeNameFirst, class TypeNameSecond> |
| struct TypeComparator { |
| // Compared by the first element which is the type name. |
| bool operator()(const std::pair<TypeNameFirst, TypeNameSecond>& lhs, |
| const std::pair<TypeNameFirst, TypeNameSecond>& rhs) const { |
| return lhs.first < rhs.first; |
| } |
| }; |
| |
| // Reading properties for IntelCpuSensors are statically configured by type. |
| // From: |
| // https://github.com/openbmc/dbus-sensors/blob/master/src/intel-cpu/IntelCPUSensor.cpp#L72 |
| constexpr std::array<IntelCpuSensor::ReadingProperties, 2> |
| kDefaultIntelCpuSensorProperties = { |
| // Power sensor |
| IntelCpuSensor::ReadingProperties{.max_reading = 511, |
| .min_reading = 0, |
| .sensor_unit = SensorUnit::UNIT_WATT}, |
| // Temperature sensor |
| IntelCpuSensor::ReadingProperties{ |
| .max_reading = 127, |
| .min_reading = -128, |
| .sensor_unit = SensorUnit::UNIT_DEGREE_CELSIUS}, |
| }; |
| |
| // From: |
| // https://github.com/openbmc/dbus-sensors/blob/master/src/Thresholds.cpp#L492 |
| static const absl::flat_hash_map< |
| std::string, std::vector<std::tuple<std::string, ThresholdType>>> |
| kThresholdAttributesMap = { // NOLINT(google3-runtime-global-variables) |
| {"average", |
| {std::make_tuple("average_min", THRESHOLD_TYPE_LOWER_NON_CRITICAL), |
| std::make_tuple("average_max", THRESHOLD_TYPE_UPPER_NON_CRITICAL)}}, |
| {"input", |
| {std::make_tuple("min", THRESHOLD_TYPE_LOWER_NON_CRITICAL), |
| std::make_tuple("max", THRESHOLD_TYPE_UPPER_NON_CRITICAL), |
| std::make_tuple("lcrit", THRESHOLD_TYPE_LOWER_CRITICAL), |
| std::make_tuple("crit", THRESHOLD_TYPE_UPPER_CRITICAL)}}}; |
| |
| ReadingRangeConfigs GetReadingRangeConfig( |
| const IntelCpuSensor::ReadingProperties& reading_properties) { |
| ReadingRangeConfigs reading_range_configs; |
| ReadingRangeConfig* reading_range_max = |
| reading_range_configs.add_reading_range_configs(); |
| reading_range_max->set_type(READING_RANGE_TYPE_MAX); |
| reading_range_max->set_reading(reading_properties.max_reading); |
| ReadingRangeConfig* reading_range_min = |
| reading_range_configs.add_reading_range_configs(); |
| reading_range_min->set_type(READING_RANGE_TYPE_MIN); |
| reading_range_min->set_reading(reading_properties.min_reading); |
| return reading_range_configs; |
| } |
| |
| std::vector<boost::filesystem::path> IntelCpuSensor::FindFiles( |
| const boost::filesystem::path& root_dir, const RE2& regex, int max_depth) { |
| std::vector<boost::filesystem::path> files; |
| if (!boost::filesystem::exists(root_dir)) { |
| DLOG(WARNING) << "root_dir does not exist: " << root_dir; |
| return files; |
| } |
| |
| boost::system::error_code ec; |
| boost::filesystem::recursive_directory_iterator p( |
| root_dir, boost::filesystem::directory_options::follow_directory_symlink, |
| ec); |
| |
| if (ec) { |
| DLOG(WARNING) << "Failed to initialize recursive_directory_iterator for " |
| << root_dir << ": " << ec.message(); |
| return files; |
| } |
| |
| boost::filesystem::recursive_directory_iterator end; |
| while (p != end) { |
| if (RE2::PartialMatch(p->path().string(), regex)) { |
| files.push_back(p->path()); |
| } |
| if (p.depth() >= max_depth) { |
| p.disable_recursion_pending(); |
| } |
| |
| p.increment(ec); |
| |
| if (ec) { |
| DLOG(WARNING) << "Error during directory iteration in " << root_dir |
| << ": " << ec.message(); |
| break; |
| } |
| } |
| |
| return files; |
| } |
| |
| std::string IntelCpuSensor::CreateSensorName(absl::string_view label, |
| uint32_t cpu_id) { |
| std::string sensor_name = std::string(label); |
| SensorUnit sensor_unit = absl::StrContainsIgnoreCase(sensor_name, "power") |
| ? SensorUnit::UNIT_WATT |
| : SensorUnit::UNIT_DEGREE_CELSIUS; |
| |
| std::string cpu_name = absl::StrCat("CPU", cpu_id); |
| constexpr absl::string_view kDimmLabel = "DIMM"; |
| std::size_t dimm_found = sensor_name.find(kDimmLabel); |
| if (dimm_found == std::string::npos) { |
| absl::StrAppend(&sensor_name, "_", cpu_name); |
| } |
| |
| // Convert to Upper Camel case whole name |
| // From: |
| // https://github.com/openbmc/dbus-sensors/blob/master/src/intel-cpu/IntelCPUSensorMain.cpp#L138 |
| bool is_word_end = true; |
| std::transform(sensor_name.begin(), sensor_name.end(), sensor_name.begin(), |
| [&is_word_end](int c) { |
| if (c == '_') { |
| is_word_end = true; |
| } else { |
| if (is_word_end) { |
| is_word_end = false; |
| return std::toupper(c); |
| } |
| } |
| return c; |
| }); |
| |
| switch (sensor_unit) { |
| case SensorUnit::UNIT_WATT: |
| sensor_name = absl::StrCat("power_", sensor_name); |
| break; |
| case SensorUnit::UNIT_DEGREE_CELSIUS: |
| sensor_name = absl::StrCat("temperature_", sensor_name); |
| break; |
| default: |
| // IntelCpuSensors will never have these units |
| DLOG(WARNING) << "IntelCpuSensor " << sensor_name |
| << " has unexpected unit: " << sensor_unit; |
| break; |
| } |
| |
| return absl::StrReplaceAll(sensor_name, {{" ", "_"}}); |
| } |
| |
| absl::StatusOr<std::tuple<std::string, std::string, std::string>> |
| IntelCpuSensor::SplitInputFile(const boost::filesystem::path& input_file_path) { |
| std::string input_file = input_file_path.filename().string(); |
| size_t find_num = input_file.find_first_of("1234567890"); |
| size_t find_underscore = input_file.find_first_of('_'); |
| if (find_num == std::string::npos || find_underscore == std::string::npos || |
| find_underscore <= find_num) { |
| return absl::InvalidArgumentError( |
| absl::Substitute("Invalid input file: $0", input_file)); |
| } |
| return std::make_tuple( |
| input_file.substr(0, find_num), |
| input_file.substr(find_num, find_underscore - find_num), |
| input_file.substr(find_underscore + 1)); |
| } |
| |
| void IntelCpuSensor::HandleRefreshResult(const boost::system::error_code& error, |
| size_t bytes_read) { |
| if (error) { |
| State state; |
| state.set_status(STATUS_STALE); |
| state.set_status_message(absl::Substitute( |
| "Failed to read from input device: $0; input device path: $1", |
| error.message(), GetInputDevicePath())); |
| UpdateState(std::move(state)); |
| return; |
| } |
| const char* buffer_end = GetConstReadBuffer().data() + bytes_read; |
| int64_t value = 0; |
| std::from_chars_result result = |
| std::from_chars(GetConstReadBuffer().data(), buffer_end, value); |
| if (result.ec != std::errc()) { |
| State state; |
| state.set_status(STATUS_STALE); |
| state.set_status_message( |
| absl::StrCat("Read data can't be converted to a number: ", |
| std::make_error_condition(result.ec).message())); |
| UpdateState(std::move(state)); |
| return; |
| } |
| // TODO(b/449557765): Parse thresholds when tControl value changes instead of |
| // on every refresh. |
| ParseThresholdsFromHwmonFiles(); |
| SensorValue sensor_data; |
| *sensor_data.mutable_timestamp() = Now(); |
| sensor_data.set_reading( |
| (static_cast<double>(value) / |
| sensor_attributes_static_.reading_transform().scale()) + |
| sensor_attributes_static_.reading_transform().offset()); |
| StoreSensorData(std::make_shared<const SensorValue>(std::move(sensor_data))); |
| State state; |
| state.set_status(STATUS_READY); |
| UpdateState(std::move(state)); |
| } |
| |
| RelatedItem IntelCpuSensor::GetRelatedItem( |
| const SensorInstanceProperties& sensor_instance_properties, |
| const IntelCpuSensorConfig& sensor_config) { |
| if (sensor_instance_properties.has_related_item()) { |
| return sensor_instance_properties.related_item(); |
| } |
| RelatedItem related_item; |
| related_item.set_type(RESOURCE_TYPE_PROCESSOR); |
| related_item.set_id(absl::StrCat("cpu", sensor_config.cpu_id())); |
| return related_item; |
| } |
| |
| absl::StatusOr<std::vector<std::shared_ptr<Sensor>>> |
| IntelCpuSensor::CreateInitialSensors( |
| const IntelCpuSensorConfig& sensor_config, |
| const std::shared_ptr<boost::asio::io_context>& io_context, |
| const PeciSysfs& peci_sysfs, |
| std::optional<NotificationCb> on_batch_notify) { |
| std::vector<std::shared_ptr<Sensor>> sensors; |
| for (const auto& [label, properties] : |
| sensor_config.label_to_sensor_instance_properties()) { |
| SensorInstanceProperties mutable_instance_properties = properties; |
| std::string_view base_name = |
| properties.name().empty() ? label : properties.name(); |
| IntelCpuSensor::ReadingProperties reading_properties = |
| absl::StrContainsIgnoreCase(base_name, "power") |
| ? kDefaultIntelCpuSensorProperties[0] |
| : kDefaultIntelCpuSensorProperties[1]; |
| mutable_instance_properties.set_name( |
| CreateSensorName(base_name, sensor_config.cpu_id())); |
| *mutable_instance_properties.mutable_related_item() = |
| GetRelatedItem(properties, sensor_config); |
| mutable_instance_properties.set_unit(reading_properties.sensor_unit); |
| mutable_instance_properties.mutable_reading_transform_config()->set_scale( |
| IntelCpuSensor::kScaleFactor); |
| *mutable_instance_properties.mutable_reading_ranges() = |
| GetReadingRangeConfig(reading_properties); |
| |
| std::shared_ptr<IntelCpuSensor> sensor = std::make_shared<IntelCpuSensor>( |
| Token(), sensor_config.type(), "", label, |
| sensor_config.hal_common_config(), sensor_config.entity_common_config(), |
| mutable_instance_properties, sensor_config.dts_offset(), io_context, |
| on_batch_notify, peci_sysfs); |
| State state; |
| state.set_status(STATUS_CREATION_PENDING); |
| state.set_status_message("Initial creation, CPU/DIMM not detected yet"); |
| sensor->UpdateState(std::move(state)); |
| LOG(INFO) << "Created IntelCpuSensor: " |
| << mutable_instance_properties.name(); |
| sensors.push_back(sensor); |
| } |
| |
| return sensors; |
| } |
| |
| absl::flat_hash_map<std::string, std::string> |
| IntelCpuSensor::CreateLabelToInputFileMap( |
| const boost::filesystem::path& peci_device_path, |
| const HalCommonConfig& hal_config) { |
| absl::flat_hash_map<std::string, std::string> label_to_input_file; |
| |
| // Expected directory structure is |
| // /sys/bus/peci/devices/peci-$BUS/$BUS-$ADDRESS/peci-*.0/hwmon/hwmon*/name |
| // So we search with max depth of 4 for potential name files. |
| // From: |
| // https://github.com/openbmc/dbus-sensors/blob/master/src/intel-cpu/IntelCPUSensorMain.cpp#L195 |
| std::vector<boost::filesystem::path> hwmon_name_paths = FindFiles( |
| peci_sysfs_.GetBusPath(hal_config.bus()) / |
| peci_sysfs_.GetDeviceDirectoryName(hal_config), |
| RE2(absl::Substitute(IntelCpuSensor::kHwmonNameRegex, hal_config.bus(), |
| absl::Hex(hal_config.address()))), |
| 4); |
| if (hwmon_name_paths.empty()) { |
| DLOG(ERROR) << "No hwmon paths found for CPU sensors in system"; |
| return label_to_input_file; |
| } |
| |
| absl::flat_hash_set<std::string> scanned_directories; |
| for (const boost::filesystem::path& hwmon_name_path : hwmon_name_paths) { |
| auto hwmon_dir = hwmon_name_path.parent_path(); |
| auto ret = scanned_directories.insert(hwmon_dir.string()); |
| if (!ret.second) { |
| continue; // already processed this directory |
| } |
| |
| std::ifstream name_file(hwmon_name_path.string()); |
| if (!name_file.good()) { |
| DLOG(WARNING) << "Failed to open file: " << hwmon_name_path.string(); |
| continue; |
| } |
| std::string name; |
| if (!std::getline(name_file, name)) { |
| DLOG(WARNING) << "Failed to read name file: " << hwmon_name_path.string() |
| << " with error: " << strerror(errno); |
| continue; |
| } |
| if (name.empty()) { |
| continue; |
| } |
| |
| auto parent_dir = hwmon_name_path.parent_path(); |
| std::vector<boost::filesystem::path> input_paths = |
| FindFiles(parent_dir, *IntelCpuSensor::kInputRegex, 0); |
| if (input_paths.empty()) { |
| DLOG(WARNING) << "No input files for sensors in system at " << parent_dir; |
| continue; |
| } |
| for (const boost::filesystem::path& input_path : input_paths) { |
| auto find_underscore = input_path.filename().string().find('_'); |
| if (find_underscore == std::string::npos) { |
| continue; |
| } |
| absl::StatusOr<std::tuple<std::string, std::string, std::string>> |
| input_file_parts = SplitInputFile(input_path); |
| if (!input_file_parts.ok()) { |
| DLOG(INFO) << "Invalid input file: " << input_file_parts.status(); |
| continue; |
| } |
| auto& [prefix, number, suffix] = *input_file_parts; |
| std::string label_file_path = |
| (parent_dir / absl::StrCat(prefix, number, "_label")).string(); |
| std::ifstream label_file(label_file_path); |
| if (!label_file.good()) { |
| DLOG(WARNING) << "Failed to open file: " << label_file_path; |
| continue; |
| } |
| std::string label; |
| if (!std::getline(label_file, label)) { |
| DLOG(WARNING) << "Failed to read label file: " << label_file_path |
| << " with error: " << strerror(errno); |
| continue; |
| } |
| label = std::string(absl::StripTrailingAsciiWhitespace(label)); |
| |
| if (std::find(IntelCpuSensor::kHiddenProperties.begin(), |
| IntelCpuSensor::kHiddenProperties.end(), |
| label) != IntelCpuSensor::kHiddenProperties.end()) { |
| continue; |
| } |
| |
| label_to_input_file[label] = input_path.string(); |
| } |
| } |
| return label_to_input_file; |
| } |
| |
| void IntelCpuSensor::ParseThresholdsFromHwmonFiles() { |
| if (absl::StatusOr<std::tuple<std::string, std::string, std::string>> |
| input_file_parts = SplitInputFile(input_dev_path_); |
| input_file_parts.ok()) { |
| auto& [prefix, number, suffix] = *input_file_parts; |
| if (kThresholdAttributesMap.contains(suffix)) { |
| ThresholdConfigs new_threshold_configs; |
| bool has_valid_threshold = false; |
| for (const auto& [attribute, threshold_type] : |
| kThresholdAttributesMap.at(suffix)) { |
| ThresholdConfig* threshold_config = |
| new_threshold_configs.add_threshold_configs(); |
| threshold_config->set_type(threshold_type); |
| boost::filesystem::path attribute_file_path = |
| boost::filesystem::path(input_dev_path_).parent_path() / |
| absl::StrCat(prefix, number, "_", attribute); |
| std::ifstream attr_file(attribute_file_path.string()); |
| if (!attr_file.good()) { |
| DLOG(INFO) << "Failed to open file: " << attribute_file_path; |
| continue; |
| } |
| std::string attr_value_str; |
| std::getline(attr_file, attr_value_str); |
| double value = 0; |
| if (!absl::SimpleAtod(attr_value_str, &value)) { |
| DLOG(INFO) << "Failed to convert value: " << attr_value_str |
| << " to double"; |
| continue; |
| } |
| double offset = (attribute == "crit") ? dts_offset_ : 0; |
| value = value / IntelCpuSensor::kScaleFactor + offset; |
| threshold_config->set_value(value); |
| // If at least one of the thresholds has a valid value, we should update |
| // this sensor's thresholds. |
| has_valid_threshold = true; |
| DLOG(INFO) << "IntelCPUSensor parsed threshold: " << attribute_file_path |
| << " " << attribute << " " << threshold_type << " " << value; |
| } |
| |
| if (has_valid_threshold) { |
| UpdateThresholds(new_threshold_configs); |
| } |
| } |
| } |
| } |
| |
| absl::Status IntelCpuSensor::ReinitializeInternal() { |
| absl::string_view sensor_name = |
| GetSensorAttributesStatic().attributes().key(); |
| if (IsInputDeviceUsable()) { |
| return absl::FailedPreconditionError( |
| absl::StrCat("Attempted to reinitialize usable sensor: ", sensor_name, |
| ". Ignoring reinitialization.")); |
| } |
| |
| ECCLESIA_ASSIGN_OR_RETURN(boost::filesystem::path peci_device_path, |
| PeciHwmonBasedSensor::CreateDevice( |
| sensor_attributes_static_.hal_common_config())); |
| |
| absl::flat_hash_map<std::string, std::string> label_to_input_file = |
| CreateLabelToInputFileMap(peci_device_path, |
| sensor_attributes_static_.hal_common_config()); |
| |
| auto it = label_to_input_file.find(sensor_label_); |
| std::string error_message = |
| absl::StrCat("Failed to find ", sensor_label_, " in hwmon folder ", |
| peci_device_path.string()); |
| if (it == label_to_input_file.end()) { |
| DLOG(WARNING) << error_message; |
| // Set state to CREATION_FAILED with error message to indicate that the |
| // sensor was attempted to be created but failed. |
| return absl::NotFoundError(error_message); |
| } |
| |
| SetInputDevicePath(it->second); |
| |
| ParseThresholdsFromHwmonFiles(); |
| |
| // Set to Status UNKNOWN to simulate fresh creation of Sensor object. |
| State state; |
| state.set_status(STATUS_UNKNOWN); |
| state.set_status_message("Reinitialized, pending sensor refresh"); |
| UpdateState(std::move(state)); |
| |
| // Set up the input device file for reading. This must be done last as right |
| // after this function is done, the sensor is readable. |
| SetUpInput(); |
| |
| LOG(INFO) << "Successfully reinitialized IntelCpuSensor: " << sensor_name; |
| |
| return absl::OkStatus(); |
| } |
| |
| void IntelCpuSensor::ReinitializeWithRetry() { |
| absl::Status status = ReinitializeInternal(); |
| if (status.ok()) { |
| absl::AnyInvocable<void(absl::Status)> callback = |
| std::move(reinitialize_callback_); |
| reinitialize_callback_ = nullptr; |
| if (callback != nullptr) { |
| callback(status); |
| } |
| return; |
| } |
| |
| absl::string_view sensor_name = |
| GetSensorAttributesStatic().attributes().key(); |
| DLOG(WARNING) << "Failed to reinitialize IntelCpuSensor: " << sensor_name |
| << " with status: " << status << ". Attempt " << attempt_num_; |
| |
| uint32_t retry_delay = attempt_num_ < retry_delays_.size() |
| ? retry_delays_[attempt_num_] |
| : kDefaultReinitializeRetryDelay; |
| if (attempt_num_ >= retry_delays_.size()) { |
| // If the sensor has failed to reinitialize after all configured retry |
| // delays, we retry at the default retry delay (5 minutes) and keep state |
| // to CREATION_PENDING. This way the sensor will be retried indefinitely |
| // and until then, it will be considered absent. |
| State state; |
| state.set_status(STATUS_CREATION_PENDING); |
| state.set_status_message(absl::StrCat( |
| "Failed to reinitialize IntelCpuSensor after ", attempt_num_, |
| " attempts. " |
| "Last status: ", |
| status.message())); |
| UpdateState(std::move(state)); |
| |
| // For unit testing, we call the callback to notify the test reinitialize |
| // has failed. In the real scenario, callback only logs and returns when |
| // status is not ok. |
| if (reinitialize_callback_ != nullptr) { |
| reinitialize_callback_(status); |
| } |
| } |
| std::weak_ptr<IntelCpuSensor> weak_self = |
| std::static_pointer_cast<IntelCpuSensor>(shared_from_this()); |
| retry_timer_.expires_after(std::chrono::seconds(retry_delay)); |
| retry_timer_.async_wait( |
| [weak_self](const boost::system::error_code& ec) mutable { |
| auto self = weak_self.lock(); |
| if (self == nullptr) { |
| return; |
| } |
| |
| if (ec) { |
| if (ec != boost::asio::error::operation_aborted) { |
| LOG(ERROR) << "Retry timer error: " << ec.message(); |
| } |
| return; |
| } |
| self->attempt_num_++; |
| self->ReinitializeWithRetry(); |
| }); |
| } |
| |
| // Entry point wrapper |
| void IntelCpuSensor::Reinitialize( |
| absl::AnyInvocable<void(absl::Status)> callback) { |
| std::weak_ptr<IntelCpuSensor> weak_self = |
| std::static_pointer_cast<IntelCpuSensor>(shared_from_this()); |
| boost::asio::post( |
| *io_context_, [weak_self, callback = std::move(callback)]() mutable { |
| auto self = weak_self.lock(); |
| if (self == nullptr) { |
| return; |
| } |
| |
| if (self->reinitialize_callback_ != nullptr) { |
| self->retry_timer_.cancel(); |
| absl::AnyInvocable<void(absl::Status)> superseded_callback = |
| std::move(self->reinitialize_callback_); |
| self->reinitialize_callback_ = nullptr; |
| superseded_callback(absl::CancelledError( |
| "Reinitialization superseded by a newer Reinitialize call")); |
| } |
| |
| self->reinitialize_callback_ = std::move(callback); |
| self->attempt_num_ = 0; |
| self->ReinitializeWithRetry(); |
| }); |
| } |
| |
| IntelCpuSensor::IntelCpuSensor( |
| Token token, IntelCpuSensorType sensor_type, |
| const std::string& input_dev_path, const std::string& sensor_label, |
| const HalCommonConfig& hal_common_config, |
| const EntityCommonConfig& entity_common_config, |
| const SensorInstanceProperties& sensor_instance_properties, |
| const double dts_offset, |
| const std::shared_ptr<boost::asio::io_context>& io_context, |
| std::optional<NotificationCb> on_batch_notify, const PeciSysfs& peci_sysfs) |
| : PeciHwmonBasedSensor( |
| input_dev_path, io_context, |
| CreateStaticAttributes(sensor_instance_properties, hal_common_config, |
| entity_common_config), |
| sensor_instance_properties.thresholds(), std::move(on_batch_notify), |
| peci_sysfs), |
| sensor_type_(sensor_type), |
| sensor_label_(sensor_label), |
| dts_offset_(dts_offset), |
| retry_timer_(*io_context) {} |
| |
| IntelCpuSensor::IntelCpuSensor( |
| const PeciSysfs& peci_sysfs, const IntelCpuSensorConfig& config, |
| const std::string& sensor_label, |
| const std::shared_ptr<boost::asio::io_context>& io_context, |
| const std::vector<uint32_t>& retry_delays) |
| : PeciHwmonBasedSensor("", io_context, |
| CreateStaticAttributes(SensorInstanceProperties(), |
| config.hal_common_config(), |
| EntityCommonConfig()), |
| ThresholdConfigs(), std::nullopt, peci_sysfs), |
| retry_delays_(retry_delays), |
| sensor_label_(sensor_label), |
| retry_timer_(*io_context) {} |
| |
| } // namespace milotic_tlbmc |