/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define ATRACE_TAG (ATRACE_TAG_THERMAL | ATRACE_TAG_HAL) #include "thermal_throttling.h" #include #include #include #include #include #include #include #include #include #include #include #include "power_files.h" #include "thermal_info.h" namespace aidl { namespace android { namespace hardware { namespace thermal { namespace implementation { using ::android::base::StringPrintf; // To find the next PID target state according to the current thermal severity size_t getTargetStateOfPID(const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity) { size_t target_state = 0; for (const auto &severity : ::ndk::enum_range()) { size_t state = static_cast(severity); if (std::isnan(sensor_info.throttling_info->s_power[state])) { continue; } target_state = state; if (severity > curr_severity) { break; } } LOG(VERBOSE) << "PID target state = " << target_state; return target_state; } void ThermalThrottling::parseProfileProperty(std::string_view sensor_name, const SensorInfo &sensor_info) { if (sensor_info.throttling_info == nullptr) { return; } const std::string profile = ::android::base::GetProperty( StringPrintf("vendor.thermal.%s.profile", sensor_name.data()), ""); if (profile.empty() || sensor_info.throttling_info->profile_map.count(profile)) { if (profile != thermal_throttling_status_map_[sensor_name.data()].profile) { LOG(INFO) << sensor_name.data() << ": throttling profile change to " << ((profile.empty()) ? "default" : profile); thermal_throttling_status_map_[sensor_name.data()].profile = profile; } } else { LOG(ERROR) << sensor_name.data() << ": set profile to default because " << profile << " is invalid"; thermal_throttling_status_map_[sensor_name.data()].profile = ""; } } void ThermalThrottling::clearThrottlingData(std::string_view sensor_name) { if (!thermal_throttling_status_map_.count(sensor_name.data())) { return; } std::unique_lock _lock(thermal_throttling_status_map_mutex_); for (auto &pid_power_budget_pair : thermal_throttling_status_map_.at(sensor_name.data()).pid_power_budget_map) { pid_power_budget_pair.second = std::numeric_limits::max(); } for (auto &pid_cdev_request_pair : thermal_throttling_status_map_.at(sensor_name.data()).pid_cdev_request_map) { pid_cdev_request_pair.second = 0; } for (auto &hardlimit_cdev_request_pair : thermal_throttling_status_map_.at(sensor_name.data()).hardlimit_cdev_request_map) { hardlimit_cdev_request_pair.second = 0; } for (auto &throttling_release_pair : thermal_throttling_status_map_.at(sensor_name.data()).throttling_release_map) { throttling_release_pair.second = 0; } thermal_throttling_status_map_[sensor_name.data()].prev_err = NAN; thermal_throttling_status_map_[sensor_name.data()].i_budget = NAN; thermal_throttling_status_map_[sensor_name.data()].prev_target = static_cast(ThrottlingSeverity::NONE); thermal_throttling_status_map_[sensor_name.data()].prev_power_budget = NAN; thermal_throttling_status_map_[sensor_name.data()].tran_cycle = 0; return; } bool ThermalThrottling::registerThermalThrottling( std::string_view sensor_name, const std::shared_ptr &throttling_info, const std::unordered_map &cooling_device_info_map) { if (thermal_throttling_status_map_.count(sensor_name.data())) { LOG(ERROR) << "Sensor " << sensor_name.data() << " throttling map has been registered"; return false; } if (throttling_info == nullptr) { LOG(ERROR) << "Sensor " << sensor_name.data() << " has no throttling info"; return false; } thermal_throttling_status_map_[sensor_name.data()].prev_err = NAN; thermal_throttling_status_map_[sensor_name.data()].i_budget = NAN; thermal_throttling_status_map_[sensor_name.data()].prev_target = static_cast(ThrottlingSeverity::NONE); thermal_throttling_status_map_[sensor_name.data()].prev_power_budget = NAN; thermal_throttling_status_map_[sensor_name.data()].tran_cycle = 0; thermal_throttling_status_map_[sensor_name.data()].profile = ""; for (auto &binded_cdev_pair : throttling_info->binded_cdev_info_map) { if (!cooling_device_info_map.count(binded_cdev_pair.first)) { LOG(ERROR) << "Could not find " << sensor_name.data() << "'s binded CDEV " << binded_cdev_pair.first; return false; } // Register PID throttling map for (const auto &cdev_weight : binded_cdev_pair.second.cdev_weight_for_pid) { if (!std::isnan(cdev_weight)) { thermal_throttling_status_map_[sensor_name.data()] .pid_power_budget_map[binded_cdev_pair.first] = std::numeric_limits::max(); thermal_throttling_status_map_[sensor_name.data()] .pid_cdev_request_map[binded_cdev_pair.first] = 0; thermal_throttling_status_map_[sensor_name.data()] .cdev_status_map[binded_cdev_pair.first] = 0; cdev_all_request_map_[binded_cdev_pair.first].insert(0); break; } } // Register hard limit throttling map for (const auto &limit_info : binded_cdev_pair.second.limit_info) { if (limit_info > 0) { thermal_throttling_status_map_[sensor_name.data()] .hardlimit_cdev_request_map[binded_cdev_pair.first] = 0; thermal_throttling_status_map_[sensor_name.data()] .cdev_status_map[binded_cdev_pair.first] = 0; cdev_all_request_map_[binded_cdev_pair.first].insert(0); break; } } // Register throttling release map if power threshold exists if (!binded_cdev_pair.second.power_rail.empty()) { for (const auto &power_threshold : binded_cdev_pair.second.power_thresholds) { if (!std::isnan(power_threshold)) { thermal_throttling_status_map_[sensor_name.data()] .throttling_release_map[binded_cdev_pair.first] = 0; break; } } } } return true; } // return power budget based on PID algo float ThermalThrottling::updatePowerBudget( const Temperature &temp, const SensorInfo &sensor_info, const std::unordered_map &cooling_device_info_map, std::chrono::milliseconds time_elapsed_ms, ThrottlingSeverity curr_severity, const bool max_throttling, const std::vector &sensor_predictions) { float p = 0, d = 0; float power_budget = std::numeric_limits::max(); bool target_changed = false; float budget_transient = 0.0; auto &throttling_status = thermal_throttling_status_map_.at(temp.name); std::string sensor_name = temp.name; if (curr_severity == ThrottlingSeverity::NONE) { return power_budget; } const auto target_state = getTargetStateOfPID(sensor_info, curr_severity); if (throttling_status.prev_target != static_cast(ThrottlingSeverity::NONE) && target_state != throttling_status.prev_target && sensor_info.throttling_info->tran_cycle > 0) { throttling_status.tran_cycle = sensor_info.throttling_info->tran_cycle - 1; target_changed = true; } throttling_status.prev_target = target_state; // Compute PID float target = sensor_info.hot_thresholds[target_state]; float err = target - temp.value; if (max_throttling && err <= 0) { return sensor_info.throttling_info->min_alloc_power[target_state]; } p = err * (err < 0 ? sensor_info.throttling_info->k_po[target_state] : sensor_info.throttling_info->k_pu[target_state]); if (std::isnan(throttling_status.i_budget)) { if (std::isnan(sensor_info.throttling_info->i_default_pct)) { throttling_status.i_budget = sensor_info.throttling_info->i_default; } else { float default_i_budget = 0.0; for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) { int max_cdev_vote; const CdevInfo &cdev_info = cooling_device_info_map.at(binded_cdev_info_pair.first); max_cdev_vote = getCdevMaxRequest(binded_cdev_info_pair.first, &max_cdev_vote); default_i_budget += cdev_info.state2power[max_cdev_vote]; } throttling_status.i_budget = default_i_budget * sensor_info.throttling_info->i_default_pct / 100; } } if (err < sensor_info.throttling_info->i_cutoff[target_state]) { throttling_status.i_budget += err * sensor_info.throttling_info->k_i[target_state]; } if (fabsf(throttling_status.i_budget) > sensor_info.throttling_info->i_max[target_state]) { throttling_status.i_budget = sensor_info.throttling_info->i_max[target_state] * (throttling_status.i_budget > 0 ? 1 : -1); } if (!std::isnan(throttling_status.prev_err) && time_elapsed_ms != std::chrono::milliseconds::zero()) { d = sensor_info.throttling_info->k_d[target_state] * (err - throttling_status.prev_err) / time_elapsed_ms.count(); } // Compute Compensation float compensation = 0; if (sensor_info.predictor_info != nullptr && sensor_info.predictor_info->support_pid_compensation) { const std::vector &prediction_weights = sensor_info.predictor_info->prediction_weights; for (size_t i = 0; i < sensor_predictions.size(); ++i) { float prediction_err = target - (sensor_predictions[i] * sensor_info.multiplier); compensation += prediction_weights[i] * prediction_err; } // apply weight based on current severity level compensation *= sensor_info.predictor_info->k_p_compensate[target_state]; } throttling_status.prev_err = err; // Calculate power budget power_budget = sensor_info.throttling_info->s_power[target_state] + p + throttling_status.i_budget + d + compensation; if (power_budget < sensor_info.throttling_info->min_alloc_power[target_state]) { power_budget = sensor_info.throttling_info->min_alloc_power[target_state]; } if (power_budget > sensor_info.throttling_info->max_alloc_power[target_state]) { power_budget = sensor_info.throttling_info->max_alloc_power[target_state]; } if (target_changed) { throttling_status.budget_transient = throttling_status.prev_power_budget - power_budget; } if (throttling_status.tran_cycle) { budget_transient = throttling_status.budget_transient * ((static_cast(throttling_status.tran_cycle) / static_cast(sensor_info.throttling_info->tran_cycle))); power_budget += budget_transient; throttling_status.tran_cycle--; } LOG(INFO) << temp.name << " power_budget=" << power_budget << " err=" << err << " s_power=" << sensor_info.throttling_info->s_power[target_state] << " time_elapsed_ms=" << time_elapsed_ms.count() << " p=" << p << " i=" << throttling_status.i_budget << " d=" << d << " compensation=" << compensation << " budget transient=" << budget_transient << " control target=" << target_state; ATRACE_INT((sensor_name + std::string("-power_budget")).c_str(), static_cast(power_budget)); ATRACE_INT((sensor_name + std::string("-s_power")).c_str(), static_cast(sensor_info.throttling_info->s_power[target_state])); ATRACE_INT((sensor_name + std::string("-time_elapsed_ms")).c_str(), static_cast(time_elapsed_ms.count())); ATRACE_INT((sensor_name + std::string("-budget_transient")).c_str(), static_cast(budget_transient)); ATRACE_INT((sensor_name + std::string("-i")).c_str(), static_cast(throttling_status.i_budget)); ATRACE_INT((sensor_name + std::string("-target_state")).c_str(), static_cast(target_state)); ATRACE_INT((sensor_name + std::string("-err")).c_str(), static_cast(err / sensor_info.multiplier)); ATRACE_INT((sensor_name + std::string("-p")).c_str(), static_cast(p)); ATRACE_INT((sensor_name + std::string("-d")).c_str(), static_cast(d)); ATRACE_INT((sensor_name + std::string("-predict_compensation")).c_str(), static_cast(compensation)); ATRACE_INT((sensor_name + std::string("-temp")).c_str(), static_cast(temp.value / sensor_info.multiplier)); throttling_status.prev_power_budget = power_budget; return power_budget; } float ThermalThrottling::computeExcludedPower( const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity, const std::unordered_map &power_status_map, std::string *log_buf, std::string_view sensor_name) { float excluded_power = 0.0; for (const auto &excluded_power_info_pair : sensor_info.throttling_info->excluded_power_info_map) { const auto last_updated_avg_power = power_status_map.at(excluded_power_info_pair.first).last_updated_avg_power; if (!std::isnan(last_updated_avg_power)) { excluded_power += last_updated_avg_power * excluded_power_info_pair.second[static_cast(curr_severity)]; log_buf->append(StringPrintf( "(%s: %0.2f mW, cdev_weight: %f)", excluded_power_info_pair.first.c_str(), last_updated_avg_power, excluded_power_info_pair.second[static_cast(curr_severity)])); ATRACE_INT((std::string(sensor_name) + std::string("-") + excluded_power_info_pair.first + std::string("-avg_power")) .c_str(), static_cast(last_updated_avg_power)); } } ATRACE_INT((std::string(sensor_name) + std::string("-excluded_power")).c_str(), static_cast(excluded_power)); return excluded_power; } // Allocate power budget to binded cooling devices base on the real ODPM power data bool ThermalThrottling::allocatePowerToCdev( const Temperature &temp, const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms, const std::unordered_map &power_status_map, const std::unordered_map &cooling_device_info_map, const bool max_throttling, const std::vector &sensor_predictions) { float total_weight = 0; float last_updated_avg_power = NAN; float allocated_power = 0; float allocated_weight = 0; bool low_power_device_check = true; bool is_budget_allocated = false; bool power_data_invalid = false; std::set allocated_cdev; std::string log_buf; std::unique_lock _lock(thermal_throttling_status_map_mutex_); auto total_power_budget = updatePowerBudget(temp, sensor_info, cooling_device_info_map, time_elapsed_ms, curr_severity, max_throttling, sensor_predictions); const auto &profile = thermal_throttling_status_map_[temp.name].profile; if (sensor_info.throttling_info->excluded_power_info_map.size()) { total_power_budget -= computeExcludedPower(sensor_info, curr_severity, power_status_map, &log_buf, temp.name); total_power_budget = std::max(total_power_budget, 0.0f); if (!log_buf.empty()) { LOG(INFO) << temp.name << " power budget=" << total_power_budget << " after " << log_buf << " is excluded"; } } // Compute total cdev weight for (const auto &binded_cdev_info_pair : (sensor_info.throttling_info->profile_map.count(profile) ? sensor_info.throttling_info->profile_map.at(profile) : sensor_info.throttling_info->binded_cdev_info_map)) { const auto cdev_weight = binded_cdev_info_pair.second .cdev_weight_for_pid[static_cast(curr_severity)]; if (!binded_cdev_info_pair.second.enabled) { continue; } else if (std::isnan(cdev_weight) || cdev_weight == 0) { allocated_cdev.insert(binded_cdev_info_pair.first); continue; } total_weight += cdev_weight; } while (!is_budget_allocated) { for (const auto &binded_cdev_info_pair : (sensor_info.throttling_info->profile_map.count(profile) ? sensor_info.throttling_info->profile_map.at(profile) : sensor_info.throttling_info->binded_cdev_info_map)) { float cdev_power_adjustment = 0; const auto cdev_weight = binded_cdev_info_pair.second .cdev_weight_for_pid[static_cast(curr_severity)]; if (allocated_cdev.count(binded_cdev_info_pair.first)) { continue; } // Get the power data if (!power_data_invalid) { if (!binded_cdev_info_pair.second.power_rail.empty()) { last_updated_avg_power = power_status_map.at(binded_cdev_info_pair.second.power_rail) .last_updated_avg_power; if (std::isnan(last_updated_avg_power)) { LOG(VERBOSE) << "power data is under collecting"; power_data_invalid = true; break; } ATRACE_INT((temp.name + std::string("-") + binded_cdev_info_pair.second.power_rail + std::string("-avg_power")) .c_str(), static_cast(last_updated_avg_power)); } else { power_data_invalid = true; break; } if (binded_cdev_info_pair.second.throttling_with_power_link) { return false; } } auto cdev_power_budget = total_power_budget * (cdev_weight / total_weight); cdev_power_adjustment = cdev_power_budget - last_updated_avg_power; if (low_power_device_check) { // Share the budget for the CDEV which power is lower than target if (cdev_power_adjustment > 0 && thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at( binded_cdev_info_pair.first) == 0) { allocated_power += last_updated_avg_power; allocated_weight += cdev_weight; allocated_cdev.insert(binded_cdev_info_pair.first); if (!binded_cdev_info_pair.second.power_rail.empty()) { log_buf.append(StringPrintf("(%s: %0.2f mW)", binded_cdev_info_pair.second.power_rail.c_str(), last_updated_avg_power)); } LOG(VERBOSE) << temp.name << " binded " << binded_cdev_info_pair.first << " has been already at min state 0"; } } else { const CdevInfo &cdev_info = cooling_device_info_map.at(binded_cdev_info_pair.first); if (!binded_cdev_info_pair.second.power_rail.empty()) { log_buf.append(StringPrintf("(%s: %0.2f mW)", binded_cdev_info_pair.second.power_rail.c_str(), last_updated_avg_power)); } // Ignore the power distribution if the CDEV has no space to reduce power if ((cdev_power_adjustment < 0 && thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at( binded_cdev_info_pair.first) == cdev_info.max_state)) { LOG(VERBOSE) << temp.name << " binded " << binded_cdev_info_pair.first << " has been already at max state " << cdev_info.max_state; continue; } if (!binded_cdev_info_pair.second.enabled) { cdev_power_budget = cdev_info.state2power[0]; } else if (!power_data_invalid && binded_cdev_info_pair.second.power_rail != "") { auto cdev_curr_power_budget = thermal_throttling_status_map_[temp.name].pid_power_budget_map.at( binded_cdev_info_pair.first); if (last_updated_avg_power > cdev_curr_power_budget) { cdev_power_budget = cdev_curr_power_budget += (cdev_power_adjustment * (cdev_curr_power_budget / last_updated_avg_power)); } else { cdev_power_budget = cdev_curr_power_budget += cdev_power_adjustment; } } else { cdev_power_budget = total_power_budget * (cdev_weight / total_weight); } if (!std::isnan(cdev_info.state2power[0]) && cdev_power_budget > cdev_info.state2power[0]) { cdev_power_budget = cdev_info.state2power[0]; } else if (cdev_power_budget < 0) { cdev_power_budget = 0; } int max_cdev_vote; if (!getCdevMaxRequest(binded_cdev_info_pair.first, &max_cdev_vote)) { return false; } const auto curr_cdev_vote = thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at( binded_cdev_info_pair.first); if (!max_throttling) { if (binded_cdev_info_pair.second.max_release_step != std::numeric_limits::max() && (power_data_invalid || cdev_power_adjustment > 0)) { if (!power_data_invalid && curr_cdev_vote < max_cdev_vote) { cdev_power_budget = cdev_info.state2power[curr_cdev_vote]; LOG(VERBOSE) << temp.name << "'s " << binded_cdev_info_pair.first << " vote: " << curr_cdev_vote << " is lower than max cdev vote: " << max_cdev_vote; } else { int target_release_step = binded_cdev_info_pair.second.max_release_step; while ((curr_cdev_vote - target_release_step) > binded_cdev_info_pair.second .limit_info[static_cast( curr_severity)] && cdev_info.state2power[curr_cdev_vote - target_release_step] == cdev_info.state2power[curr_cdev_vote]) { target_release_step += 1; } const auto target_state = std::max(curr_cdev_vote - target_release_step, 0); cdev_power_budget = std::min(cdev_power_budget, cdev_info.state2power[target_state]); } } if (binded_cdev_info_pair.second.max_throttle_step != std::numeric_limits::max() && (power_data_invalid || cdev_power_adjustment < 0)) { int target_throttle_step = binded_cdev_info_pair.second.max_throttle_step; while ((curr_cdev_vote + target_throttle_step) < binded_cdev_info_pair.second .cdev_ceiling[static_cast(curr_severity)] && cdev_info.state2power[curr_cdev_vote + target_throttle_step] == cdev_info.state2power[curr_cdev_vote]) { target_throttle_step += 1; } const auto target_state = std::min(curr_cdev_vote + target_throttle_step, binded_cdev_info_pair.second .cdev_ceiling[static_cast(curr_severity)]); cdev_power_budget = std::max(cdev_power_budget, cdev_info.state2power[target_state]); } } thermal_throttling_status_map_[temp.name].pid_power_budget_map.at( binded_cdev_info_pair.first) = cdev_power_budget; LOG(VERBOSE) << temp.name << " allocate " << thermal_throttling_status_map_[temp.name].pid_power_budget_map.at( binded_cdev_info_pair.first) << "mW to " << binded_cdev_info_pair.first << "(cdev_weight=" << cdev_weight << ")"; } } if (!power_data_invalid) { total_power_budget -= allocated_power; total_weight -= allocated_weight; } allocated_power = 0; allocated_weight = 0; if (low_power_device_check) { low_power_device_check = false; } else { is_budget_allocated = true; } } if (log_buf.size()) { LOG(INFO) << temp.name << " binded power rails: " << log_buf; } return true; } void ThermalThrottling::updateCdevRequestByPower( std::string sensor_name, const std::unordered_map &cooling_device_info_map) { size_t i; std::unique_lock _lock(thermal_throttling_status_map_mutex_); for (auto &pid_power_budget_pair : thermal_throttling_status_map_[sensor_name.data()].pid_power_budget_map) { const CdevInfo &cdev_info = cooling_device_info_map.at(pid_power_budget_pair.first); for (i = 0; i < cdev_info.state2power.size() - 1; ++i) { if (pid_power_budget_pair.second >= cdev_info.state2power[i]) { break; } } thermal_throttling_status_map_[sensor_name.data()].pid_cdev_request_map.at( pid_power_budget_pair.first) = static_cast(i); } return; } void ThermalThrottling::updateCdevRequestBySeverity(std::string_view sensor_name, const SensorInfo &sensor_info, ThrottlingSeverity curr_severity) { std::unique_lock _lock(thermal_throttling_status_map_mutex_); const auto &profile = thermal_throttling_status_map_[sensor_name.data()].profile; for (const auto &binded_cdev_info_pair : (sensor_info.throttling_info->profile_map.count(profile) ? sensor_info.throttling_info->profile_map.at(profile) : sensor_info.throttling_info->binded_cdev_info_map)) { if (!thermal_throttling_status_map_[sensor_name.data()].hardlimit_cdev_request_map.count( binded_cdev_info_pair.first)) { continue; } thermal_throttling_status_map_[sensor_name.data()].hardlimit_cdev_request_map.at( binded_cdev_info_pair.first) = (binded_cdev_info_pair.second.enabled) ? binded_cdev_info_pair.second .limit_info[static_cast(curr_severity)] : 0; LOG(VERBOSE) << "Hard Limit: Sensor " << sensor_name.data() << " update cdev " << binded_cdev_info_pair.first << " to " << thermal_throttling_status_map_[sensor_name.data()] .hardlimit_cdev_request_map.at(binded_cdev_info_pair.first); } } bool ThermalThrottling::throttlingReleaseUpdate( std::string_view sensor_name, const std::unordered_map &cooling_device_info_map, const std::unordered_map &power_status_map, const ThrottlingSeverity severity, const SensorInfo &sensor_info) { ATRACE_CALL(); std::unique_lock _lock(thermal_throttling_status_map_mutex_); if (!thermal_throttling_status_map_.count(sensor_name.data())) { return false; } auto &thermal_throttling_status = thermal_throttling_status_map_.at(sensor_name.data()); for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) { float avg_power = -1; if (!thermal_throttling_status.throttling_release_map.count(binded_cdev_info_pair.first) || !power_status_map.count(binded_cdev_info_pair.second.power_rail)) { return false; } const auto max_state = cooling_device_info_map.at(binded_cdev_info_pair.first).max_state; auto &release_step = thermal_throttling_status.throttling_release_map.at(binded_cdev_info_pair.first); avg_power = power_status_map.at(binded_cdev_info_pair.second.power_rail).last_updated_avg_power; if (std::isnan(avg_power) || avg_power < 0) { release_step = binded_cdev_info_pair.second.throttling_with_power_link ? max_state : 0; continue; } bool is_over_budget = true; if (!binded_cdev_info_pair.second.high_power_check) { if (avg_power < binded_cdev_info_pair.second.power_thresholds[static_cast(severity)]) { is_over_budget = false; } } else { if (avg_power > binded_cdev_info_pair.second.power_thresholds[static_cast(severity)]) { is_over_budget = false; } } LOG(INFO) << sensor_name.data() << "'s " << binded_cdev_info_pair.first << " binded power rail " << binded_cdev_info_pair.second.power_rail << ": power threshold = " << binded_cdev_info_pair.second.power_thresholds[static_cast(severity)] << ", avg power = " << avg_power; std::string atrace_prefix = ::android::base::StringPrintf( "%s-%s", sensor_name.data(), binded_cdev_info_pair.second.power_rail.data()); ATRACE_INT( (atrace_prefix + std::string("-power_threshold")).c_str(), static_cast( binded_cdev_info_pair.second.power_thresholds[static_cast(severity)])); ATRACE_INT((atrace_prefix + std::string("-avg_power")).c_str(), avg_power); switch (binded_cdev_info_pair.second.release_logic) { case ReleaseLogic::INCREASE: if (!is_over_budget) { if (std::abs(release_step) < static_cast(max_state)) { release_step--; } } else { release_step = 0; } break; case ReleaseLogic::DECREASE: if (!is_over_budget) { if (release_step < static_cast(max_state)) { release_step++; } } else { release_step = 0; } break; case ReleaseLogic::STEPWISE: if (!is_over_budget) { if (release_step < static_cast(max_state)) { release_step++; } } else { if (std::abs(release_step) < static_cast(max_state)) { release_step--; } } break; case ReleaseLogic::RELEASE_TO_FLOOR: release_step = is_over_budget ? 0 : max_state; break; case ReleaseLogic::NONE: default: break; } } return true; } void ThermalThrottling::thermalThrottlingUpdate( const Temperature &temp, const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms, const std::unordered_map &power_status_map, const std::unordered_map &cooling_device_info_map, const bool max_throttling, const std::vector &sensor_predictions) { if (!thermal_throttling_status_map_.count(temp.name)) { return; } if (sensor_info.throttling_info->profile_map.size()) { parseProfileProperty(temp.name.c_str(), sensor_info); } if (thermal_throttling_status_map_[temp.name].pid_power_budget_map.size()) { if (!allocatePowerToCdev(temp, sensor_info, curr_severity, time_elapsed_ms, power_status_map, cooling_device_info_map, max_throttling, sensor_predictions)) { LOG(ERROR) << "Sensor " << temp.name << " PID request cdev failed"; // Clear the CDEV request if the power budget is failed to be allocated for (auto &pid_cdev_request_pair : thermal_throttling_status_map_[temp.name].pid_cdev_request_map) { pid_cdev_request_pair.second = 0; } } updateCdevRequestByPower(temp.name, cooling_device_info_map); } if (thermal_throttling_status_map_[temp.name].hardlimit_cdev_request_map.size()) { updateCdevRequestBySeverity(temp.name.c_str(), sensor_info, curr_severity); } if (thermal_throttling_status_map_[temp.name].throttling_release_map.size()) { throttlingReleaseUpdate(temp.name.c_str(), cooling_device_info_map, power_status_map, curr_severity, sensor_info); } } void ThermalThrottling::computeCoolingDevicesRequest( std::string_view sensor_name, const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity, std::vector *cooling_devices_to_update, ThermalStatsHelper *thermal_stats_helper) { int release_step = 0; std::unique_lock _lock(thermal_throttling_status_map_mutex_); if (!thermal_throttling_status_map_.count(sensor_name.data())) { return; } auto &thermal_throttling_status = thermal_throttling_status_map_.at(sensor_name.data()); const auto &cdev_release_map = thermal_throttling_status.throttling_release_map; const auto &profile = thermal_throttling_status_map_[sensor_name.data()].profile; const auto &binded_cdev_info_map = sensor_info.throttling_info->profile_map.count(profile) ? sensor_info.throttling_info->profile_map.at(profile) : sensor_info.throttling_info->binded_cdev_info_map; for (auto &cdev_request_pair : thermal_throttling_status.cdev_status_map) { int pid_cdev_request = 0; int hardlimit_cdev_request = 0; const auto &cdev_name = cdev_request_pair.first; const auto &binded_cdev_info = binded_cdev_info_map.at(cdev_name); const auto cdev_ceiling = binded_cdev_info.cdev_ceiling[static_cast(curr_severity)]; const auto cdev_floor = binded_cdev_info.cdev_floor_with_power_link[static_cast(curr_severity)]; release_step = 0; if (thermal_throttling_status.pid_cdev_request_map.count(cdev_name)) { pid_cdev_request = thermal_throttling_status.pid_cdev_request_map.at(cdev_name); } if (thermal_throttling_status.hardlimit_cdev_request_map.count(cdev_name)) { hardlimit_cdev_request = thermal_throttling_status.hardlimit_cdev_request_map.at(cdev_name); } if (cdev_release_map.count(cdev_name)) { release_step = cdev_release_map.at(cdev_name); } LOG(VERBOSE) << sensor_name.data() << " binded cooling device " << cdev_name << "'s pid_request=" << pid_cdev_request << " hardlimit_cdev_request=" << hardlimit_cdev_request << " release_step=" << release_step << " cdev_floor_with_power_link=" << cdev_floor << " cdev_ceiling=" << cdev_ceiling; std::string atrace_prefix = ::android::base::StringPrintf("%s-%s", sensor_name.data(), cdev_name.data()); ATRACE_INT((atrace_prefix + std::string("-pid_request")).c_str(), pid_cdev_request); ATRACE_INT((atrace_prefix + std::string("-hardlimit_request")).c_str(), hardlimit_cdev_request); ATRACE_INT((atrace_prefix + std::string("-release_step")).c_str(), release_step); ATRACE_INT((atrace_prefix + std::string("-cdev_floor")).c_str(), cdev_floor); ATRACE_INT((atrace_prefix + std::string("-cdev_ceiling")).c_str(), cdev_ceiling); auto request_state = std::max(pid_cdev_request, hardlimit_cdev_request); if (release_step) { if (release_step >= request_state) { request_state = 0; } else { request_state = request_state - release_step; } // Only check the cdev_floor when release step is non zero request_state = std::max(request_state, cdev_floor); } request_state = std::min(request_state, cdev_ceiling); if (cdev_request_pair.second != request_state) { ATRACE_INT((atrace_prefix + std::string("-final_request")).c_str(), request_state); if (updateCdevMaxRequestAndNotifyIfChange(cdev_name, cdev_request_pair.second, request_state)) { cooling_devices_to_update->emplace_back(cdev_name); } cdev_request_pair.second = request_state; // Update sensor cdev request time in state thermal_stats_helper->updateSensorCdevRequestStats(sensor_name, cdev_name, cdev_request_pair.second); } } } bool ThermalThrottling::updateCdevMaxRequestAndNotifyIfChange(std::string_view cdev_name, int cur_request, int new_request) { std::unique_lock _lock(cdev_all_request_map_mutex_); auto &request_set = cdev_all_request_map_.at(cdev_name.data()); int cur_max_request = (*request_set.begin()); // Remove old cdev request and add the new one. request_set.erase(request_set.find(cur_request)); request_set.insert(new_request); // Check if there is any change in aggregated max cdev request. int new_max_request = (*request_set.begin()); LOG(VERBOSE) << "For cooling device [" << cdev_name.data() << "] cur_max_request is: " << cur_max_request << " new_max_request is: " << new_max_request; return new_max_request != cur_max_request; } bool ThermalThrottling::getCdevMaxRequest(std::string_view cdev_name, int *max_state) { std::shared_lock _lock(cdev_all_request_map_mutex_); if (!cdev_all_request_map_.count(cdev_name.data())) { LOG(ERROR) << "Cooling device [" << cdev_name.data() << "] not present in cooling device request map"; return false; } *max_state = *cdev_all_request_map_.at(cdev_name.data()).begin(); return true; } } // namespace implementation } // namespace thermal } // namespace hardware } // namespace android } // namespace aidl