| #include "tlbmc/sensors/amd_cpu_sensor.h" |
| |
| #include <algorithm> |
| #include <array> |
| #include <cmath> |
| #include <cstdint> |
| #include <fstream> |
| #include <limits> |
| #include <memory> |
| #include <optional> |
| #include <regex> // NOLINT |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/base/no_destructor.h" |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/log/log.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_format.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/strings/substitute.h" |
| #include "absl/synchronization/mutex.h" |
| #include "absl/time/clock.h" |
| #include "absl/time/time.h" |
| #include "boost/filesystem.hpp" // NOLINT |
| #include "g3/macros.h" |
| #include "tlbmc/central_config/config.h" |
| #include "entity_common_config.pb.h" |
| #include "psu_sensor_config.pb.h" |
| #include "sensor_instance_properties.pb.h" |
| #include "tlbmc/hal/sysfs/i3c.h" |
| #include "resource.pb.h" |
| #include "sensor.pb.h" |
| #include "tlbmc/sensors/ixc_hwmon_based_sensor.h" |
| #include "tlbmc/sensors/psu_sensor.h" |
| #include "tlbmc/sensors/sensor.h" |
| #include "tlbmc/time/time.h" |
| |
| namespace milotic_tlbmc { |
| |
| constexpr absl::Duration kApmlDriverReloadInterval = absl::Seconds(5); |
| |
| constexpr auto kSupportedAmdCpuSensorTypes = std::to_array<PsuSensorType>({ |
| // go/keep-sorted start numeric=yes |
| PsuSensorType::PSU_SENSOR_TYPE15_SBTSI_I3C, |
| PsuSensorType::PSU_SENSOR_TYPE22_SBRMI_I3C, |
| // go/keep-sorted end |
| }); |
| |
| absl::StatusOr<std::vector<std::shared_ptr<Sensor>>> AmdCpuSensor::Create( |
| const PsuSensorConfig& config, |
| const std::shared_ptr<boost::asio::io_context>& io_context, |
| I3cSysfs& i3c_sysfs, std::optional<NotificationCb> on_batch_notify, |
| AmdCpuSensorSysfsPaths paths) { |
| DLOG(INFO) << "Creating AmdCpuSensor for device: " << absl::StrCat(config); |
| const HalCommonConfig& hal_common_config = config.hal_common_config(); |
| ECCLESIA_ASSIGN_OR_RETURN(std::string_view driver_name, |
| PsuSensor::GetDriverName(config.type())); |
| |
| std::vector<std::shared_ptr<Sensor>> sensors; |
| |
| auto make_amd_sensor = [&config, &io_context, &on_batch_notify, &i3c_sysfs, |
| &paths](const std::string& label, |
| const std::string& input_dev_path, |
| const SensorInstanceProperties& props) |
| -> absl::StatusOr<std::shared_ptr<AmdCpuSensor>> { |
| ECCLESIA_ASSIGN_OR_RETURN(PsuSensor::ReadingProperties reading_properties, |
| PsuSensor::GetPsuSensorProperties(label)); |
| SensorInstanceProperties mutable_instance_properties = props; |
| mutable_instance_properties.set_unit(reading_properties.sensor_unit); |
| mutable_instance_properties.set_name(PsuSensor::GetPsuSensorKey( |
| config.entity_common_config(), props, reading_properties)); |
| *mutable_instance_properties.mutable_reading_ranges() = |
| PsuSensor::GetReadingRangeConfigs(props.reading_ranges(), |
| reading_properties); |
| if (!props.reading_transform_config().has_scale()) { |
| mutable_instance_properties.mutable_reading_transform_config()->set_scale( |
| std::pow(10, reading_properties.scale)); |
| } |
| if (!props.has_related_item()) { |
| mutable_instance_properties.mutable_related_item()->set_type( |
| RESOURCE_TYPE_BOARD); |
| mutable_instance_properties.mutable_related_item()->set_id( |
| config.entity_common_config().board_config_key()); |
| } |
| if (!props.reading_transform_config().has_offset()) { |
| mutable_instance_properties.mutable_reading_transform_config() |
| ->set_offset(reading_properties.offset); |
| } |
| return std::make_shared<AmdCpuSensor>( |
| Token(), config.type(), input_dev_path, label, |
| config.hal_common_config(), config.entity_common_config(), |
| mutable_instance_properties, io_context, on_batch_notify, i3c_sysfs, |
| paths); |
| }; |
| |
| absl::StatusOr<boost::filesystem::path> hwmon_path; |
| hwmon_path = IXcHwmonBasedSensor::CreateIXcDeviceAndReturnsHwmonPath( |
| hal_common_config, driver_name, i3c_sysfs); |
| if (!hwmon_path.ok()) { |
| if (!GetTlbmcConfig() |
| .sensor_collector_module() |
| .allow_sensor_creation_failure() && |
| config.entity_common_config().config_detected()) { |
| return hwmon_path.status(); |
| } |
| |
| std::string error_message = |
| absl::StrFormat("Failed to create AmdCpuSensor (NONFATAL): %s: %s", |
| absl::StatusCodeToString(hwmon_path.status().code()), |
| hwmon_path.status().message()); |
| for (const auto& [label, properties] : |
| config.label_to_sensor_instance_properties()) { |
| ECCLESIA_ASSIGN_OR_RETURN(std::shared_ptr<AmdCpuSensor> amd_sensor, |
| make_amd_sensor(label, "", properties)); |
| State state; |
| // Initialize with STATUS_STALE so that the sensor is added to the |
| // polling queue when it becomes ready. |
| state.set_status(STATUS_STALE); |
| state.set_status_message(error_message); |
| amd_sensor->UpdateState(std::move(state)); |
| sensors.push_back(amd_sensor); |
| } |
| return sensors; |
| } |
| |
| // Now find all labels and their input files. |
| // Example hwmon folder contents: |
| // SBTSI: |
| // # /sys/bus/i3c/devices/6-118/hwmon/hwmon51/ |
| // device name of_node power subsystem |
| // temp1_input temp1_max temp1_min uevent |
| // SBRMI: |
| // # ls /sys/bus/i3c/devices/6-1118/hwmon/hwmon53/ |
| // device of_node power1_cap power1_input |
| // temp10_input temp12_input temp14_input temp16_input |
| // temp2_input temp4_input temp6_input temp8_input uevent |
| // name power power1_cap_max subsystem |
| // temp11_input temp13_input temp15_input temp1_input |
| // temp3_input temp5_input temp7_input temp9_input |
| absl::flat_hash_map<std::string, std::string> label_to_input_file; |
| for (const auto& entry : boost::filesystem::directory_iterator(*hwmon_path)) { |
| if (entry.is_regular_file()) { |
| std::string input_file = entry.path().filename().string(); |
| PsuSensorFileInfo file_info = PsuSensor::GetPsuSensorFileInfo(input_file); |
| |
| // Ignore sensors that we don't support |
| if (file_info.file_type == PsuSensorFileType::kUnknown) { |
| continue; |
| } |
| |
| std::string label = PsuSensor::ApplyLabelModifications( |
| file_info.file_prefix, file_info.file_type); |
| label_to_input_file[label] = input_file; |
| } |
| } |
| |
| for (const auto& [label, properties] : |
| config.label_to_sensor_instance_properties()) { |
| auto label_to_input_file_it = label_to_input_file.find(label); |
| if (label_to_input_file_it == label_to_input_file.end()) { |
| std::string error_message = absl::Substitute( |
| "Failed to find $0 in hwmon folder $1", label, hwmon_path->string()); |
| if (!GetTlbmcConfig() |
| .sensor_collector_module() |
| .allow_sensor_creation_failure()) { |
| return absl::InvalidArgumentError(error_message); |
| } |
| ECCLESIA_ASSIGN_OR_RETURN(std::shared_ptr<AmdCpuSensor> amd_sensor, |
| make_amd_sensor(label, "", properties)); |
| State state; |
| if (!config.entity_common_config().config_detected()) { |
| state.set_status(STATUS_CREATION_PENDING); |
| } else { |
| state.set_status(STATUS_CREATION_FAILED); |
| } |
| state.set_status_message( |
| absl::StrFormat("Failed to create AmdCpuSensor (NONFATAL): %s: %s", |
| absl::StatusCodeToString(absl::StatusCode::kNotFound), |
| error_message)); |
| amd_sensor->UpdateState(std::move(state)); |
| sensors.push_back(amd_sensor); |
| continue; |
| } |
| const std::string& input_file = label_to_input_file_it->second; |
| boost::filesystem::path input_dev_path = *hwmon_path / input_file; |
| |
| ECCLESIA_ASSIGN_OR_RETURN( |
| std::shared_ptr<AmdCpuSensor> amd_sensor, |
| make_amd_sensor(label, input_dev_path.string(), properties)); |
| sensors.push_back(amd_sensor); |
| } |
| |
| return sensors; |
| } |
| |
| bool AmdCpuSensor::IsAmdCpuSensor(PsuSensorType psu_sensor_type) { |
| const auto* it = |
| std::lower_bound(kSupportedAmdCpuSensorTypes.begin(), |
| kSupportedAmdCpuSensorTypes.end(), psu_sensor_type); |
| return it != kSupportedAmdCpuSensorTypes.end() && (*it == psu_sensor_type); |
| } |
| |
| void AmdCpuSensor::RefreshHwmonDevpath() { |
| absl::StatusOr<std::string_view> driver_name = |
| PsuSensor::GetDriverName(sensor_type_); |
| if (!driver_name.ok()) { |
| LOG(ERROR) << "AmdCpuSensor (" << label_ |
| << ") failed to get driver name: " << driver_name.status(); |
| return; |
| } |
| if (i3c_sysfs_ == nullptr) { |
| LOG(ERROR) << "AmdCpuSensor (" << label_ << ") i3c_sysfs_ is null"; |
| return; |
| } |
| |
| absl::StatusOr<boost::filesystem::path> hwmon = |
| IXcHwmonBasedSensor::CreateIXcDeviceAndReturnsHwmonPath( |
| sensor_attributes_static_.hal_common_config(), *driver_name, |
| *i3c_sysfs_); |
| |
| if (!hwmon.ok()) { |
| LOG(ERROR) << "AmdCpuSensor (" << label_ |
| << ") failed to create/find hwmon path: " << hwmon.status(); |
| return; |
| } |
| |
| LOG(INFO) << "AmdCpuSensor (" << label_ |
| << ") successfully found/created hwmon path: " << hwmon->string(); |
| |
| bool found_match = false; |
| for (const auto& entry : boost::filesystem::directory_iterator(*hwmon)) { |
| if (!entry.is_regular_file()) { |
| continue; |
| } |
| std::string input_file = entry.path().filename().string(); |
| PsuSensorFileInfo file_info = PsuSensor::GetPsuSensorFileInfo(input_file); |
| if (file_info.file_type == PsuSensorFileType::kUnknown) { |
| continue; |
| } |
| |
| std::string file_label = PsuSensor::ApplyLabelModifications( |
| file_info.file_prefix, file_info.file_type); |
| |
| LOG(INFO) << "AmdCpuSensor (" << label_ |
| << "): Check hwmon path: " << hwmon->string() |
| << ", checking file: " << input_file |
| << ", computed label: " << file_label; |
| |
| if (file_label == label_) { |
| std::string new_input_path = (*hwmon / input_file).string(); |
| LOG(INFO) << "AmdCpuSensor (" << label_ |
| << "): Found match! Setting input dev path to: " |
| << new_input_path; |
| SetDevpath(new_input_path).IgnoreError(); |
| SetInputDevicePath(new_input_path); |
| SensorValue initial_value; |
| *initial_value.mutable_timestamp() = Now(); |
| initial_value.set_reading(std::numeric_limits<double>::quiet_NaN()); |
| StoreSensorData( |
| std::make_shared<const SensorValue>(std::move(initial_value))); |
| State state; |
| state.set_status(STATUS_READY); |
| UpdateState(std::move(state)); |
| found_match = true; |
| break; |
| } |
| } |
| |
| if (!found_match) { |
| LOG(ERROR) |
| << "AmdCpuSensor (" << label_ |
| << ") could not find a matching input file for label in hwmon path: " |
| << hwmon->string(); |
| } |
| } |
| |
| void AmdCpuSensor::PowerOffToOnCallback(bool setup_run) { |
| if (setup_run) { |
| ready_for_read_ = true; |
| return; |
| } |
| absl::Status status = |
| ReloadDriver(sensor_attributes_static_.hal_common_config().bus()); |
| if (!status.ok()) { |
| LOG(WARNING) << "Failed to reload APML driver: " << status; |
| } else { |
| RefreshHwmonDevpath(); |
| ready_for_read_ = true; |
| } |
| } |
| |
| void AmdCpuSensor::PowerOnToOffCallback(bool setup_run) { |
| if (setup_run) { |
| ready_for_read_ = false; |
| return; |
| } |
| absl::Status status = |
| UnbindI3cBusDevices(sensor_attributes_static_.hal_common_config().bus()); |
| if (!status.ok()) { |
| LOG(WARNING) << "Failed to unbind APML devices: " << status; |
| } |
| ready_for_read_ = false; |
| } |
| |
| void AmdCpuSensor::OSInactiveToStandbyCallback(bool setup_run) { |
| if (setup_run) { |
| ready_for_read_ = true; |
| return; |
| } |
| absl::Status status = |
| ReloadDriver(sensor_attributes_static_.hal_common_config().bus()); |
| if (!status.ok()) { |
| LOG(WARNING) << "Failed to reload APML driver: " << status; |
| } else { |
| ready_for_read_ = true; |
| } |
| } |
| |
| void AmdCpuSensor::OSStandbyToInactiveCallback(bool setup_run) { |
| if (setup_run) { |
| ready_for_read_ = false; |
| return; |
| } |
| absl::Status status = |
| UnbindI3cBusDevices(sensor_attributes_static_.hal_common_config().bus()); |
| if (!status.ok()) { |
| LOG(WARNING) << "Failed to unbind APML devices: " << status; |
| } |
| ready_for_read_ = false; |
| } |
| |
| AmdCpuSensor::SharedState& AmdCpuSensor::GetSharedState() { |
| static absl::NoDestructor<SharedState> state; |
| return *state; |
| } |
| |
| void AmdCpuSensor::ResetSharedStateForTest() { |
| auto& shared_state = GetSharedState(); |
| absl::MutexLock lock(shared_state.global_mutex); |
| shared_state.last_reload.clear(); |
| shared_state.bus_mutexes.clear(); |
| } |
| |
| absl::Status AmdCpuSensor::ReloadDriver(uint64_t bus) { |
| auto& shared_state = GetSharedState(); |
| absl::Mutex* bus_mutex = nullptr; |
| |
| // Step 1: Check cooldown cache under global lock, and get/create per-bus |
| // mutex. |
| { |
| absl::MutexLock lock(shared_state.global_mutex); |
| auto it = shared_state.last_reload.find(bus); |
| if (it != shared_state.last_reload.end()) { |
| absl::Duration elapsed = absl::Now() - it->second.time; |
| if (elapsed < kApmlDriverReloadInterval) { |
| LOG(INFO) << "Skipping redundant APML driver reload for bus " << bus |
| << " (last reload was " << elapsed << " ago)"; |
| return it->second.status; |
| } |
| } |
| auto mutex_it = shared_state.bus_mutexes.find(bus); |
| if (mutex_it == shared_state.bus_mutexes.end()) { |
| mutex_it = shared_state.bus_mutexes |
| .try_emplace(bus, std::make_unique<absl::Mutex>()) |
| .first; |
| } |
| bus_mutex = mutex_it->second.get(); |
| } |
| |
| // Step 2: Acquire per-bus lock to serialize reload for this specific bus |
| // only. |
| absl::MutexLock bus_lock(*bus_mutex); |
| |
| // Step 3: Re-validate under global lock in case another thread on the same |
| // bus finished while we were waiting for bus_lock. |
| { |
| absl::MutexLock lock(shared_state.global_mutex); |
| auto it = shared_state.last_reload.find(bus); |
| if (it != shared_state.last_reload.end()) { |
| absl::Duration elapsed = absl::Now() - it->second.time; |
| if (elapsed < kApmlDriverReloadInterval) { |
| LOG(INFO) << "Skipping redundant APML driver reload for bus " << bus |
| << " (last reload was " << elapsed << " ago)"; |
| return it->second.status; |
| } |
| } |
| } |
| |
| // Step 4: Perform blocking sysfs reload operations (different buses can run |
| // in parallel). |
| absl::Status status = UnbindI3cBusDevices(bus); |
| if (!status.ok()) { |
| LOG(ERROR) << "Failed to unbind I3C devices on bus " << bus |
| << ". Warning: This may potentially cause a BMC crash. Status: " |
| << status; |
| } |
| if (status.ok()) { |
| status = UnbindI3cBusController(bus); |
| if (!status.ok()) { |
| LOG(ERROR) << "Failed to unbind I3C controller on bus " << bus |
| << ". Status: " << status; |
| } |
| } |
| if (status.ok()) { |
| status = BindI3cBusController(bus); |
| if (!status.ok()) { |
| LOG(ERROR) << "Failed to bind I3C controller on bus " << bus |
| << ". Status: " << status; |
| } |
| } |
| |
| // Step 5: Update cooldown cache under global lock. |
| { |
| absl::MutexLock lock(shared_state.global_mutex); |
| auto it = shared_state.last_reload.find(bus); |
| if (it != shared_state.last_reload.end()) { |
| it->second = {absl::Now(), status}; |
| } else { |
| shared_state.last_reload.try_emplace(bus, absl::Now(), status); |
| } |
| } |
| return status; |
| } |
| |
| absl::Status AmdCpuSensor::UnbindI3cBusDevices(uint64_t bus) { |
| LOG(INFO) << "UnbindI3cBusDevices called for bus " << bus; |
| std::vector<boost::filesystem::path> driver_dirs; |
| |
| boost::system::error_code ec; |
| if (!boost::filesystem::exists(i3c_sysfs_drivers_path_, ec)) { |
| if (ec && ec != boost::system::errc::no_such_file_or_directory) { |
| return absl::InternalError( |
| absl::StrCat("Error checking exists for path '", |
| i3c_sysfs_drivers_path_, "': ", ec.message())); |
| } |
| LOG(INFO) << "I3C driver path does not exist: " << i3c_sysfs_drivers_path_; |
| return absl::OkStatus(); |
| } |
| |
| boost::filesystem::directory_iterator driver_filepath_iterator( |
| i3c_sysfs_drivers_path_, ec); |
| if (ec) { |
| LOG(ERROR) << "Error opening directory '" << i3c_sysfs_drivers_path_ |
| << "': " << ec.message(); |
| } |
| for (const auto& entry : driver_filepath_iterator) { |
| if (boost::filesystem::is_directory(entry.path(), ec)) { |
| driver_dirs.push_back(entry.path()); |
| } |
| if (ec) { |
| LOG(ERROR) << "Error checking is_directory for path '" |
| << entry.path().string() << "': " << ec.message(); |
| break; |
| } |
| } |
| |
| std::string device_pattern = absl::StrCat(bus, "-[0-9a-f]+"); |
| std::regex re(device_pattern); |
| |
| for (const auto& driver_dir : driver_dirs) { |
| std::vector<std::string> devices_to_unbind; |
| boost::filesystem::path unbind_file_path = driver_dir / "unbind"; |
| |
| DLOG(INFO) << "Searching devices under: " << driver_dir.string(); |
| boost::filesystem::directory_iterator device_filepath_iterator(driver_dir, |
| ec); |
| if (ec) { |
| LOG(ERROR) << "Error opening directory '" << driver_dir.string() |
| << "': " << ec.message(); |
| } |
| for (const auto& entry : device_filepath_iterator) { |
| std::string name = entry.path().filename().string(); |
| if (std::regex_match(name, re)) { |
| devices_to_unbind.push_back(name); |
| DLOG(INFO) << "Matched device: " << name; |
| } |
| } |
| |
| if (devices_to_unbind.empty()) { |
| DLOG(INFO) << "No i3c" << bus << " device found in " |
| << driver_dir.filename().string(); |
| continue; |
| } |
| |
| for (const auto& device : devices_to_unbind) { |
| std::ofstream unbind_file(unbind_file_path.string()); |
| if (!unbind_file.good()) { |
| return absl::InternalError( |
| absl::StrCat("Failed to open ", unbind_file_path.string())); |
| } |
| |
| unbind_file << device << "\n"; |
| unbind_file.flush(); |
| if (!unbind_file.good()) { |
| return absl::InternalError( |
| absl::StrCat("Failed to write to ", unbind_file_path.string())); |
| } |
| unbind_file.close(); |
| |
| LOG(INFO) << "Unbound device " << device << " from driver " |
| << driver_dir.filename().string(); |
| } |
| |
| absl::SleepFor(absl::Milliseconds(100)); |
| boost::filesystem::directory_iterator remaining_device_filepath_iterator( |
| driver_dir, ec); |
| if (ec) { |
| LOG(ERROR) << "Error opening directory '" << driver_dir.string() |
| << "': " << ec.message(); |
| } |
| for (const auto& entry : remaining_device_filepath_iterator) { |
| std::string remain = entry.path().filename().string(); |
| if (std::regex_match(remain, re)) { |
| LOG(ERROR) << "Device " << remain << " on I3C bus " << bus |
| << " still remains under driver " |
| << driver_dir.filename().string(); |
| return absl::InternalError("Failed to unbind all devices"); |
| } |
| } |
| } |
| |
| LOG(INFO) << "UnbindI3cBusDevices succeeded for bus " << bus; |
| return absl::OkStatus(); |
| } |
| |
| absl::Status AmdCpuSensor::UnbindI3cBusController(uint64_t bus) { |
| absl::MutexLock lock(controller_mutex_); |
| // If the controller for this bus is already recorded as unbound, return OK. |
| auto it = unbound_i3c_controllers_.find(bus); |
| if (it != unbound_i3c_controllers_.end() && !it->second.empty()) { |
| LOG(INFO) << "Controller for I3C bus " << bus << " is already unbound."; |
| return absl::OkStatus(); |
| } |
| |
| std::string controller; |
| std::string controller_pattern = absl::StrCat("[0-9a-f]+\\.i3c", bus); |
| std::regex re(controller_pattern); |
| |
| DLOG(INFO) << "Searching controller under: " << i3c_controller_driver_path_; |
| boost::system::error_code ec; |
| if (!boost::filesystem::exists(i3c_controller_driver_path_, ec)) { |
| if (ec && ec != boost::system::errc::no_such_file_or_directory) { |
| return absl::InternalError( |
| absl::StrCat("Error checking exists for path '", |
| i3c_controller_driver_path_, "': ", ec.message())); |
| } |
| LOG(INFO) << "I3C controller driver path does not exist: " |
| << i3c_controller_driver_path_; |
| return absl::OkStatus(); |
| } |
| |
| boost::filesystem::directory_iterator controller_filepath_iterator( |
| i3c_controller_driver_path_, ec); |
| if (ec) { |
| LOG(ERROR) << "Error opening directory '" << i3c_controller_driver_path_ |
| << "': " << ec.message(); |
| } |
| for (const auto& p : controller_filepath_iterator) { |
| std::string name = p.path().filename().string(); |
| |
| if (std::regex_match(name, re)) { |
| controller = name; |
| DLOG(INFO) << "Matched controller: " << controller; |
| break; |
| } |
| } |
| |
| if (controller.empty()) { |
| return absl::NotFoundError( |
| absl::StrCat("No controller found for I3C bus ", bus)); |
| } |
| |
| boost::filesystem::path unbind_file_path = |
| absl::StrCat(i3c_controller_driver_path_, "unbind"); |
| |
| std::ofstream unbind_file(unbind_file_path.string()); |
| if (!unbind_file.good()) { |
| return absl::InternalError(absl::StrCat("Failed to open unbind file: ", |
| unbind_file_path.string())); |
| } |
| |
| unbind_file << controller << "\n"; |
| unbind_file.flush(); |
| if (!unbind_file.good()) { |
| return absl::InternalError(absl::StrCat("Failed to write to unbind file: ", |
| unbind_file_path.string())); |
| } |
| unbind_file.close(); |
| unbound_i3c_controllers_[bus] = {controller}; |
| |
| LOG(INFO) << "Unbound controller " << controller; |
| |
| absl::SleepFor(absl::Milliseconds(100)); |
| |
| boost::filesystem::directory_iterator remaining_controller_filepath_iterator( |
| i3c_controller_driver_path_, ec); |
| if (ec) { |
| LOG(ERROR) << "Error opening directory '" << i3c_controller_driver_path_ |
| << "': " << ec.message(); |
| } |
| for (const auto& p : remaining_controller_filepath_iterator) { |
| std::string remain = p.path().filename().string(); |
| if (std::regex_match(remain, re)) { |
| LOG(ERROR) << "I3C Controller " << remain << " on bus " << bus |
| << " failed to unbind from " << i3c_controller_driver_path_; |
| return absl::InternalError("Failed to unbind controller"); |
| } |
| } |
| |
| return absl::OkStatus(); |
| } |
| |
| absl::Status AmdCpuSensor::BindI3cBusController(uint64_t bus) { |
| std::vector<std::string> controllers; |
| absl::MutexLock lock(controller_mutex_); |
| auto it = unbound_i3c_controllers_.find(bus); |
| if (it == unbound_i3c_controllers_.end() || it->second.empty()) { |
| return absl::NotFoundError( |
| absl::StrCat("No unbound controllers recorded for bus ", bus)); |
| } |
| controllers = it->second; |
| |
| boost::filesystem::path bind_file_path = |
| absl::StrCat(i3c_controller_driver_path_, "bind"); |
| |
| for (const auto& controller : controllers) { |
| std::ofstream bind_file(bind_file_path.string()); |
| if (!bind_file.good()) { |
| return absl::InternalError( |
| absl::StrCat("Failed to open bind file for controller ", controller)); |
| } |
| |
| bind_file << controller << "\n"; |
| bind_file.flush(); |
| if (!bind_file.good()) { |
| return absl::InternalError(absl::StrCat( |
| "Failed to write to bind file for controller ", controller)); |
| } |
| bind_file.close(); |
| |
| LOG(INFO) << "Bound controller " << controller << " for bus " << bus; |
| } |
| |
| unbound_i3c_controllers_.erase(bus); |
| |
| absl::SleepFor(absl::Milliseconds(100)); |
| |
| return absl::OkStatus(); |
| } |
| |
| } // namespace milotic_tlbmc |