diff options
author | TeYuan Wang <kamewang@google.com> | 2022-03-28 20:26:06 +0800 |
---|---|---|
committer | TeYuan Wang <kamewang@google.com> | 2022-04-20 19:59:27 +0800 |
commit | 708a1dfa926bc2ca1f0c9aaa6543ed95c9f1fc89 (patch) | |
tree | 8aeebcf79a58107a98ad8bb24f9b1bcd0f483942 /thermal | |
parent | 996a9e16cedb40c0035235e7a81b32742e2a5725 (diff) |
thermal: Introduce PID 2.0
Support dynamically power budget distribution base on real power loading
Bug: 196478280
Test: run burn8 and manhattan and confirm thermal throttling,
Test: Verify thermal powerhint, and thermal event notification with emul_temp
Test: adb shell lshal debug android.hardware.thermal@2.0::IThermal/default
Change-Id: I9741762375f96e9b7c03db857f0e7d78c31eb5ee
Diffstat (limited to 'thermal')
-rw-r--r-- | thermal/Android.bp | 43 | ||||
-rw-r--r-- | thermal/Thermal.cpp | 375 | ||||
-rw-r--r-- | thermal/Thermal.h | 10 | ||||
-rw-r--r-- | thermal/device.mk | 11 | ||||
-rw-r--r-- | thermal/pid_1_0/utils/thermal_throttling.cpp | 451 | ||||
-rw-r--r-- | thermal/pid_1_0/utils/thermal_throttling.h | 121 | ||||
-rw-r--r-- | thermal/thermal-helper.cpp | 459 | ||||
-rw-r--r-- | thermal/thermal-helper.h | 81 | ||||
-rw-r--r-- | thermal/utils/power_files.cpp | 391 | ||||
-rw-r--r-- | thermal/utils/power_files.h | 88 | ||||
-rw-r--r-- | thermal/utils/powerhal_helper.cpp | 134 | ||||
-rw-r--r-- | thermal/utils/powerhal_helper.h | 67 | ||||
-rw-r--r-- | thermal/utils/thermal_files.cpp | 2 | ||||
-rw-r--r-- | thermal/utils/thermal_files.h | 2 | ||||
-rw-r--r-- | thermal/utils/thermal_info.cpp (renamed from thermal/utils/config_parser.cpp) | 66 | ||||
-rw-r--r-- | thermal/utils/thermal_info.h (renamed from thermal/utils/config_parser.h) | 12 | ||||
-rw-r--r-- | thermal/utils/thermal_throttling.cpp | 590 | ||||
-rw-r--r-- | thermal/utils/thermal_throttling.h | 127 |
18 files changed, 2041 insertions, 989 deletions
diff --git a/thermal/Android.bp b/thermal/Android.bp index 09722ce..f0d5a6e 100644 --- a/thermal/Android.bp +++ b/thermal/Android.bp @@ -1,21 +1,39 @@ -package { - // See: http://go/android-license-faq - default_applicable_licenses: [ - "Android-Apache-2.0", - "hardware_google_pixel_thermal_license", - ], +soong_config_module_type { + name: "thermal_hal_feature_cc_defaults", + module_type: "cc_defaults", + config_namespace: "thermal_hal_feature", + variables: ["pid"], + properties: ["cflags", "srcs"], } -license { - name: "hardware_google_pixel_thermal_license", - license_kinds: ["SPDX-license-identifier-BSD"], - license_text: ["LICENSE"], +soong_config_string_variable { + name: "pid", + values: ["apply_1_0", "apply_2_0"], +} + +thermal_hal_feature_cc_defaults { + name: "thermal_hal_feature_defaults", + soong_config_variables: { + pid: { + apply_1_0: { + srcs: [ + "pid_1_0/utils/thermal_throttling.cpp", + ], + }, + apply_2_0: { + srcs: [ + "utils/thermal_throttling.cpp", + ], + }, + }, + }, } cc_binary { name: "android.hardware.thermal@2.0-service.pixel", defaults: [ "hidl_defaults", + "thermal_hal_feature_defaults", ], vendor: true, relative_install_path: "hw", @@ -27,10 +45,11 @@ cc_binary { "service.cpp", "Thermal.cpp", "thermal-helper.cpp", - "utils/config_parser.cpp", + "utils/thermal_info.cpp", "utils/thermal_files.cpp", - "utils/thermal_watcher.cpp", "utils/power_files.cpp", + "utils/powerhal_helper.cpp", + "utils/thermal_watcher.cpp", ], shared_libs: [ "libbase", diff --git a/thermal/Thermal.cpp b/thermal/Thermal.cpp index 8f9f0e2..d0dc078 100644 --- a/thermal/Thermal.cpp +++ b/thermal/Thermal.cpp @@ -64,7 +64,7 @@ Return<void> setInitFailureAndCallback(T _hidl_cb, hidl_vec<U> data) { // Thermal() is killed. Thermal::Thermal() : thermal_helper_( - std::bind(&Thermal::sendThermalChangedCallback, this, std::placeholders::_1)) {} + std::bind(&Thermal::sendThermalChangedCallback, this, std::placeholders::_1)) {} // Methods from ::android::hardware::thermal::V1_0::IThermal. Return<void> Thermal::getTemperatures(getTemperatures_cb _hidl_cb) { @@ -164,7 +164,7 @@ Return<void> Thermal::getCurrentCoolingDevices(bool filterType, CoolingType type } if (!thermal_helper_.fillCurrentCoolingDevices(filterType, type, &cooling_devices)) { - return setFailureAndCallback(_hidl_cb, cooling_devices, "Failed to read thermal sensors."); + return setFailureAndCallback(_hidl_cb, cooling_devices, "Failed to read cooling devices."); } _hidl_cb(status, cooling_devices); @@ -219,7 +219,7 @@ Return<void> Thermal::registerThermalChangedCallback(const sp<IThermalChangedCal } Return<void> Thermal::unregisterThermalChangedCallback( - const sp<IThermalChangedCallback> &callback, unregisterThermalChangedCallback_cb _hidl_cb) { + const sp<IThermalChangedCallback> &callback, unregisterThermalChangedCallback_cb _hidl_cb) { ThermalStatus status; if (callback == nullptr) { status.code = ThermalStatusCode::FAILURE; @@ -233,19 +233,20 @@ Return<void> Thermal::unregisterThermalChangedCallback( bool removed = false; std::lock_guard<std::mutex> _lock(thermal_callback_mutex_); callbacks_.erase( - std::remove_if(callbacks_.begin(), callbacks_.end(), - [&](const CallbackSetting &c) { - if (interfacesEqual(c.callback, callback)) { - LOG(INFO) - << "a callback has been unregistered to ThermalHAL, isFilter: " - << c.is_filter_type << " Type: " - << android::hardware::thermal::V2_0::toString(c.type); - removed = true; - return true; - } - return false; - }), - callbacks_.end()); + std::remove_if( + callbacks_.begin(), callbacks_.end(), + [&](const CallbackSetting &c) { + if (interfacesEqual(c.callback, callback)) { + LOG(INFO) + << "a callback has been unregistered to ThermalHAL, isFilter: " + << c.is_filter_type << " Type: " + << android::hardware::thermal::V2_0::toString(c.type); + removed = true; + return true; + } + return false; + }), + callbacks_.end()); if (!removed) { status.code = ThermalStatusCode::FAILURE; status.debugMessage = "The callback was not registered before"; @@ -332,69 +333,88 @@ void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) { void Thermal::dumpThrottlingInfo(std::ostringstream *dump_buf) { *dump_buf << "Throttling Info:" << std::endl; const auto &map = thermal_helper_.GetSensorInfoMap(); + const auto &thermal_throttling_status_map = thermal_helper_.GetThermalThrottlingStatusMap(); for (const auto &name_info_pair : map) { if (name_info_pair.second.throttling_info->binded_cdev_info_map.size()) { *dump_buf << " Name: " << name_info_pair.first << std::endl; - *dump_buf << " PID Info:" << std::endl; - *dump_buf << " K_po: ["; - for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { - *dump_buf << name_info_pair.second.throttling_info->k_po[i] << " "; - } - *dump_buf << "]" << std::endl; - *dump_buf << " K_pu: ["; - for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { - *dump_buf << name_info_pair.second.throttling_info->k_pu[i] << " "; - } - *dump_buf << "]" << std::endl; - *dump_buf << " K_i: ["; - for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { - *dump_buf << name_info_pair.second.throttling_info->k_i[i] << " "; - } - *dump_buf << "]" << std::endl; - *dump_buf << " K_d: ["; - for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { - *dump_buf << name_info_pair.second.throttling_info->k_d[i] << " "; - } - *dump_buf << "]" << std::endl; - *dump_buf << " i_max: ["; - for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { - *dump_buf << name_info_pair.second.throttling_info->i_max[i] << " "; - } - *dump_buf << "]" << std::endl; - *dump_buf << " max_alloc_power: ["; - for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { - *dump_buf << name_info_pair.second.throttling_info->max_alloc_power[i] << " "; - } - *dump_buf << "]" << std::endl; - *dump_buf << " min_alloc_power: ["; - for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { - *dump_buf << name_info_pair.second.throttling_info->min_alloc_power[i] << " "; - } - *dump_buf << "]" << std::endl; - *dump_buf << " s_power: ["; - for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { - *dump_buf << name_info_pair.second.throttling_info->s_power[i] << " "; - } - *dump_buf << "]" << std::endl; - *dump_buf << " i_cutoff: ["; - for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { - *dump_buf << name_info_pair.second.throttling_info->i_cutoff[i] << " "; + if (thermal_throttling_status_map.at(name_info_pair.first) + .pid_power_budget_map.size()) { + *dump_buf << " PID Info:" << std::endl; + *dump_buf << " K_po: ["; + for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { + *dump_buf << name_info_pair.second.throttling_info->k_po[i] << " "; + } + *dump_buf << "]" << std::endl; + *dump_buf << " K_pu: ["; + for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { + *dump_buf << name_info_pair.second.throttling_info->k_pu[i] << " "; + } + *dump_buf << "]" << std::endl; + *dump_buf << " K_i: ["; + for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { + *dump_buf << name_info_pair.second.throttling_info->k_i[i] << " "; + } + *dump_buf << "]" << std::endl; + *dump_buf << " K_d: ["; + for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { + *dump_buf << name_info_pair.second.throttling_info->k_d[i] << " "; + } + *dump_buf << "]" << std::endl; + *dump_buf << " i_max: ["; + for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { + *dump_buf << name_info_pair.second.throttling_info->i_max[i] << " "; + } + *dump_buf << "]" << std::endl; + *dump_buf << " max_alloc_power: ["; + for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { + *dump_buf << name_info_pair.second.throttling_info->max_alloc_power[i] << " "; + } + *dump_buf << "]" << std::endl; + *dump_buf << " min_alloc_power: ["; + for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { + *dump_buf << name_info_pair.second.throttling_info->min_alloc_power[i] << " "; + } + *dump_buf << "]" << std::endl; + *dump_buf << " s_power: ["; + for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { + *dump_buf << name_info_pair.second.throttling_info->s_power[i] << " "; + } + *dump_buf << "]" << std::endl; + *dump_buf << " i_cutoff: ["; + for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { + *dump_buf << name_info_pair.second.throttling_info->i_cutoff[i] << " "; + } + *dump_buf << "]" << std::endl; } - *dump_buf << "]" << std::endl; *dump_buf << " Binded CDEV Info:" << std::endl; - if (name_info_pair.second.throttling_info->binded_cdev_info_map.size()) { - for (const auto &binded_cdev_info_pair : - name_info_pair.second.throttling_info->binded_cdev_info_map) { - *dump_buf << " Cooling device name: " << binded_cdev_info_pair.first - << std::endl; + for (const auto &binded_cdev_info_pair : + name_info_pair.second.throttling_info->binded_cdev_info_map) { + *dump_buf << " Cooling device name: " << binded_cdev_info_pair.first << std::endl; + if (thermal_throttling_status_map.at(name_info_pair.first) + .pid_power_budget_map.size()) { *dump_buf << " WeightForPID: ["; for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { *dump_buf << binded_cdev_info_pair.second.cdev_weight_for_pid[i] << " "; } *dump_buf << "]" << std::endl; - *dump_buf << " Ceiling: ["; + } + *dump_buf << " Ceiling: ["; + for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { + *dump_buf << binded_cdev_info_pair.second.cdev_ceiling[i] << " "; + } + *dump_buf << "]" << std::endl; + *dump_buf << " Hard limit: ["; + for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { + *dump_buf << binded_cdev_info_pair.second.limit_info[i] << " "; + } + *dump_buf << "]" << std::endl; + + if (!binded_cdev_info_pair.second.power_rail.empty()) { + *dump_buf << " Binded power rail: " + << binded_cdev_info_pair.second.power_rail << std::endl; + *dump_buf << " Power threshold: ["; for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { - *dump_buf << binded_cdev_info_pair.second.cdev_ceiling[i] << " "; + *dump_buf << binded_cdev_info_pair.second.power_thresholds[i] << " "; } *dump_buf << "]" << std::endl; *dump_buf << " Floor with PowerLink: ["; @@ -403,45 +423,30 @@ void Thermal::dumpThrottlingInfo(std::ostringstream *dump_buf) { << " "; } *dump_buf << "]" << std::endl; - *dump_buf << " Hard limit: ["; - for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { - *dump_buf << binded_cdev_info_pair.second.limit_info[i] << " "; - } - *dump_buf << "]" << std::endl; - - if (!binded_cdev_info_pair.second.power_rail.empty()) { - *dump_buf << " Binded power rail: " - << binded_cdev_info_pair.second.power_rail << std::endl; - *dump_buf << " Power threshold: ["; - for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { - *dump_buf << binded_cdev_info_pair.second.power_thresholds[i] << " "; - } - *dump_buf << "]" << std::endl; - *dump_buf << " Release logic: "; - switch (binded_cdev_info_pair.second.release_logic) { - case ReleaseLogic::INCREASE: - *dump_buf << "INCREASE"; - break; - case ReleaseLogic::DECREASE: - *dump_buf << "DECREASE"; - break; - case ReleaseLogic::STEPWISE: - *dump_buf << "STEPWISE"; - break; - case ReleaseLogic::RELEASE_TO_FLOOR: - *dump_buf << "RELEASE_TO_FLOOR"; - break; - default: - *dump_buf << "NONE"; - break; - } - *dump_buf << std::endl; - *dump_buf << " high_power_check: " << std::boolalpha - << binded_cdev_info_pair.second.high_power_check << std::endl; - *dump_buf << " throttling_with_power_link: " << std::boolalpha - << binded_cdev_info_pair.second.throttling_with_power_link - << std::endl; + *dump_buf << " Release logic: "; + switch (binded_cdev_info_pair.second.release_logic) { + case ReleaseLogic::INCREASE: + *dump_buf << "INCREASE"; + break; + case ReleaseLogic::DECREASE: + *dump_buf << "DECREASE"; + break; + case ReleaseLogic::STEPWISE: + *dump_buf << "STEPWISE"; + break; + case ReleaseLogic::RELEASE_TO_FLOOR: + *dump_buf << "RELEASE_TO_FLOOR"; + break; + default: + *dump_buf << "NONE"; + break; } + *dump_buf << std::endl; + *dump_buf << " high_power_check: " << std::boolalpha + << binded_cdev_info_pair.second.high_power_check << std::endl; + *dump_buf << " throttling_with_power_link: " << std::boolalpha + << binded_cdev_info_pair.second.throttling_with_power_link + << std::endl; } } } @@ -449,36 +454,50 @@ void Thermal::dumpThrottlingInfo(std::ostringstream *dump_buf) { } void Thermal::dumpThrottlingRequestStatus(std::ostringstream *dump_buf) { - const auto &sensor_status_map = thermal_helper_.GetSensorStatusMap(); - const auto &cdev_status_map = thermal_helper_.GetCdevStatusMap(); - const auto &release_map = thermal_helper_.GetThrottlingReleaseMap(); + const auto &thermal_throttling_status_map = thermal_helper_.GetThermalThrottlingStatusMap(); + if (!thermal_throttling_status_map.size()) { + return; + } *dump_buf << "Throttling Request Status " << std::endl; - for (const auto &cdev_status_pair : cdev_status_map) { - *dump_buf << " Name: " << cdev_status_pair.first << std::endl; - for (const auto &request_pair : cdev_status_pair.second) { - *dump_buf << " Request Sensor: " << request_pair.first << std::endl; - *dump_buf << " Request Throttling State: " << request_pair.second << std::endl; - if (sensor_status_map.at(request_pair.first).pid_request_map.size() && - sensor_status_map.at(request_pair.first) - .pid_request_map.count(cdev_status_pair.first)) { - *dump_buf << " PID Request State: " - << sensor_status_map.at(request_pair.first) - .pid_request_map.at(cdev_status_pair.first) + for (const auto &thermal_throttling_status_pair : thermal_throttling_status_map) { + *dump_buf << " Name: " << thermal_throttling_status_pair.first << std::endl; + if (thermal_throttling_status_pair.second.pid_power_budget_map.size()) { + *dump_buf << " power budget request state" << std::endl; + for (const auto &request_pair : + thermal_throttling_status_pair.second.pid_power_budget_map) { + *dump_buf << " " << request_pair.first << ": " << request_pair.second << std::endl; } - if (sensor_status_map.at(request_pair.first).hard_limit_request_map.size() && - sensor_status_map.at(request_pair.first) - .hard_limit_request_map.count(cdev_status_pair.first)) { - *dump_buf << " Hard Limit Request State: " - << sensor_status_map.at(request_pair.first) - .hard_limit_request_map.at(cdev_status_pair.first) + } + if (thermal_throttling_status_pair.second.pid_cdev_request_map.size()) { + *dump_buf << " pid cdev request state" << std::endl; + for (const auto &request_pair : + thermal_throttling_status_pair.second.pid_cdev_request_map) { + *dump_buf << " " << request_pair.first << ": " << request_pair.second << std::endl; } - if (release_map.count(request_pair.first) && - release_map.at(request_pair.first).count(cdev_status_pair.first)) { - const auto &cdev_release_info = - release_map.at(request_pair.first).at(cdev_status_pair.first); - *dump_buf << " Release Step: " << cdev_release_info.release_step << std::endl; + } + if (thermal_throttling_status_pair.second.hardlimit_cdev_request_map.size()) { + *dump_buf << " hard limit cdev request state" << std::endl; + for (const auto &request_pair : + thermal_throttling_status_pair.second.hardlimit_cdev_request_map) { + *dump_buf << " " << request_pair.first << ": " << request_pair.second + << std::endl; + } + } + if (thermal_throttling_status_pair.second.throttling_release_map.size()) { + *dump_buf << " cdev release state" << std::endl; + for (const auto &request_pair : + thermal_throttling_status_pair.second.throttling_release_map) { + *dump_buf << " " << request_pair.first << ": " << request_pair.second + << std::endl; + } + } + if (thermal_throttling_status_pair.second.cdev_status_map.size()) { + *dump_buf << " cdev request state" << std::endl; + for (const auto &request_pair : thermal_throttling_status_pair.second.cdev_status_map) { + *dump_buf << " " << request_pair.first << ": " << request_pair.second + << std::endl; } } } @@ -495,57 +514,52 @@ void Thermal::dumpPowerRailInfo(std::ostringstream *dump_buf) { << std::endl; *dump_buf << " Power Sample Delay: " << power_rail_pair.second.power_sample_delay.count() << std::endl; - for (const auto &power_status_pair : power_status_map) { - if (power_status_pair.second.count(power_rail_pair.first)) { - auto power_history = - power_status_pair.second.at(power_rail_pair.first).power_history; - *dump_buf << " Request Sensor: " << power_status_pair.first << std::endl; - *dump_buf - << " Last Updated AVG Power: " - << power_status_pair.second.at(power_rail_pair.first).last_updated_avg_power - << " mW" << std::endl; + if (power_status_map.count(power_rail_pair.first)) { + auto power_history = power_status_map.at(power_rail_pair.first).power_history; + *dump_buf << " Last Updated AVG Power: " + << power_status_map.at(power_rail_pair.first).last_updated_avg_power << " mW" + << std::endl; + if (power_rail_pair.second.virtual_power_rail_info != nullptr) { + *dump_buf << " Formula="; + switch (power_rail_pair.second.virtual_power_rail_info->formula) { + case FormulaOption::COUNT_THRESHOLD: + *dump_buf << "COUNT_THRESHOLD"; + break; + case FormulaOption::WEIGHTED_AVG: + *dump_buf << "WEIGHTED_AVG"; + break; + case FormulaOption::MAXIMUM: + *dump_buf << "MAXIMUM"; + break; + case FormulaOption::MINIMUM: + *dump_buf << "MINIMUM"; + break; + default: + *dump_buf << "NONE"; + break; + } + *dump_buf << std::endl; + } + for (size_t i = 0; i < power_history.size(); ++i) { if (power_rail_pair.second.virtual_power_rail_info != nullptr) { - *dump_buf << " Formula="; - switch (power_rail_pair.second.virtual_power_rail_info->formula) { - case FormulaOption::COUNT_THRESHOLD: - *dump_buf << "COUNT_THRESHOLD"; - break; - case FormulaOption::WEIGHTED_AVG: - *dump_buf << "WEIGHTED_AVG"; - break; - case FormulaOption::MAXIMUM: - *dump_buf << "MAXIMUM"; - break; - case FormulaOption::MINIMUM: - *dump_buf << "MINIMUM"; - break; - default: - *dump_buf << "NONE"; - break; - } - *dump_buf << std::endl; + *dump_buf + << " Linked power rail " + << power_rail_pair.second.virtual_power_rail_info->linked_power_rails[i] + << std::endl; + *dump_buf << " Coefficient=" + << power_rail_pair.second.virtual_power_rail_info->coefficients[i] + << std::endl; + *dump_buf << " Power Samples: "; + } else { + *dump_buf << " Power Samples: "; } - for (size_t i = 0; i < power_history.size(); ++i) { - if (power_rail_pair.second.virtual_power_rail_info != nullptr) { - *dump_buf << " Linked power rail " - << power_rail_pair.second.virtual_power_rail_info - ->linked_power_rails[i] - << std::endl; - *dump_buf << " Coefficient=" - << power_rail_pair.second.virtual_power_rail_info->coefficients[i] - << std::endl; - *dump_buf << " Power Samples: "; - } else { - *dump_buf << " Power Samples: "; - } - while (power_history[i].size() > 0) { - const auto power_sample = power_history[i].front(); - power_history[i].pop(); - *dump_buf << "(T=" << power_sample.duration - << ", uWs=" << power_sample.energy_counter << ") "; - } - *dump_buf << std::endl; + while (power_history[i].size() > 0) { + const auto power_sample = power_history[i].front(); + power_history[i].pop(); + *dump_buf << "(T=" << power_sample.duration + << ", uWs=" << power_sample.energy_counter << ") "; } + *dump_buf << std::endl; } } } @@ -660,6 +674,9 @@ Return<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin dump_buf << "getHysteresis:" << std::endl; const auto &map = thermal_helper_.GetSensorInfoMap(); for (const auto &name_info_pair : map) { + if (!name_info_pair.second.is_watch) { + continue; + } dump_buf << " Name: " << name_info_pair.first; dump_buf << " hotHysteresis: ["; for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { diff --git a/thermal/Thermal.h b/thermal/Thermal.h index d33e77d..36e079e 100644 --- a/thermal/Thermal.h +++ b/thermal/Thermal.h @@ -16,13 +16,13 @@ #pragma once -#include <mutex> -#include <thread> - #include <android/hardware/thermal/2.0/IThermal.h> #include <android/hardware/thermal/2.0/IThermalChangedCallback.h> #include <hidl/Status.h> +#include <mutex> +#include <thread> + #include "thermal-helper.h" namespace android { @@ -69,8 +69,8 @@ class Thermal : public IThermal { const sp<IThermalChangedCallback> &callback, bool filterType, TemperatureType_2_0 type, registerThermalChangedCallback_cb _hidl_cb) override; Return<void> unregisterThermalChangedCallback( - const sp<IThermalChangedCallback> &callback, - unregisterThermalChangedCallback_cb _hidl_cb) override; + const sp<IThermalChangedCallback> &callback, + unregisterThermalChangedCallback_cb _hidl_cb) override; Return<void> getCurrentCoolingDevices(bool filterType, CoolingType type, getCurrentCoolingDevices_cb _hidl_cb) override; diff --git a/thermal/device.mk b/thermal/device.mk index d316633..bf150cd 100644 --- a/thermal/device.mk +++ b/thermal/device.mk @@ -1,6 +1,15 @@ # Thermal HAL +SOONG_CONFIG_NAMESPACES += thermal_hal_feature +SOONG_CONFIG_thermal_hal_feature += \ + pid \ + +SOONG_CONFIG_thermal_hal_feature_pid ?= apply_1_0 + +PRODUCT_PACKAGES += \ + android.hardware.thermal@2.0-service.pixel + +# Thermal utils PRODUCT_PACKAGES += \ - android.hardware.thermal@2.0-service.pixel \ thermal_symlinks ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT))) diff --git a/thermal/pid_1_0/utils/thermal_throttling.cpp b/thermal/pid_1_0/utils/thermal_throttling.cpp new file mode 100644 index 0000000..f158b6c --- /dev/null +++ b/thermal/pid_1_0/utils/thermal_throttling.cpp @@ -0,0 +1,451 @@ +/* + * 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. + */ + +#include "thermal_throttling.h" + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <utils/Trace.h> + +#include <iterator> +#include <set> +#include <sstream> +#include <thread> +#include <vector> + +#include "../../utils/power_files.h" +#include "../../utils/thermal_info.h" + +namespace android { +namespace hardware { +namespace thermal { +namespace V2_0 { +namespace implementation { + +// 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 : hidl_enum_range<ThrottlingSeverity>()) { + size_t state = static_cast<size_t>(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::clearThrottlingData(std::string_view sensor_name, + const SensorInfo &sensor_info) { + if (!thermal_throttling_status_map_.count(sensor_name.data())) { + return; + } + std::unique_lock<std::shared_mutex> _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<int>::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()].err_integral = + sensor_info.throttling_info->err_integral_default; + thermal_throttling_status_map_[sensor_name.data()].prev_err = NAN; + return; +} + +bool ThermalThrottling::registerThermalThrottling( + std::string_view sensor_name, const std::shared_ptr<ThrottlingInfo> &throttling_info, + const std::unordered_map<std::string, CdevInfo> &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()].err_integral = + throttling_info->err_integral_default; + thermal_throttling_status_map_[sensor_name.data()].prev_err = NAN; + + 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<int>::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; + 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; + break; + } + } + // Register throttling release map if power threshold is exist + 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_2_0 &temp, + const SensorInfo &sensor_info, + std::chrono::milliseconds time_elapsed_ms, + ThrottlingSeverity curr_severity) { + float p = 0, i = 0, d = 0; + float power_budget = std::numeric_limits<float>::max(); + + if (curr_severity == ThrottlingSeverity::NONE) { + return power_budget; + } + + const auto target_state = getTargetStateOfPID(sensor_info, curr_severity); + + // Compute PID + float err = sensor_info.hot_thresholds[target_state] - temp.value; + p = err * (err < 0 ? sensor_info.throttling_info->k_po[target_state] + : sensor_info.throttling_info->k_pu[target_state]); + i = thermal_throttling_status_map_[temp.name].err_integral * + sensor_info.throttling_info->k_i[target_state]; + if (err < sensor_info.throttling_info->i_cutoff[target_state]) { + float i_next = i + err * sensor_info.throttling_info->k_i[target_state]; + if (abs(i_next) < sensor_info.throttling_info->i_max[target_state]) { + i = i_next; + thermal_throttling_status_map_[temp.name].err_integral += err; + } + } + + if (!std::isnan(thermal_throttling_status_map_[temp.name].prev_err) && + time_elapsed_ms != std::chrono::milliseconds::zero()) { + d = sensor_info.throttling_info->k_d[target_state] * + (err - thermal_throttling_status_map_[temp.name].prev_err) / time_elapsed_ms.count(); + } + + thermal_throttling_status_map_[temp.name].prev_err = err; + // Calculate power budget + power_budget = sensor_info.throttling_info->s_power[target_state] + p + i + d; + 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]; + } + + LOG(INFO) << temp.name << " power_budget=" << power_budget << " err=" << err + << " err_integral=" << thermal_throttling_status_map_[temp.name].err_integral + << " s_power=" << sensor_info.throttling_info->s_power[target_state] + << " time_elapsed_ms=" << time_elapsed_ms.count() << " p=" << p << " i=" << i + << " d=" << d << " control target=" << target_state; + + return power_budget; +} + +bool ThermalThrottling::updateCdevRequestByPower( + const Temperature_2_0 &temp, const SensorInfo &sensor_info, + const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms, + const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) { + float total_weight = 0, cdev_power_budget; + size_t j; + + const auto target_state = getTargetStateOfPID(sensor_info, curr_severity); + auto total_power_budget = updatePowerBudget(temp, sensor_info, time_elapsed_ms, curr_severity); + + std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_); + // Compute total cdev weight + for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) { + const auto cdev_weight = binded_cdev_info_pair.second + .cdev_weight_for_pid[static_cast<size_t>(curr_severity)]; + if (std::isnan(cdev_weight)) { + continue; + } + total_weight += cdev_weight; + } + + // Map cdev state by power + for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) { + const auto cdev_weight = binded_cdev_info_pair.second.cdev_weight_for_pid[target_state]; + if (!std::isnan(cdev_weight)) { + cdev_power_budget = total_power_budget * (cdev_weight / total_weight); + + const CdevInfo &cdev_info_pair = + cooling_device_info_map.at(binded_cdev_info_pair.first); + for (j = 0; j < cdev_info_pair.state2power.size() - 1; ++j) { + if (cdev_power_budget > cdev_info_pair.state2power[j]) { + break; + } + } + + thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at( + binded_cdev_info_pair.first) = static_cast<int>(j); + LOG(VERBOSE) << "Power allocator: Sensor " << temp.name << " allocate " + << cdev_power_budget << "mW to " << binded_cdev_info_pair.first + << "(cdev_weight=" << cdev_weight << ") update state to " << j; + } + } + return true; +} + +void ThermalThrottling::updateCdevRequestBySeverity(std::string_view sensor_name, + const SensorInfo &sensor_info, + ThrottlingSeverity curr_severity) { + std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_); + for (auto const &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) { + thermal_throttling_status_map_[sensor_name.data()].hardlimit_cdev_request_map.at( + binded_cdev_info_pair.first) = + binded_cdev_info_pair.second.limit_info[static_cast<size_t>(curr_severity)]; + 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<std::string, CdevInfo> &cooling_device_info_map, + const std::unordered_map<std::string, PowerStatus> &power_status_map, + const ThrottlingSeverity severity, const SensorInfo &sensor_info) { + ATRACE_CALL(); + std::unique_lock<std::shared_mutex> _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; + + // Return false if we cannot get the 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<int>(severity)]) { + is_over_budget = false; + } + } else { + if (avg_power > + binded_cdev_info_pair.second.power_thresholds[static_cast<int>(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<int>(severity)] + << ", avg power = " << avg_power; + + switch (binded_cdev_info_pair.second.release_logic) { + case ReleaseLogic::INCREASE: + if (!is_over_budget) { + if (std::abs(release_step) < static_cast<int>(max_state)) { + release_step--; + } + } else { + release_step = 0; + } + break; + case ReleaseLogic::DECREASE: + if (!is_over_budget) { + if (release_step < static_cast<int>(max_state)) { + release_step++; + } + } else { + release_step = 0; + } + break; + case ReleaseLogic::STEPWISE: + if (!is_over_budget) { + if (release_step < static_cast<int>(max_state)) { + release_step++; + } + } else { + if (std::abs(release_step) < static_cast<int>(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_2_0 &temp, const SensorInfo &sensor_info, + const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms, + const std::unordered_map<std::string, PowerStatus> &power_status_map, + const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) { + if (!thermal_throttling_status_map_.count(temp.name)) { + return; + } + + if (thermal_throttling_status_map_[temp.name].pid_power_budget_map.size()) { + updateCdevRequestByPower(temp, sensor_info, curr_severity, time_elapsed_ms, + 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<std::string> *cooling_devices_to_update) { + int release_step = 0; + std::unique_lock<std::shared_mutex> _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; + + for (auto &cdev_request_pair : thermal_throttling_status.cdev_status_map) { + int pid_cdev_request = 0; + int hardlimit_cdev_request = 0; + const auto &binded_cdev_info = + sensor_info.throttling_info->binded_cdev_info_map.at(cdev_request_pair.first); + const auto cdev_ceiling = binded_cdev_info.cdev_ceiling[static_cast<size_t>(curr_severity)]; + const auto cdev_floor = + binded_cdev_info.cdev_floor_with_power_link[static_cast<size_t>(curr_severity)]; + release_step = 0; + + if (thermal_throttling_status.pid_cdev_request_map.count(cdev_request_pair.first)) { + pid_cdev_request = + thermal_throttling_status.pid_cdev_request_map.at(cdev_request_pair.first); + } + + if (thermal_throttling_status.hardlimit_cdev_request_map.count(cdev_request_pair.first)) { + hardlimit_cdev_request = thermal_throttling_status.hardlimit_cdev_request_map.at( + cdev_request_pair.first); + } + + if (cdev_release_map.count(cdev_request_pair.first)) { + release_step = cdev_release_map.at(cdev_request_pair.first); + } + + LOG(VERBOSE) << sensor_name.data() << " binded cooling device " << cdev_request_pair.first + << "'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; + + 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 + if (request_state < cdev_floor) { + request_state = cdev_floor; + } + } + if (request_state > cdev_ceiling) { + request_state = cdev_ceiling; + } + + if (cdev_request_pair.second != request_state) { + cdev_request_pair.second = request_state; + cooling_devices_to_update->emplace_back(cdev_request_pair.first); + } + } +} + +} // namespace implementation +} // namespace V2_0 +} // namespace thermal +} // namespace hardware +} // namespace android diff --git a/thermal/pid_1_0/utils/thermal_throttling.h b/thermal/pid_1_0/utils/thermal_throttling.h new file mode 100644 index 0000000..e94f25d --- /dev/null +++ b/thermal/pid_1_0/utils/thermal_throttling.h @@ -0,0 +1,121 @@ +/* + * 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. + */ + +#pragma once + +#include <android/hardware/thermal/2.0/IThermal.h> + +#include <queue> +#include <shared_mutex> +#include <string> +#include <unordered_map> +#include <unordered_set> + +#include "../../utils/power_files.h" +#include "../../utils/thermal_info.h" + +namespace android { +namespace hardware { +namespace thermal { +namespace V2_0 { +namespace implementation { + +using ::android::hardware::hidl_vec; +using ::android::hardware::thermal::V2_0::IThermal; +using Temperature_2_0 = ::android::hardware::thermal::V2_0::Temperature; +using ::android::hardware::thermal::V2_0::TemperatureThreshold; +using ::android::hardware::thermal::V2_0::ThrottlingSeverity; + +struct ThermalThrottlingStatus { + std::unordered_map<std::string, int> pid_power_budget_map; + std::unordered_map<std::string, int> pid_cdev_request_map; + std::unordered_map<std::string, int> hardlimit_cdev_request_map; + std::unordered_map<std::string, int> throttling_release_map; + std::unordered_map<std::string, int> cdev_status_map; + float err_integral; + float prev_err; +}; + +// Return the target state of PID algorithm +size_t getTargetStateOfPID(const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity); + +// A helper class for conducting thermal throttling +class ThermalThrottling { + public: + ThermalThrottling() = default; + ~ThermalThrottling() = default; + // Disallow copy and assign. + ThermalThrottling(const ThermalThrottling &) = delete; + void operator=(const ThermalThrottling &) = delete; + + // Clear throttling data + void clearThrottlingData(std::string_view sensor_name, const SensorInfo &sensor_info); + // Register map for throttling algo + bool registerThermalThrottling( + std::string_view sensor_name, const std::shared_ptr<ThrottlingInfo> &throttling_info, + const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map); + // Register map for throttling release algo + bool registerThrottlingReleaseToWatch(std::string_view sensor_name, std::string_view cdev_name, + const BindedCdevInfo &binded_cdev_info); + // Get throttling status map + const std::unordered_map<std::string, ThermalThrottlingStatus> &GetThermalThrottlingStatusMap() + const { + std::shared_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_); + return thermal_throttling_status_map_; + } + // update thermal throttling request for the specific sensor + void thermalThrottlingUpdate( + const Temperature_2_0 &temp, const SensorInfo &sensor_info, + const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms, + const std::unordered_map<std::string, PowerStatus> &power_status_map, + const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map); + + // Compute the throttling target from all the sensors' request + void computeCoolingDevicesRequest(std::string_view sensor_name, const SensorInfo &sensor_info, + const ThrottlingSeverity curr_severity, + std::vector<std::string> *cooling_devices_to_update); + + private: + // PID algo - get the total power budget + float updatePowerBudget(const Temperature_2_0 &temp, const SensorInfo &sensor_info, + std::chrono::milliseconds time_elapsed_ms, + ThrottlingSeverity curr_severity); + // PID algo - map the target throttling state according to the power budget + bool updateCdevRequestByPower( + const Temperature_2_0 &temp, const SensorInfo &sensor_info, + const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms, + const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map); + // Hard limit algo - assign the throttling state according to the severity + void updateCdevRequestBySeverity(std::string_view sensor_name, const SensorInfo &sensor_info, + ThrottlingSeverity curr_severity); + // Throttling release algo according to predefined power threshold + bool throttlingReleaseUpdate( + std::string_view sensor_name, + const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map, + const std::unordered_map<std::string, PowerStatus> &power_status_map, + const ThrottlingSeverity severity, const SensorInfo &sensor_info); + + mutable std::shared_mutex thermal_throttling_status_map_mutex_; + // Thermal throttling status from each sensor + std::unordered_map<std::string, ThermalThrottlingStatus> thermal_throttling_status_map_; + std::vector<std::string> cooling_devices_to_update; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace thermal +} // namespace hardware +} // namespace android diff --git a/thermal/thermal-helper.cpp b/thermal/thermal-helper.cpp index 41b2b27..9b23560 100644 --- a/thermal/thermal-helper.cpp +++ b/thermal/thermal-helper.cpp @@ -22,8 +22,6 @@ #include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> -#include <android/binder_manager.h> -#include <hidl/HidlTransportSupport.h> #include <utils/Trace.h> #include <iterator> @@ -61,7 +59,6 @@ constexpr std::string_view kThermalDisabledProperty("vendor.disable.thermal.cont namespace { using android::base::StringPrintf; -using android::hardware::thermal::V2_0::toString; /* * Pixel don't offline CPU, so std::thread::hardware_concurrency(); should work. @@ -75,18 +72,18 @@ using android::hardware::thermal::V2_0::toString; static int getNumberOfCores() { std::string file; if (!android::base::ReadFileToString(kCpuPresentFile.data(), &file)) { - LOG(ERROR) << "Error reading Cpu present file: " << kCpuPresentFile; + LOG(ERROR) << "Error reading CPU present file: " << kCpuPresentFile; return 0; } std::vector<std::string> pieces = android::base::Split(file, "-"); if (pieces.size() != 2) { - LOG(ERROR) << "Error parsing Cpu present file content: " << file; + LOG(ERROR) << "Error parsing CPU present file content: " << file; return 0; } auto min_core = std::stoul(pieces[0]); auto max_core = std::stoul(pieces[1]); if (max_core < min_core) { - LOG(ERROR) << "Error parsing Cpu present min and max: " << min_core << " - " << max_core; + LOG(ERROR) << "Error parsing CPU present min and max: " << min_core << " - " << max_core; return 0; } return static_cast<std::size_t>(max_core - min_core + 1); @@ -96,7 +93,7 @@ const int kMaxCpus = getNumberOfCores(); void parseCpuUsagesFileAndAssignUsages(hidl_vec<CpuUsage> *cpu_usages) { std::string data; if (!android::base::ReadFileToString(kCpuUsageFile.data(), &data)) { - LOG(ERROR) << "Error reading cpu usage file: " << kCpuUsageFile; + LOG(ERROR) << "Error reading CPU usage file: " << kCpuUsageFile; return; } @@ -121,7 +118,7 @@ void parseCpuUsagesFileAndAssignUsages(hidl_vec<CpuUsage> *cpu_usages) { kCpuOnlineFileSuffix.data()); std::string is_online; if (!android::base::ReadFileToString(cpu_online_path, &is_online)) { - LOG(ERROR) << "Could not open Cpu online file: " << cpu_online_path; + LOG(ERROR) << "Could not open CPU online file: " << cpu_online_path; if (cpu_num != 0) { return; } @@ -134,7 +131,7 @@ void parseCpuUsagesFileAndAssignUsages(hidl_vec<CpuUsage> *cpu_usages) { (*cpu_usages)[cpu_num].total = user + nice + system + idle; (*cpu_usages)[cpu_num].isOnline = (is_online == "1") ? true : false; } else { - LOG(ERROR) << "Unexpected cpu number: " << words[0]; + LOG(ERROR) << "Unexpected CPU number: " << words[0]; return; } } @@ -176,91 +173,6 @@ std::unordered_map<std::string, std::string> parseThermalPathMap(std::string_vie } } // namespace -PowerHalService::PowerHalService() - : power_hal_aidl_exist_(true), power_hal_aidl_(nullptr), power_hal_ext_aidl_(nullptr) { - connect(); -} - -bool PowerHalService::connect() { - std::lock_guard<std::mutex> lock(lock_); - if (!power_hal_aidl_exist_) - return false; - - if (power_hal_aidl_ != nullptr) - return true; - - const std::string kInstance = std::string(IPower::descriptor) + "/default"; - ndk::SpAIBinder power_binder = ndk::SpAIBinder(AServiceManager_getService(kInstance.c_str())); - ndk::SpAIBinder ext_power_binder; - - if (power_binder.get() == nullptr) { - LOG(ERROR) << "Cannot get Power Hal Binder"; - power_hal_aidl_exist_ = false; - return false; - } - - power_hal_aidl_ = IPower::fromBinder(power_binder); - - if (power_hal_aidl_ == nullptr) { - power_hal_aidl_exist_ = false; - LOG(ERROR) << "Cannot get Power Hal AIDL" << kInstance.c_str(); - return false; - } - - if (STATUS_OK != AIBinder_getExtension(power_binder.get(), ext_power_binder.getR()) || - ext_power_binder.get() == nullptr) { - LOG(ERROR) << "Cannot get Power Hal Extension Binder"; - power_hal_aidl_exist_ = false; - return false; - } - - power_hal_ext_aidl_ = IPowerExt::fromBinder(ext_power_binder); - if (power_hal_ext_aidl_ == nullptr) { - LOG(ERROR) << "Cannot get Power Hal Extension AIDL"; - power_hal_aidl_exist_ = false; - } - - return true; -} - -bool PowerHalService::isModeSupported(const std::string &type, const ThrottlingSeverity &t) { - bool isSupported = false; - if (!isPowerHalConnected()) { - return false; - } - std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str()); - lock_.lock(); - if (!power_hal_ext_aidl_->isModeSupported(power_hint, &isSupported).isOk()) { - LOG(ERROR) << "Fail to check supported mode, Hint: " << power_hint; - power_hal_aidl_exist_ = false; - power_hal_ext_aidl_ = nullptr; - power_hal_aidl_ = nullptr; - lock_.unlock(); - return false; - } - lock_.unlock(); - return isSupported; -} - -void PowerHalService::setMode(const std::string &type, const ThrottlingSeverity &t, - const bool &enable) { - if (!isPowerHalConnected()) { - return; - } - - std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str()); - LOG(INFO) << "Send Hint " << power_hint << " Enable: " << std::boolalpha << enable; - lock_.lock(); - if (!power_hal_ext_aidl_->setMode(power_hint, enable).isOk()) { - LOG(ERROR) << "Fail to set mode, Hint: " << power_hint; - power_hal_aidl_exist_ = false; - power_hal_ext_aidl_ = nullptr; - power_hal_aidl_ = nullptr; - lock_.unlock(); - return; - } - lock_.unlock(); -} /* * Populate the sensor_name_to_file_map_ map by walking through the file tree, @@ -278,8 +190,7 @@ ThermalHelper::ThermalHelper(const NotificationCallback &cb) android::base::GetBoolProperty(kThermalDisabledProperty.data(), false); is_initialized_ = ParseCoolingDevice(config_path, &cooling_device_info_map_) && - ParseSensorInfo(config_path, &sensor_info_map_) && - ParsePowerRailInfo(config_path, &power_rail_info_map_); + ParseSensorInfo(config_path, &sensor_info_map_); if (thermal_throttling_disabled) { return; @@ -298,6 +209,10 @@ ThermalHelper::ThermalHelper(const NotificationCallback &cb) LOG(FATAL) << "ThermalHAL could not be initialized properly."; } + if (!power_files_.registerPowerRailsToWatch(config_path)) { + LOG(FATAL) << "Failed to register power rails"; + } + for (auto const &name_status_pair : sensor_info_map_) { sensor_status_map_[name_status_pair.first] = { .severity = ThrottlingSeverity::NONE, @@ -306,33 +221,19 @@ ThermalHelper::ThermalHelper(const NotificationCallback &cb) .prev_hint_severity = ThrottlingSeverity::NONE, .last_update_time = boot_clock::time_point::min(), .thermal_cached = {NAN, boot_clock::time_point::min()}, - .err_integral = 0.0, - .prev_err = NAN, }; - for (auto &binded_cdev_pair : - name_status_pair.second.throttling_info->binded_cdev_info_map) { - if (!cooling_device_info_map_.count(binded_cdev_pair.first)) { - LOG(FATAL) << "Could not find " << binded_cdev_pair.first - << " in cooling device info map"; - } - - for (const auto &cdev_weight : binded_cdev_pair.second.cdev_weight_for_pid) { - if (!std::isnan(cdev_weight)) { - sensor_status_map_[name_status_pair.first] - .pid_request_map[binded_cdev_pair.first] = 0; - cdev_status_map_[binded_cdev_pair.first][name_status_pair.first] = 0; - break; - } + if (name_status_pair.second.throttling_info != nullptr) { + if (!thermal_throttling_.registerThermalThrottling( + name_status_pair.first, name_status_pair.second.throttling_info, + cooling_device_info_map_)) { + LOG(FATAL) << name_status_pair.first << " failed to register thermal throttling"; } + } - for (const auto &limit_info : binded_cdev_pair.second.limit_info) { - if (limit_info > 0) { - sensor_status_map_[name_status_pair.first] - .hard_limit_request_map[binded_cdev_pair.first] = 0; - cdev_status_map_[binded_cdev_pair.first][name_status_pair.first] = 0; - } - } + // Update cooling device max state + for (auto &binded_cdev_pair : + name_status_pair.second.throttling_info->binded_cdev_info_map) { const auto &cdev_info = cooling_device_info_map_.at(binded_cdev_pair.first); for (auto &cdev_ceiling : binded_cdev_pair.second.cdev_ceiling) { @@ -345,19 +246,6 @@ ThermalHelper::ThermalHelper(const NotificationCallback &cb) cdev_ceiling = cdev_info.max_state; } } - - if (power_rail_info_map_.count(binded_cdev_pair.second.power_rail) && - power_rail_info_map_.at(binded_cdev_pair.second.power_rail).power_sample_count && - power_files_.findEnergySourceToWatch()) { - const auto &power_rail_info = - power_rail_info_map_.at(binded_cdev_pair.second.power_rail); - if (!power_files_.registerPowerRailsToWatch( - name_status_pair.first, binded_cdev_pair.first, binded_cdev_pair.second, - cdev_info, power_rail_info)) { - LOG(FATAL) << "Could not register " << binded_cdev_pair.first - << "'s power energy source: " << binded_cdev_pair.second.power_rail; - } - } } if (name_status_pair.second.virtual_sensor_info != nullptr && @@ -368,8 +256,9 @@ ThermalHelper::ThermalHelper(const NotificationCallback &cb) sensor_info_map_[name_status_pair.second.virtual_sensor_info->trigger_sensor] .is_watch = true; } else { - LOG(FATAL) << "Could not find " << name_status_pair.first << "'s trigger sensor: " - << name_status_pair.second.virtual_sensor_info->trigger_sensor; + LOG(FATAL) << name_status_pair.first << "'s trigger sensor: " + << name_status_pair.second.virtual_sensor_info->trigger_sensor + << " is invalid"; } } } @@ -446,16 +335,16 @@ bool ThermalHelper::readTemperature(std::string_view sensor_name, Temperature_1_ const SensorInfo &sensor_info = sensor_info_map_.at(sensor_name.data()); TemperatureType_1_0 type = - (static_cast<int>(sensor_info.type) > static_cast<int>(TemperatureType_1_0::SKIN)) - ? TemperatureType_1_0::UNKNOWN - : static_cast<TemperatureType_1_0>(sensor_info.type); + (static_cast<int>(sensor_info.type) > static_cast<int>(TemperatureType_1_0::SKIN)) + ? TemperatureType_1_0::UNKNOWN + : static_cast<TemperatureType_1_0>(sensor_info.type); out->type = type; out->name = sensor_name.data(); out->currentValue = temp * sensor_info.multiplier; out->throttlingThreshold = - sensor_info.hot_thresholds[static_cast<size_t>(ThrottlingSeverity::SEVERE)]; + sensor_info.hot_thresholds[static_cast<size_t>(ThrottlingSeverity::SEVERE)]; out->shutdownThreshold = - sensor_info.hot_thresholds[static_cast<size_t>(ThrottlingSeverity::SHUTDOWN)]; + sensor_info.hot_thresholds[static_cast<size_t>(ThrottlingSeverity::SHUTDOWN)]; out->vrThrottlingThreshold = sensor_info.vr_threshold; return true; @@ -479,7 +368,7 @@ bool ThermalHelper::readTemperature( out->value = temp * sensor_info.multiplier; std::pair<ThrottlingSeverity, ThrottlingSeverity> status = - std::make_pair(ThrottlingSeverity::NONE, ThrottlingSeverity::NONE); + std::make_pair(ThrottlingSeverity::NONE, ThrottlingSeverity::NONE); // Only update status if the thermal sensor is being monitored if (sensor_info.is_watch) { ThrottlingSeverity prev_hot_severity, prev_cold_severity; @@ -498,8 +387,8 @@ bool ThermalHelper::readTemperature( } out->throttlingStatus = static_cast<size_t>(status.first) > static_cast<size_t>(status.second) - ? status.first - : status.second; + ? status.first + : status.second; return true; } @@ -525,216 +414,34 @@ bool ThermalHelper::readTemperatureThreshold(std::string_view sensor_name, return true; } -// To find the next PID target state according to the current thermal severity -size_t ThermalHelper::getTargetStateOfPID(const SensorInfo &sensor_info, - const SensorStatus &sensor_status) { - size_t target_state = 0; - - for (const auto &severity : hidl_enum_range<ThrottlingSeverity>()) { - size_t state = static_cast<size_t>(severity); - if (std::isnan(sensor_info.throttling_info->s_power[state])) { - continue; - } - target_state = state; - if (severity > sensor_status.severity) { - break; - } - } - return target_state; -} - -// Return the power budget which is computed by PID algorithm -float ThermalHelper::pidPowerCalculator(const Temperature_2_0 &temp, const SensorInfo &sensor_info, - SensorStatus *sensor_status, - std::chrono::milliseconds time_elapsed_ms, - size_t target_state) { - float p = 0, i = 0, d = 0; - float power_budget = std::numeric_limits<float>::max(); - - ATRACE_CALL(); - LOG(VERBOSE) << "PID target state=" << target_state; - if (!target_state || (sensor_status->severity == ThrottlingSeverity::NONE)) { - sensor_status->err_integral = 0; - sensor_status->prev_err = NAN; - return power_budget; - } - - // Compute PID - float err = sensor_info.hot_thresholds[target_state] - temp.value; - p = err * (err < 0 ? sensor_info.throttling_info->k_po[target_state] - : sensor_info.throttling_info->k_pu[target_state]); - i = sensor_status->err_integral * sensor_info.throttling_info->k_i[target_state]; - if (err < sensor_info.throttling_info->i_cutoff[target_state]) { - float i_next = i + err * sensor_info.throttling_info->k_i[target_state]; - if (abs(i_next) < sensor_info.throttling_info->i_max[target_state]) { - i = i_next; - sensor_status->err_integral += err; - } - } - - if (!std::isnan(sensor_status->prev_err) && - time_elapsed_ms != std::chrono::milliseconds::zero()) { - d = sensor_info.throttling_info->k_d[target_state] * (err - sensor_status->prev_err) / - time_elapsed_ms.count(); - } - - sensor_status->prev_err = err; - // Calculate power budget - power_budget = sensor_info.throttling_info->s_power[target_state] + p + i + d; - 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]; - } - - LOG(VERBOSE) << "power_budget=" << power_budget << " err=" << err - << " err_integral=" << sensor_status->err_integral - << " s_power=" << sensor_info.throttling_info->s_power[target_state] - << " time_elpased_ms=" << time_elapsed_ms.count() << " p=" << p << " i=" << i - << " d=" << d; - - return power_budget; -} - -bool ThermalHelper::requestCdevByPower(std::string_view sensor_name, SensorStatus *sensor_status, - const SensorInfo &sensor_info, float total_power_budget, - size_t target_state) { - float total_weight = 0, cdev_power_budget; - size_t j; - - ATRACE_CALL(); - for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) { - if (!std::isnan(binded_cdev_info_pair.second.cdev_weight_for_pid[target_state])) { - total_weight += binded_cdev_info_pair.second.cdev_weight_for_pid[target_state]; - } - } - - if (!total_weight) { - LOG(ERROR) << "Sensor: " << sensor_name.data() << " total weight value is zero"; - return false; - } - - // Map cdev state by power - for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) { - const auto cdev_weight = binded_cdev_info_pair.second.cdev_weight_for_pid[target_state]; - if (!std::isnan(cdev_weight)) { - cdev_power_budget = total_power_budget * (cdev_weight / total_weight); - - const CdevInfo &cdev_info_pair = - cooling_device_info_map_.at(binded_cdev_info_pair.first); - for (j = 0; j < cdev_info_pair.state2power.size() - 1; ++j) { - if (cdev_power_budget > cdev_info_pair.state2power[j]) { - break; - } - } - sensor_status->pid_request_map.at(binded_cdev_info_pair.first) = static_cast<int>(j); - LOG(VERBOSE) << "Power allocator: Sensor " << sensor_name.data() << " allocate " - << cdev_power_budget << "mW to " << binded_cdev_info_pair.first - << "(cdev_weight=" << cdev_weight << ") update state to " << j; - } - } - return true; -} - -void ThermalHelper::requestCdevBySeverity(std::string_view sensor_name, SensorStatus *sensor_status, - const SensorInfo &sensor_info) { - ATRACE_CALL(); - for (auto const &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) { - sensor_status->hard_limit_request_map.at(binded_cdev_info_pair.first) = - binded_cdev_info_pair.second - .limit_info[static_cast<size_t>(sensor_status->severity)]; - LOG(VERBOSE) << "Hard Limit: Sensor " << sensor_name.data() << " update cdev " - << binded_cdev_info_pair.first << " to " - << sensor_status->hard_limit_request_map.at(binded_cdev_info_pair.first); - } -} - -void ThermalHelper::computeCoolingDevicesRequest( - std::string_view sensor_name, const SensorInfo &sensor_info, - const SensorStatus &sensor_status, std::vector<std::string> *cooling_devices_to_update) { - int release_step = 0; - - ATRACE_CALL(); - std::unique_lock<std::shared_mutex> _lock(cdev_status_map_mutex_); - for (auto &cdev_request_pair : cdev_status_map_) { - if (!cdev_request_pair.second.count(sensor_name.data())) { - continue; - } - int pid_request = 0; - int hard_limit_request = 0; - const auto &binded_cdev_info = - sensor_info.throttling_info->binded_cdev_info_map.at(cdev_request_pair.first); - const auto cdev_ceiling = - binded_cdev_info.cdev_ceiling[static_cast<size_t>(sensor_status.severity)]; - const auto cdev_floor = - binded_cdev_info - .cdev_floor_with_power_link[static_cast<size_t>(sensor_status.severity)]; - release_step = 0; - - if (sensor_status.pid_request_map.count(cdev_request_pair.first)) { - pid_request = sensor_status.pid_request_map.at(cdev_request_pair.first); - } - - if (sensor_status.hard_limit_request_map.count(cdev_request_pair.first)) { - hard_limit_request = sensor_status.hard_limit_request_map.at(cdev_request_pair.first); - } - - release_step = power_files_.getReleaseStep(sensor_name, cdev_request_pair.first); - LOG(VERBOSE) << "Sensor: " << sensor_name.data() << " binded cooling device " - << cdev_request_pair.first << "'s pid_request=" << pid_request - << " hard_limit_request=" << hard_limit_request - << " release_step=" << release_step - << " cdev_floor_with_power_link=" << cdev_floor - << " cdev_ceiling=" << cdev_ceiling; - - auto request_state = std::max(pid_request, hard_limit_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 - if (request_state < cdev_floor) { - request_state = cdev_floor; - } - } - - if (request_state > cdev_ceiling) { - request_state = cdev_ceiling; - } - if (cdev_request_pair.second.at(sensor_name.data()) != request_state) { - cdev_request_pair.second.at(sensor_name.data()) = request_state; - cooling_devices_to_update->emplace_back(cdev_request_pair.first); - LOG(INFO) << "Sensor: " << sensor_name.data() << " request " << cdev_request_pair.first - << " to " << request_state; - } - } -} - void ThermalHelper::updateCoolingDevices(const std::vector<std::string> &updated_cdev) { int max_state; + const auto &thermal_throttling_status_map = thermal_throttling_.GetThermalThrottlingStatusMap(); + for (const auto &target_cdev : updated_cdev) { max_state = 0; - const CdevRequestStatus &cdev_status = cdev_status_map_.at(target_cdev); - for (auto &sensor_request_pair : cdev_status) { - if (sensor_request_pair.second > max_state) { - max_state = sensor_request_pair.second; + for (const auto &thermal_throttling_status_pair : thermal_throttling_status_map) { + if (!thermal_throttling_status_pair.second.cdev_status_map.count(target_cdev)) { + continue; + } + const auto state = + thermal_throttling_status_pair.second.cdev_status_map.at(target_cdev); + if (state > max_state) { + max_state = state; } } if (cooling_devices_.writeCdevFile(target_cdev, std::to_string(max_state))) { - LOG(VERBOSE) << "Successfully update cdev " << target_cdev << " sysfs to " << max_state; + LOG(INFO) << "Successfully update cdev " << target_cdev << " sysfs to " << max_state; } } } std::pair<ThrottlingSeverity, ThrottlingSeverity> ThermalHelper::getSeverityFromThresholds( - const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds, - const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis, - ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity, - float value) const { + const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds, + const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis, + ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity, + float value) const { ThrottlingSeverity ret_hot = ThrottlingSeverity::NONE; ThrottlingSeverity ret_hot_hysteresis = ThrottlingSeverity::NONE; ThrottlingSeverity ret_cold = ThrottlingSeverity::NONE; @@ -981,10 +688,10 @@ bool ThermalHelper::fillTemperatures(hidl_vec<Temperature_1_0> *temperatures) { } else { LOG(ERROR) << __func__ << ": error reading temperature for sensor: " << name_info_pair.first; + return false; } } *temperatures = ret; - return ret.size() > 0; } @@ -994,11 +701,9 @@ bool ThermalHelper::fillCurrentTemperatures(bool filterType, bool filterCallback std::vector<Temperature_2_0> ret; for (const auto &name_info_pair : sensor_info_map_) { Temperature_2_0 temp; - if (name_info_pair.second.is_hidden) { continue; } - if (filterType && name_info_pair.second.type != type) { continue; } @@ -1021,11 +726,9 @@ bool ThermalHelper::fillTemperatureThresholds(bool filterType, TemperatureType_2 std::vector<TemperatureThreshold> ret; for (const auto &name_info_pair : sensor_info_map_) { TemperatureThreshold temp; - if (name_info_pair.second.is_hidden) { continue; } - if (filterType && name_info_pair.second.type != type) { continue; } @@ -1168,15 +871,14 @@ std::chrono::milliseconds ThermalHelper::thermalWatcherCallbackFunc( const std::set<std::string> &uevent_sensors) { std::vector<Temperature_2_0> temps; std::vector<std::string> cooling_devices_to_update; - std::set<std::string> updated_power_rails; boot_clock::time_point now = boot_clock::now(); auto min_sleep_ms = std::chrono::milliseconds::max(); + bool power_data_is_updated = false; ATRACE_CALL(); for (auto &name_status_pair : sensor_status_map_) { bool force_update = false; bool force_sysfs = false; - bool severity_changed = false; Temperature_2_0 temp; TemperatureThreshold threshold; SensorStatus &sensor_status = name_status_pair.second; @@ -1264,7 +966,6 @@ std::chrono::milliseconds ThermalHelper::thermalWatcherCallbackFunc( } if (temp.throttlingStatus != sensor_status.severity) { temps.push_back(temp); - severity_changed = true; sensor_status.severity = temp.throttlingStatus; sleep_ms = (sensor_status.severity != ThrottlingSeverity::NONE) ? sensor_info.passive_delay @@ -1272,58 +973,29 @@ std::chrono::milliseconds ThermalHelper::thermalWatcherCallbackFunc( } } - if (sensor_status.severity != ThrottlingSeverity::NONE) { - LOG(INFO) << temp.name << ": " << temp.value; - } else { - LOG(VERBOSE) << temp.name << ": " << temp.value; - } - - // Start PID computation - if (sensor_status.pid_request_map.size()) { - size_t target_state = getTargetStateOfPID(sensor_info, sensor_status); - float power_budget = pidPowerCalculator(temp, sensor_info, &sensor_status, - time_elapsed_ms, target_state); - if (!requestCdevByPower(name_status_pair.first, &sensor_status, sensor_info, - power_budget, target_state)) { - LOG(ERROR) << "Sensor " << temp.name << " PID request cdev failed"; - } - } - - if (sensor_status.hard_limit_request_map.size()) { - // Start hard limit computation - requestCdevBySeverity(name_status_pair.first, &sensor_status, sensor_info); + if (!power_data_is_updated) { + power_files_.refreshPowerStatus(); + power_data_is_updated = true; } - // Aggregate cooling device request - if (sensor_status.pid_request_map.size() || sensor_status.hard_limit_request_map.size()) { - if (sensor_status.severity == ThrottlingSeverity::NONE) { - power_files_.setPowerDataToDefault(name_status_pair.first); - } else { - for (const auto &binded_cdev_info_pair : - sensor_info.throttling_info->binded_cdev_info_map) { - if (binded_cdev_info_pair.second.power_rail != "") { - const auto &power_rail_info = - power_rail_info_map_.at(binded_cdev_info_pair.second.power_rail); - - if (power_files_.throttlingReleaseUpdate( - name_status_pair.first, binded_cdev_info_pair.first, - sensor_status.severity, time_elapsed_ms, - binded_cdev_info_pair.second, power_rail_info, - !updated_power_rails.count( - binded_cdev_info_pair.second.power_rail), - severity_changed)) { - updated_power_rails.insert(binded_cdev_info_pair.second.power_rail); - } - } - } - } - computeCoolingDevicesRequest(name_status_pair.first, sensor_info, sensor_status, - &cooling_devices_to_update); + if (sensor_status.severity == ThrottlingSeverity::NONE) { + LOG(VERBOSE) << temp.name << ": " << temp.value; + thermal_throttling_.clearThrottlingData(name_status_pair.first, sensor_info); + } else { + LOG(INFO) << temp.name << ": " << temp.value; + // update thermal throttling request + thermal_throttling_.thermalThrottlingUpdate( + temp, sensor_info, sensor_status.severity, time_elapsed_ms, + power_files_.GetPowerStatusMap(), cooling_device_info_map_); } + thermal_throttling_.computeCoolingDevicesRequest(name_status_pair.first, sensor_info, + sensor_status.severity, + &cooling_devices_to_update); if (min_sleep_ms > sleep_ms) { min_sleep_ms = sleep_ms; } + LOG(VERBOSE) << "Sensor " << name_status_pair.first << ": sleep_ms=" << sleep_ms.count() << ", min_sleep_ms voting result=" << min_sleep_ms.count(); sensor_status.last_update_time = now; @@ -1345,8 +1017,7 @@ std::chrono::milliseconds ThermalHelper::thermalWatcherCallbackFunc( } } - power_files_.clearEnergyInfoMap(); - return min_sleep_ms < kMinPollIntervalMs ? kMinPollIntervalMs : min_sleep_ms; + return min_sleep_ms; } bool ThermalHelper::connectToPowerHal() { diff --git a/thermal/thermal-helper.h b/thermal/thermal-helper.h index c9d0e49..8e047f1 100644 --- a/thermal/thermal-helper.h +++ b/thermal/thermal-helper.h @@ -26,13 +26,11 @@ #include <unordered_map> #include <vector> -#include <aidl/android/hardware/power/IPower.h> -#include <aidl/google/hardware/power/extension/pixel/IPowerExt.h> -#include <android/hardware/thermal/2.0/IThermal.h> - -#include "utils/config_parser.h" #include "utils/power_files.h" +#include "utils/powerhal_helper.h" #include "utils/thermal_files.h" +#include "utils/thermal_info.h" +#include "utils/thermal_throttling.h" #include "utils/thermal_watcher.h" namespace android { @@ -41,8 +39,6 @@ namespace thermal { namespace V2_0 { namespace implementation { -using ::aidl::android::hardware::power::IPower; -using ::aidl::google::hardware::power::extension::pixel::IPowerExt; using ::android::hardware::hidl_vec; using ::android::hardware::thermal::V1_0::CpuUsage; using ::android::hardware::thermal::V2_0::CoolingType; @@ -58,7 +54,6 @@ using ::android::hardware::thermal::V2_0::ThrottlingSeverity; using NotificationCallback = std::function<void(const Temperature_2_0 &t)>; using NotificationTime = std::chrono::time_point<std::chrono::steady_clock>; -using CdevRequestStatus = std::unordered_map<std::string, int>; // Get thermal_zone type bool getThermalZoneTypeById(int tz_id, std::string *); @@ -74,29 +69,7 @@ struct SensorStatus { ThrottlingSeverity prev_cold_severity; ThrottlingSeverity prev_hint_severity; boot_clock::time_point last_update_time; - std::unordered_map<std::string, int> pid_request_map; - std::unordered_map<std::string, int> hard_limit_request_map; ThermalSample thermal_cached; - float err_integral; - float prev_err; -}; - -class PowerHalService { - public: - PowerHalService(); - ~PowerHalService() = default; - bool connect(); - bool isAidlPowerHalExist() { return power_hal_aidl_exist_; } - bool isModeSupported(const std::string &type, const ThrottlingSeverity &t); - bool isPowerHalConnected() { return power_hal_aidl_ != nullptr; } - bool isPowerHalExtConnected() { return power_hal_ext_aidl_ != nullptr; } - void setMode(const std::string &type, const ThrottlingSeverity &t, const bool &enable); - - private: - bool power_hal_aidl_exist_; - std::shared_ptr<IPower> power_hal_aidl_; - std::shared_ptr<IPowerExt> power_hal_ext_aidl_; - std::mutex lock_; }; class ThermalHelper { @@ -136,30 +109,26 @@ class ThermalHelper { const std::unordered_map<std::string, CdevInfo> &GetCdevInfoMap() const { return cooling_device_info_map_; } - // Get PowerRailInfo Map - const std::unordered_map<std::string, PowerRailInfo> &GetPowerRailInfoMap() const { - return power_rail_info_map_; - } // Get SensorStatus Map const std::unordered_map<std::string, SensorStatus> &GetSensorStatusMap() const { std::shared_lock<std::shared_mutex> _lock(sensor_status_map_mutex_); return sensor_status_map_; } - // Get CdevStatus Map - const std::unordered_map<std::string, CdevRequestStatus> &GetCdevStatusMap() const { - std::shared_lock<std::shared_mutex> _lock(cdev_status_map_mutex_); - return cdev_status_map_; + // Get Thermal Throttling Map + const std::unordered_map<std::string, ThermalThrottlingStatus> &GetThermalThrottlingStatusMap() + const { + return thermal_throttling_.GetThermalThrottlingStatusMap(); } - // Get ThrottlingRelease Map - const std::unordered_map<std::string, CdevReleaseStatus> &GetThrottlingReleaseMap() const { - return power_files_.GetThrottlingReleaseMap(); + + // Get PowerRailInfo Map + const std::unordered_map<std::string, PowerRailInfo> &GetPowerRailInfoMap() const { + return power_files_.GetPowerRailInfoMap(); } // Get PowerStatus Map - const std::unordered_map<std::string, PowerStatusMap> &GetPowerStatusMap() const { + const std::unordered_map<std::string, PowerStatus> &GetPowerStatusMap() const { return power_files_.GetPowerStatusMap(); } - void sendPowerExtHint(const Temperature_2_0 &t); bool isAidlPowerHalExist() { return power_hal_service_.isAidlPowerHalExist(); } bool isPowerHalConnected() { return power_hal_service_.isPowerHalConnected(); } @@ -177,46 +146,30 @@ class ThermalHelper { const std::set<std::string> &uevent_sensors); // Return hot and cold severity status as std::pair std::pair<ThrottlingSeverity, ThrottlingSeverity> getSeverityFromThresholds( - const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds, - const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis, - ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity, - float value) const; + const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds, + const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis, + ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity, + float value) const; // Read temperature data according to thermal sensor's info bool readThermalSensor(std::string_view sensor_name, float *temp, const bool force_sysfs); - // Return the target state of PID algorithm - size_t getTargetStateOfPID(const SensorInfo &sensor_info, const SensorStatus &sensor_status); - // Return the power budget which is computed by PID algorithm - float pidPowerCalculator(const Temperature_2_0 &temp, const SensorInfo &sensor_info, - SensorStatus *sensor_status, - const std::chrono::milliseconds time_elapsed_ms, size_t target_state); bool connectToPowerHal(); void updateSupportedPowerHints(); - bool requestCdevByPower(std::string_view sensor_name, SensorStatus *sensor_status, - const SensorInfo &sensor_info, float total_power_budget, - size_t target_state); - void requestCdevBySeverity(std::string_view sensor_name, SensorStatus *sensor_status, - const SensorInfo &sensor_info); - void computeCoolingDevicesRequest(std::string_view sensor_name, const SensorInfo &sensor_info, - const SensorStatus &sensor_status, - std::vector<std::string> *cooling_devices_to_update); void updateCoolingDevices(const std::vector<std::string> &cooling_devices_to_update); sp<ThermalWatcher> thermal_watcher_; PowerFiles power_files_; ThermalFiles thermal_sensors_; ThermalFiles cooling_devices_; + ThermalThrottling thermal_throttling_; bool is_initialized_; const NotificationCallback cb_; std::unordered_map<std::string, CdevInfo> cooling_device_info_map_; std::unordered_map<std::string, SensorInfo> sensor_info_map_; - std::unordered_map<std::string, PowerRailInfo> power_rail_info_map_; std::unordered_map<std::string, std::map<ThrottlingSeverity, ThrottlingSeverity>> supported_powerhint_map_; PowerHalService power_hal_service_; mutable std::shared_mutex sensor_status_map_mutex_; std::unordered_map<std::string, SensorStatus> sensor_status_map_; - mutable std::shared_mutex cdev_status_map_mutex_; - std::unordered_map<std::string, CdevRequestStatus> cdev_status_map_; }; } // namespace implementation diff --git a/thermal/utils/power_files.cpp b/thermal/utils/power_files.cpp index a559454..3561e84 100644 --- a/thermal/utils/power_files.cpp +++ b/thermal/utils/power_files.cpp @@ -39,56 +39,20 @@ constexpr std::string_view kEnergyValueNode("energy_value"); using android::base::ReadFileToString; using android::base::StringPrintf; -void PowerFiles::setPowerDataToDefault(std::string_view sensor_name) { - std::unique_lock<std::shared_mutex> _lock(throttling_release_map_mutex_); - if (!throttling_release_map_.count(sensor_name.data()) || - !power_status_map_.count(sensor_name.data())) { - return; - } - - auto &cdev_release_map = throttling_release_map_.at(sensor_name.data()); - PowerSample power_sample = {}; - - for (auto &power_status_pair : power_status_map_.at(sensor_name.data())) { - for (size_t i = 0; i < power_status_pair.second.power_history.size(); ++i) { - for (size_t j = 0; j < power_status_pair.second.power_history[i].size(); ++j) { - power_status_pair.second.power_history[i].pop(); - power_status_pair.second.power_history[i].emplace(power_sample); - } - } - power_status_pair.second.last_updated_avg_power = NAN; - } - - for (auto &cdev_release_pair : cdev_release_map) { - cdev_release_pair.second.release_step = 0; +bool PowerFiles::registerPowerRailsToWatch(std::string_view config_path) { + if (!ParsePowerRailInfo(config_path, &power_rail_info_map_)) { + LOG(ERROR) << "Failed to parse power rail info config"; + return false; } -} -int PowerFiles::getReleaseStep(std::string_view sensor_name, std::string_view cdev_name) { - int release_step = 0; - std::shared_lock<std::shared_mutex> _lock(throttling_release_map_mutex_); - - if (throttling_release_map_.count(sensor_name.data()) && - throttling_release_map_[sensor_name.data()].count(cdev_name.data())) { - release_step = throttling_release_map_[sensor_name.data()][cdev_name.data()].release_step; + if (!power_rail_info_map_.size()) { + LOG(INFO) << " No power rail info config found"; + return true; } - return release_step; -} - -bool PowerFiles::registerPowerRailsToWatch(std::string_view sensor_name, std::string_view cdev_name, - const BindedCdevInfo &binded_cdev_info, - const CdevInfo &cdev_info, - const PowerRailInfo &power_rail_info) { - std::vector<std::queue<PowerSample>> power_history; - PowerSample power_sample = { - .energy_counter = 0, - .duration = 0, - }; - - if (throttling_release_map_.count(sensor_name.data()) && - throttling_release_map_[sensor_name.data()].count(binded_cdev_info.power_rail)) { - return true; + if (!findEnergySourceToWatch()) { + LOG(ERROR) << "Cannot find energy source"; + return false; } if (!energy_info_map_.size() && !updateEnergyValues()) { @@ -96,50 +60,59 @@ bool PowerFiles::registerPowerRailsToWatch(std::string_view sensor_name, std::st return false; } - if (power_rail_info.virtual_power_rail_info != nullptr && - power_rail_info.virtual_power_rail_info->linked_power_rails.size()) { - for (size_t i = 0; i < power_rail_info.virtual_power_rail_info->linked_power_rails.size(); - ++i) { - if (energy_info_map_.count( - power_rail_info.virtual_power_rail_info->linked_power_rails[i])) { + for (const auto &power_rail_info_pair : power_rail_info_map_) { + std::vector<std::queue<PowerSample>> power_history; + if (!power_rail_info_pair.second.power_sample_count || + power_rail_info_pair.second.power_sample_delay == std::chrono::milliseconds::max()) { + continue; + } + + PowerSample power_sample = { + .energy_counter = 0, + .duration = 0, + }; + + if (power_rail_info_pair.second.virtual_power_rail_info != nullptr && + power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails.size()) { + for (size_t i = 0; + i < power_rail_info_pair.second.virtual_power_rail_info->linked_power_rails.size(); + ++i) { + if (!energy_info_map_.count(power_rail_info_pair.second.virtual_power_rail_info + ->linked_power_rails[i])) { + LOG(ERROR) << " Could not find energy source " + << power_rail_info_pair.second.virtual_power_rail_info + ->linked_power_rails[i]; + return false; + } power_history.emplace_back(std::queue<PowerSample>()); - for (int j = 0; j < power_rail_info.power_sample_count; j++) { + for (int j = 0; j < power_rail_info_pair.second.power_sample_count; j++) { power_history[i].emplace(power_sample); } + } + } else { + if (energy_info_map_.count(power_rail_info_pair.first)) { + power_history.emplace_back(std::queue<PowerSample>()); + for (int j = 0; j < power_rail_info_pair.second.power_sample_count; j++) { + power_history[0].emplace(power_sample); + } } else { - LOG(ERROR) << "Could not find power rail " - << power_rail_info.virtual_power_rail_info->linked_power_rails[i]; + LOG(ERROR) << "Could not find energy source " << power_rail_info_pair.first; return false; } } - } else { - if (energy_info_map_.count(power_rail_info.rail)) { - power_history.emplace_back(std::queue<PowerSample>()); - for (int j = 0; j < power_rail_info.power_sample_count; j++) { - power_history[0].emplace(power_sample); - } + + if (power_history.size()) { + power_status_map_[power_rail_info_pair.first] = { + .power_history = power_history, + .last_update_time = boot_clock::time_point::min(), + .last_updated_avg_power = NAN, + }; } else { - LOG(ERROR) << "Could not find power rail " << power_rail_info.rail; + LOG(ERROR) << "power history size is zero"; return false; } + LOG(INFO) << "Successfully to register power rail " << power_rail_info_pair.first; } - - if (power_history.size()) { - throttling_release_map_[sensor_name.data()][cdev_name.data()] = { - .release_step = 0, - .max_release_step = cdev_info.max_state, - }; - power_status_map_[sensor_name.data()][binded_cdev_info.power_rail] = { - .power_history = power_history, - .time_remaining = power_rail_info.power_sample_delay, - .last_updated_avg_power = NAN, - }; - } else { - return false; - } - - LOG(INFO) << "Sensor " << sensor_name.data() << " successfully registers power rail " - << binded_cdev_info.power_rail << " for cooling device " << cdev_name.data(); return true; } @@ -179,10 +152,6 @@ bool PowerFiles::findEnergySourceToWatch(void) { return true; } -void PowerFiles::clearEnergyInfoMap(void) { - energy_info_map_.clear(); -} - bool PowerFiles::updateEnergyValues(void) { std::string deviceEnergyContent; std::string deviceEnergyContents; @@ -200,7 +169,6 @@ bool PowerFiles::updateEnergyValues(void) { std::istringstream energyData(deviceEnergyContents); - clearEnergyInfoMap(); while (std::getline(energyData, line)) { /* Read rail energy */ uint64_t energy_counter = 0; @@ -241,215 +209,128 @@ bool PowerFiles::updateEnergyValues(void) { return true; } -bool PowerFiles::getAveragePower(std::string_view power_rail, - std::queue<PowerSample> *power_history, bool power_sample_update, - float *avg_power) { - bool ret = true; - const auto last_sample = power_history->front(); +float PowerFiles::updateAveragePower(std::string_view power_rail, + std::queue<PowerSample> *power_history) { + float avg_power = NAN; if (!energy_info_map_.count(power_rail.data())) { LOG(ERROR) << " Could not find power rail " << power_rail.data(); - return false; + return avg_power; } + const auto last_sample = power_history->front(); const auto curr_sample = energy_info_map_.at(power_rail.data()); const auto duration = curr_sample.duration - last_sample.duration; const auto deltaEnergy = curr_sample.energy_counter - last_sample.energy_counter; if (!last_sample.duration) { - LOG(VERBOSE) << "Power rail " << power_rail.data() << ": the last energy timestamp is zero"; + LOG(VERBOSE) << "Power rail " << power_rail.data() + << ": all power samples have not been collected yet"; } else if (duration <= 0 || deltaEnergy < 0) { LOG(ERROR) << "Power rail " << power_rail.data() << " is invalid: duration = " << duration << ", deltaEnergy = " << deltaEnergy; - ret = false; + return avg_power; } else { - *avg_power = static_cast<float>(deltaEnergy) / static_cast<float>(duration); - LOG(VERBOSE) << "Power rail " << power_rail.data() << ", avg power = " << *avg_power + avg_power = static_cast<float>(deltaEnergy) / static_cast<float>(duration); + LOG(VERBOSE) << "Power rail " << power_rail.data() << ", avg power = " << avg_power << ", duration = " << duration << ", deltaEnergy = " << deltaEnergy; } - if (power_sample_update) { - power_history->pop(); - power_history->push(curr_sample); - } + power_history->pop(); + power_history->push(curr_sample); - return ret; + return avg_power; } -bool PowerFiles::computeAveragePower(const PowerRailInfo &power_rail_info, - PowerStatus *power_status, bool power_sample_update, - float *avg_power) { - bool ret = true; - - float avg_power_val = -1; - float offset = power_rail_info.virtual_power_rail_info->offset; - for (size_t i = 0; i < power_rail_info.virtual_power_rail_info->linked_power_rails.size(); - i++) { - float coefficient = power_rail_info.virtual_power_rail_info->coefficients[i]; - float avg_power_number = -1; - if (!getAveragePower(power_rail_info.virtual_power_rail_info->linked_power_rails[i], - &power_status->power_history[i], power_sample_update, - &avg_power_number)) { - ret = false; - continue; - } else if (avg_power_number < 0) { - continue; - } - switch (power_rail_info.virtual_power_rail_info->formula) { - case FormulaOption::COUNT_THRESHOLD: - if ((coefficient < 0 && avg_power_number < -coefficient) || - (coefficient >= 0 && avg_power_number >= coefficient)) - avg_power_val += 1; - break; - case FormulaOption::WEIGHTED_AVG: - avg_power_val += avg_power_number * coefficient; - break; - case FormulaOption::MAXIMUM: - if (i == 0) - avg_power_val = std::numeric_limits<float>::lowest(); - if (avg_power_number * coefficient > avg_power_val) - avg_power_val = avg_power_number * coefficient; - break; - case FormulaOption::MINIMUM: - if (i == 0) - avg_power_val = std::numeric_limits<float>::max(); - if (avg_power_number * coefficient < avg_power_val) - avg_power_val = avg_power_number * coefficient; - break; - default: - break; - } - } - if (avg_power_val >= 0) { - avg_power_val = avg_power_val + offset; - } +float PowerFiles::updatePowerRail(std::string_view power_rail) { + float avg_power = NAN; - *avg_power = avg_power_val; - return ret; -} - -bool PowerFiles::throttlingReleaseUpdate(std::string_view sensor_name, std::string_view cdev_name, - const ThrottlingSeverity severity, - const std::chrono::milliseconds time_elapsed_ms, - const BindedCdevInfo &binded_cdev_info, - const PowerRailInfo &power_rail_info, - bool power_sample_update, bool severity_changed) { - std::unique_lock<std::shared_mutex> _lock(throttling_release_map_mutex_); - float avg_power = -1; + if (!power_rail_info_map_.count(power_rail.data())) { + return avg_power; + } - ATRACE_CALL(); - if (!throttling_release_map_.count(sensor_name.data()) || - !throttling_release_map_[sensor_name.data()].count(cdev_name.data()) || - !power_status_map_.count(sensor_name.data()) || - !power_status_map_[sensor_name.data()].count(binded_cdev_info.power_rail)) { - return false; + if (!power_status_map_.count(power_rail.data())) { + return avg_power; } - auto &release_status = throttling_release_map_[sensor_name.data()].at(cdev_name.data()); - auto &power_status = power_status_map_[sensor_name.data()].at(binded_cdev_info.power_rail); + const auto &power_rail_info = power_rail_info_map_.at(power_rail.data()); + auto &power_status = power_status_map_.at(power_rail.data()); - if (power_sample_update) { - if (time_elapsed_ms > power_status.time_remaining) { - power_status.time_remaining = power_rail_info.power_sample_delay; - } else { - power_status.time_remaining = power_status.time_remaining - time_elapsed_ms; - LOG(VERBOSE) << "Power rail " << binded_cdev_info.power_rail - << " : timeout remaining = " << power_status.time_remaining.count(); - if (!severity_changed) { - return true; - } else { - // get the cached average power when thermal severity is changed - power_sample_update = false; - } - } - } else if (!severity_changed && - power_status.time_remaining != power_rail_info.power_sample_delay) { - return false; + boot_clock::time_point now = boot_clock::now(); + auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>( + now - power_status.last_update_time); + + if (power_status.last_update_time != boot_clock::time_point::min() && + time_elapsed_ms < power_rail_info.power_sample_delay) { + return power_status.last_updated_avg_power; } if (!energy_info_map_.size() && !updateEnergyValues()) { LOG(ERROR) << "Failed to update energy values"; - release_status.release_step = 0; - return false; + return avg_power; } - if (!power_sample_update && !std::isnan(power_status.last_updated_avg_power)) { - avg_power = power_status.last_updated_avg_power; + if (power_rail_info.virtual_power_rail_info == nullptr) { + avg_power = updateAveragePower(power_rail, &power_status.power_history[0]); } else { - // Return false if we cannot get the average power of the target power rail - if (!((power_rail_info.virtual_power_rail_info == nullptr) - ? getAveragePower(binded_cdev_info.power_rail, &power_status.power_history[0], - power_sample_update, &avg_power) - : computeAveragePower(power_rail_info, &power_status, power_sample_update, - &avg_power))) { - release_status.release_step = 0; - if (binded_cdev_info.throttling_with_power_link) { - release_status.release_step = release_status.max_release_step; - } - return false; - } else if (avg_power < 0) { - if (binded_cdev_info.throttling_with_power_link) { - release_status.release_step = release_status.max_release_step; + const auto offset = power_rail_info.virtual_power_rail_info->offset; + float avg_power_val = 0.0; + for (size_t i = 0; i < power_rail_info.virtual_power_rail_info->linked_power_rails.size(); + i++) { + float coefficient = power_rail_info.virtual_power_rail_info->coefficients[i]; + float avg_power_number = updateAveragePower( + power_rail_info.virtual_power_rail_info->linked_power_rails[i], + &power_status.power_history[i]); + + switch (power_rail_info.virtual_power_rail_info->formula) { + case FormulaOption::COUNT_THRESHOLD: + if ((coefficient < 0 && avg_power_number < -coefficient) || + (coefficient >= 0 && avg_power_number >= coefficient)) + avg_power_val += 1; + break; + case FormulaOption::WEIGHTED_AVG: + avg_power_val += avg_power_number * coefficient; + break; + case FormulaOption::MAXIMUM: + if (i == 0) + avg_power_val = std::numeric_limits<float>::lowest(); + if (avg_power_number * coefficient > avg_power_val) + avg_power_val = avg_power_number * coefficient; + break; + case FormulaOption::MINIMUM: + if (i == 0) + avg_power_val = std::numeric_limits<float>::max(); + if (avg_power_number * coefficient < avg_power_val) + avg_power_val = avg_power_number * coefficient; + break; + default: + break; } - return true; } + if (avg_power_val >= 0) { + avg_power_val = avg_power_val + offset; + } + + avg_power = avg_power_val; + } + + if (avg_power < 0) { + avg_power = NAN; } power_status.last_updated_avg_power = avg_power; - bool is_over_budget = true; - if (!binded_cdev_info.high_power_check) { - if (avg_power < binded_cdev_info.power_thresholds[static_cast<int>(severity)]) { - is_over_budget = false; - } - } else { - if (avg_power > binded_cdev_info.power_thresholds[static_cast<int>(severity)]) { - is_over_budget = false; - } + power_status.last_update_time = now; + return avg_power; +} + +bool PowerFiles::refreshPowerStatus(void) { + if (!updateEnergyValues()) { + LOG(ERROR) << "Failed to update energy values"; + return false; } - LOG(INFO) << "Power rail " << binded_cdev_info.power_rail << ": power threshold = " - << binded_cdev_info.power_thresholds[static_cast<int>(severity)] - << ", avg power = " << avg_power; - - switch (binded_cdev_info.release_logic) { - case ReleaseLogic::INCREASE: - if (!is_over_budget) { - if (std::abs(release_status.release_step) < - static_cast<int>(release_status.max_release_step)) { - release_status.release_step--; - } - } else { - release_status.release_step = 0; - } - break; - case ReleaseLogic::DECREASE: - if (!is_over_budget) { - if (release_status.release_step < - static_cast<int>(release_status.max_release_step)) { - release_status.release_step++; - } - } else { - release_status.release_step = 0; - } - break; - case ReleaseLogic::STEPWISE: - if (!is_over_budget) { - if (release_status.release_step < - static_cast<int>(release_status.max_release_step)) { - release_status.release_step++; - } - } else { - if (std::abs(release_status.release_step) < - static_cast<int>(release_status.max_release_step)) { - release_status.release_step--; - } - } - break; - case ReleaseLogic::RELEASE_TO_FLOOR: - release_status.release_step = is_over_budget ? 0 : release_status.max_release_step; - break; - case ReleaseLogic::NONE: - default: - break; + + for (const auto &power_status_pair : power_status_map_) { + updatePowerRail(power_status_pair.first); } return true; } diff --git a/thermal/utils/power_files.h b/thermal/utils/power_files.h index 6207ba5..65c6758 100644 --- a/thermal/utils/power_files.h +++ b/thermal/utils/power_files.h @@ -16,13 +16,16 @@ #pragma once +#include <android-base/chrono_utils.h> + +#include <chrono> #include <queue> #include <shared_mutex> #include <string> #include <unordered_map> #include <unordered_set> -#include "config_parser.h" +#include "thermal_info.h" namespace android { namespace hardware { @@ -30,26 +33,20 @@ namespace thermal { namespace V2_0 { namespace implementation { +using android::base::boot_clock; + struct PowerSample { uint64_t energy_counter; uint64_t duration; }; -struct ReleaseStatus { - int release_step; - int max_release_step; -}; - struct PowerStatus { - std::chrono::milliseconds time_remaining; + boot_clock::time_point last_update_time; // A vector to record the queues of power sample history. std::vector<std::queue<PowerSample>> power_history; float last_updated_avg_power; }; -using CdevReleaseStatus = std::unordered_map<std::string, ReleaseStatus>; -using PowerStatusMap = std::unordered_map<std::string, PowerStatus>; - // A helper class for monitoring power rails. class PowerFiles { public: @@ -58,64 +55,35 @@ class PowerFiles { // Disallow copy and assign. PowerFiles(const PowerFiles &) = delete; void operator=(const PowerFiles &) = delete; - - // Register a map for the throttling release decision of target power rail - // Return false if the power_rail is not supported. - bool registerPowerRailsToWatch(std::string_view sensor_name, std::string_view cdev_name, - const BindedCdevInfo &binded_cdev_info, - const CdevInfo &cdev_info, const PowerRailInfo &power_rail_info); - - // Find the energy source path, return false if no energy source found. - bool findEnergySourceToWatch(void); - - // Clear the data of energy_info_map_. - void clearEnergyInfoMap(void); - - // Update energy value to energy_info_map_, return false if the value is failed to update. - bool updateEnergyValues(void); - - bool getAveragePower(std::string_view power_rail, std::queue<PowerSample> *power_history, - bool power_sample_update, float *avg_power); - bool computeAveragePower(const PowerRailInfo &power_rail_info, PowerStatus *power_status, - bool power_sample_update, float *avg_power); - - // Update the throttling release status according to the average power, return true if power - // rail is updated. - bool throttlingReleaseUpdate(std::string_view sensor_name, std::string_view cdev_name, - const ThrottlingSeverity severity, - const std::chrono::milliseconds time_elapsed_ms, - const BindedCdevInfo &binded_cdev_info, - const PowerRailInfo &power_rail_info, bool power_sample_update, - bool severity_changed); - - // Get the throttling release status for the targer power rail which is binded in specific - // sensor. - int getReleaseStep(std::string_view sensor_name, std::string_view cdev_name); - - // Clear the data of throttling_release_map_. - void setPowerDataToDefault(std::string_view sensor_name); - - // Get throttling release status map - const std::unordered_map<std::string, CdevReleaseStatus> &GetThrottlingReleaseMap() const { - std::shared_lock<std::shared_mutex> _lock(throttling_release_map_mutex_); - return throttling_release_map_; - } - - // Get Power status map - const std::unordered_map<std::string, PowerStatusMap> &GetPowerStatusMap() const { + bool registerPowerRailsToWatch(std::string_view config_path); + // Update the power data from ODPM sysfs + bool refreshPowerStatus(void); + // Get power status map + const std::unordered_map<std::string, PowerStatus> &GetPowerStatusMap() const { std::shared_lock<std::shared_mutex> _lock(power_status_map_mutex_); return power_status_map_; } + // Get power rail info map + const std::unordered_map<std::string, PowerRailInfo> &GetPowerRailInfoMap() const { + return power_rail_info_map_; + } private: - // The map to record the energy info for each power rail. + // Update energy value to energy_info_map_, return false if the value is failed to update. + bool updateEnergyValues(void); + // Compute the average power for physical power rail. + float updateAveragePower(std::string_view power_rail, std::queue<PowerSample> *power_history); + // Update the power data for the target power rail. + float updatePowerRail(std::string_view power_rail); + // Find the energy source path, return false if no energy source found. + bool findEnergySourceToWatch(void); + // The map to record the energy counter for each power rail. std::unordered_map<std::string, PowerSample> energy_info_map_; - // The map to record the throttling release status for each thermal sensor. - std::unordered_map<std::string, CdevReleaseStatus> throttling_release_map_; - mutable std::shared_mutex throttling_release_map_mutex_; // The map to record the power data for each thermal sensor. - std::unordered_map<std::string, PowerStatusMap> power_status_map_; + std::unordered_map<std::string, PowerStatus> power_status_map_; mutable std::shared_mutex power_status_map_mutex_; + // The map to record the power rail information from thermal config + std::unordered_map<std::string, PowerRailInfo> power_rail_info_map_; // The set to store the energy source paths std::unordered_set<std::string> energy_path_set_; }; diff --git a/thermal/utils/powerhal_helper.cpp b/thermal/utils/powerhal_helper.cpp new file mode 100644 index 0000000..515fa2d --- /dev/null +++ b/thermal/utils/powerhal_helper.cpp @@ -0,0 +1,134 @@ +/* + * 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. + */ + +#include "powerhal_helper.h" + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <android/binder_manager.h> +#include <hidl/HidlTransportSupport.h> + +#include <iterator> +#include <set> +#include <sstream> +#include <thread> +#include <vector> + +#include "thermal_info.h" +#include "thermal_throttling.h" + +namespace android { +namespace hardware { +namespace thermal { +namespace V2_0 { +namespace implementation { + +using android::base::StringPrintf; + +PowerHalService::PowerHalService() + : power_hal_aidl_exist_(true), power_hal_aidl_(nullptr), power_hal_ext_aidl_(nullptr) { + connect(); +} + +bool PowerHalService::connect() { + std::lock_guard<std::mutex> lock(lock_); + if (!power_hal_aidl_exist_) + return false; + + if (power_hal_aidl_ != nullptr) + return true; + + const std::string kInstance = std::string(IPower::descriptor) + "/default"; + ndk::SpAIBinder power_binder = ndk::SpAIBinder(AServiceManager_getService(kInstance.c_str())); + ndk::SpAIBinder ext_power_binder; + + if (power_binder.get() == nullptr) { + LOG(ERROR) << "Cannot get Power Hal Binder"; + power_hal_aidl_exist_ = false; + return false; + } + + power_hal_aidl_ = IPower::fromBinder(power_binder); + + if (power_hal_aidl_ == nullptr) { + power_hal_aidl_exist_ = false; + LOG(ERROR) << "Cannot get Power Hal AIDL" << kInstance.c_str(); + return false; + } + + if (STATUS_OK != AIBinder_getExtension(power_binder.get(), ext_power_binder.getR()) || + ext_power_binder.get() == nullptr) { + LOG(ERROR) << "Cannot get Power Hal Extension Binder"; + power_hal_aidl_exist_ = false; + return false; + } + + power_hal_ext_aidl_ = IPowerExt::fromBinder(ext_power_binder); + if (power_hal_ext_aidl_ == nullptr) { + LOG(ERROR) << "Cannot get Power Hal Extension AIDL"; + power_hal_aidl_exist_ = false; + } + + return true; +} + +bool PowerHalService::isModeSupported(const std::string &type, const ThrottlingSeverity &t) { + bool isSupported = false; + if (!isPowerHalConnected()) { + return false; + } + std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str()); + lock_.lock(); + if (!power_hal_ext_aidl_->isModeSupported(power_hint, &isSupported).isOk()) { + LOG(ERROR) << "Fail to check supported mode, Hint: " << power_hint; + power_hal_aidl_exist_ = false; + power_hal_ext_aidl_ = nullptr; + power_hal_aidl_ = nullptr; + lock_.unlock(); + return false; + } + lock_.unlock(); + return isSupported; +} + +void PowerHalService::setMode(const std::string &type, const ThrottlingSeverity &t, + const bool &enable) { + if (!isPowerHalConnected()) { + return; + } + + std::string power_hint = StringPrintf("THERMAL_%s_%s", type.c_str(), toString(t).c_str()); + LOG(INFO) << "Send Hint " << power_hint << " Enable: " << std::boolalpha << enable; + lock_.lock(); + if (!power_hal_ext_aidl_->setMode(power_hint, enable).isOk()) { + LOG(ERROR) << "Fail to set mode, Hint: " << power_hint; + power_hal_aidl_exist_ = false; + power_hal_ext_aidl_ = nullptr; + power_hal_aidl_ = nullptr; + lock_.unlock(); + return; + } + lock_.unlock(); +} + +} // namespace implementation +} // namespace V2_0 +} // namespace thermal +} // namespace hardware +} // namespace android diff --git a/thermal/utils/powerhal_helper.h b/thermal/utils/powerhal_helper.h new file mode 100644 index 0000000..761e315 --- /dev/null +++ b/thermal/utils/powerhal_helper.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#pragma once + +#include <aidl/android/hardware/power/IPower.h> +#include <aidl/google/hardware/power/extension/pixel/IPowerExt.h> +#include <android/hardware/thermal/2.0/IThermal.h> + +#include <queue> +#include <shared_mutex> +#include <string> +#include <unordered_map> +#include <unordered_set> + +namespace android { +namespace hardware { +namespace thermal { +namespace V2_0 { +namespace implementation { + +using ::aidl::android::hardware::power::IPower; +using ::aidl::google::hardware::power::extension::pixel::IPowerExt; + +using ::android::hardware::hidl_vec; +using ::android::hardware::thermal::V2_0::IThermal; +using Temperature_2_0 = ::android::hardware::thermal::V2_0::Temperature; +using ::android::hardware::thermal::V2_0::TemperatureThreshold; +using ::android::hardware::thermal::V2_0::ThrottlingSeverity; +using CdevRequestStatus = std::unordered_map<std::string, int>; + +class PowerHalService { + public: + PowerHalService(); + ~PowerHalService() = default; + bool connect(); + bool isAidlPowerHalExist() { return power_hal_aidl_exist_; } + bool isModeSupported(const std::string &type, const ThrottlingSeverity &t); + bool isPowerHalConnected() { return power_hal_aidl_ != nullptr; } + bool isPowerHalExtConnected() { return power_hal_ext_aidl_ != nullptr; } + void setMode(const std::string &type, const ThrottlingSeverity &t, const bool &enable); + + private: + bool power_hal_aidl_exist_; + std::shared_ptr<IPower> power_hal_aidl_; + std::shared_ptr<IPowerExt> power_hal_ext_aidl_; + std::mutex lock_; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace thermal +} // namespace hardware +} // namespace android diff --git a/thermal/utils/thermal_files.cpp b/thermal/utils/thermal_files.cpp index d98f16a..ff10d8b 100644 --- a/thermal/utils/thermal_files.cpp +++ b/thermal/utils/thermal_files.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2021 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. diff --git a/thermal/utils/thermal_files.h b/thermal/utils/thermal_files.h index 0c0a852..59afba6 100644 --- a/thermal/utils/thermal_files.h +++ b/thermal/utils/thermal_files.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2021 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. diff --git a/thermal/utils/config_parser.cpp b/thermal/utils/thermal_info.cpp index 2c2bbf1..7ed63c3 100644 --- a/thermal/utils/config_parser.cpp +++ b/thermal/utils/thermal_info.cpp @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "thermal_info.h" + #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/properties.h> #include <android-base/strings.h> -#include <cmath> -#include <unordered_set> - #include <json/reader.h> #include <json/value.h> -#include "config_parser.h" +#include <cmath> +#include <unordered_set> namespace android { namespace hardware { @@ -429,9 +429,10 @@ bool ParseSensorInfo(std::string_view config_path, } float vr_threshold = NAN; - vr_threshold = getFloatFromValue(sensors[i]["VrThreshold"]); - LOG(INFO) << "Sensor[" << name << "]'s VrThreshold: " << vr_threshold; - + if (!sensors[i]["VrThreshold"].empty()) { + vr_threshold = getFloatFromValue(sensors[i]["VrThreshold"]); + LOG(INFO) << "Sensor[" << name << "]'s VrThreshold: " << vr_threshold; + } float multiplier = sensors[i]["Multiplier"].asFloat(); LOG(INFO) << "Sensor[" << name << "]'s Multiplier: " << multiplier; @@ -479,6 +480,7 @@ bool ParseSensorInfo(std::string_view config_path, s_power.fill(NAN); std::array<float, kThrottlingSeverityCount> i_cutoff; i_cutoff.fill(NAN); + float err_integral_default = 0.0; // Parse PID parameters if (!sensors[i]["PIDInfo"].empty()) { @@ -557,6 +559,10 @@ bool ParseSensorInfo(std::string_view config_path, sensors_parsed->clear(); return false; } + LOG(INFO) << "Start to parse" + << " Sensor[" << name << "]'s E_Integral_Default"; + err_integral_default = getFloatFromValue(sensors[i]["PIDInfo"]["E_Integral_Default"]); + LOG(INFO) << "Sensor[" << name << "]'s E_Integral_Default: " << err_integral_default; // Confirm we have at least one valid PID combination bool valid_pid_combination = false; for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) { @@ -592,6 +598,8 @@ bool ParseSensorInfo(std::string_view config_path, cdev_weight_for_pid.fill(NAN); CdevArray cdev_ceiling; cdev_ceiling.fill(std::numeric_limits<int>::max()); + int max_release_step = std::numeric_limits<int>::max(); + int max_throttle_step = std::numeric_limits<int>::max(); if (support_pid) { if (!values[j]["CdevWeightForPID"].empty()) { LOG(INFO) << "Sensor[" << name << "]: Star to parse " << cdev_name @@ -613,6 +621,30 @@ bool ParseSensorInfo(std::string_view config_path, return false; } } + if (!values[j]["MaxReleaseStep"].empty()) { + max_release_step = getIntFromValue(values[j]["MaxReleaseStep"]); + if (max_release_step < 0) { + LOG(ERROR) << "Sensor[" << name << "]'s " << cdev_name + << " MaxReleaseStep: " << max_release_step; + sensors_parsed->clear(); + return false; + } else { + LOG(INFO) << "Sensor[" << name << "]'s " << cdev_name + << " MaxReleaseStep: " << max_release_step; + } + } + if (!values[j]["MaxThrottleStep"].empty()) { + max_throttle_step = getIntFromValue(values[j]["MaxThrottleStep"]); + if (max_throttle_step < 0) { + LOG(ERROR) << "Sensor[" << name << "]'s " << cdev_name + << " MaxThrottleStep: " << max_throttle_step; + sensors_parsed->clear(); + return false; + } else { + LOG(INFO) << "Sensor[" << name << "]'s " << cdev_name + << " MaxThrottleStep: " << max_throttle_step; + } + } } CdevArray limit_info; limit_info.fill(0); @@ -675,7 +707,6 @@ bool ParseSensorInfo(std::string_view config_path, sensors_parsed->clear(); return false; } - if (values[j]["ReleaseLogic"].asString() == "INCREASE") { release_logic = ReleaseLogic::INCREASE; LOG(INFO) << "Release logic: INCREASE"; @@ -704,6 +735,8 @@ bool ParseSensorInfo(std::string_view config_path, .throttling_with_power_link = throttling_with_power_link, .cdev_weight_for_pid = cdev_weight_for_pid, .cdev_ceiling = cdev_ceiling, + .max_release_step = max_release_step, + .max_throttle_step = max_throttle_step, .cdev_floor_with_power_link = cdev_floor_with_power_link, .power_rail = power_rail, }; @@ -724,9 +757,9 @@ bool ParseSensorInfo(std::string_view config_path, trigger_sensor, formula}); } - std::unique_ptr<ThrottlingInfo> throttling_info( + std::shared_ptr<ThrottlingInfo> throttling_info( new ThrottlingInfo{k_po, k_pu, k_i, k_d, i_max, max_alloc_power, min_alloc_power, - s_power, i_cutoff, binded_cdev_info_map}); + s_power, i_cutoff, err_integral_default, binded_cdev_info_map}); (*sensors_parsed)[name] = { .type = sensor_type, @@ -767,7 +800,7 @@ bool ParseCoolingDevice(std::string_view config_path, std::string errorMessage; if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) { - LOG(ERROR) << "Failed to parse JSON config"; + LOG(ERROR) << "Failed to parse JSON config: " << errorMessage; return false; } @@ -820,7 +853,7 @@ bool ParseCoolingDevice(std::string_view config_path, } } else { LOG(INFO) << "CoolingDevice[" << i << "]'s Name: " << name - << " does not support Power2State"; + << " does not support State2Power"; } const std::string &power_rail = cooling_devices[i]["PowerRail"].asString(); @@ -851,7 +884,7 @@ bool ParsePowerRailInfo(std::string_view config_path, std::string errorMessage; if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) { - LOG(ERROR) << "Failed to parse JSON config"; + LOG(ERROR) << "Failed to parse JSON config: " << errorMessage; return false; } @@ -920,6 +953,13 @@ bool ParsePowerRailInfo(std::string_view config_path, return false; } + if (linked_power_rails.size() != coefficients.size()) { + LOG(ERROR) << "PowerRails[" << name + << "]'s combination size is not matched with coefficient size"; + power_rails_parsed->clear(); + return false; + } + if (!power_rails[i]["Offset"].empty()) { offset = power_rails[i]["Offset"].asFloat(); } diff --git a/thermal/utils/config_parser.h b/thermal/utils/thermal_info.h index 12bdf24..84d0402 100644 --- a/thermal/utils/config_parser.h +++ b/thermal/utils/thermal_info.h @@ -16,11 +16,11 @@ #pragma once +#include <android/hardware/thermal/2.0/IThermal.h> + #include <string> #include <unordered_map> -#include <android/hardware/thermal/2.0/IThermal.h> - namespace android { namespace hardware { namespace thermal { @@ -32,7 +32,7 @@ using CoolingType_2_0 = ::android::hardware::thermal::V2_0::CoolingType; using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType; using ::android::hardware::thermal::V2_0::ThrottlingSeverity; constexpr size_t kThrottlingSeverityCount = std::distance( - hidl_enum_range<ThrottlingSeverity>().begin(), hidl_enum_range<ThrottlingSeverity>().end()); + hidl_enum_range<ThrottlingSeverity>().begin(), hidl_enum_range<ThrottlingSeverity>().end()); using ThrottlingArray = std::array<float, static_cast<size_t>(kThrottlingSeverityCount)>; using CdevArray = std::array<int, static_cast<size_t>(kThrottlingSeverityCount)>; constexpr std::chrono::milliseconds kMinPollIntervalMs = std::chrono::milliseconds(2000); @@ -75,6 +75,8 @@ struct BindedCdevInfo { ReleaseLogic release_logic; ThrottlingArray cdev_weight_for_pid; CdevArray cdev_ceiling; + int max_release_step; + int max_throttle_step; CdevArray cdev_floor_with_power_link; std::string power_rail; // The flag for activate release logic when power is higher than power threshold @@ -93,6 +95,7 @@ struct ThrottlingInfo { ThrottlingArray min_alloc_power; ThrottlingArray s_power; ThrottlingArray i_cutoff; + float err_integral_default; std::unordered_map<std::string, BindedCdevInfo> binded_cdev_info_map; }; @@ -113,7 +116,7 @@ struct SensorInfo { bool is_watch; bool is_hidden; std::unique_ptr<VirtualSensorInfo> virtual_sensor_info; - std::unique_ptr<ThrottlingInfo> throttling_info; + std::shared_ptr<ThrottlingInfo> throttling_info; }; struct CdevInfo { @@ -123,6 +126,7 @@ struct CdevInfo { std::vector<float> state2power; int max_state; }; + struct PowerRailInfo { std::string rail; int power_sample_count; diff --git a/thermal/utils/thermal_throttling.cpp b/thermal/utils/thermal_throttling.cpp new file mode 100644 index 0000000..be1c39e --- /dev/null +++ b/thermal/utils/thermal_throttling.cpp @@ -0,0 +1,590 @@ +/* + * 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. + */ + +#include "thermal_throttling.h" + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <utils/Trace.h> + +#include <iterator> +#include <set> +#include <sstream> +#include <thread> +#include <vector> + +#include "power_files.h" +#include "thermal_info.h" + +namespace android { +namespace hardware { +namespace thermal { +namespace V2_0 { +namespace implementation { + +// 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 : hidl_enum_range<ThrottlingSeverity>()) { + size_t state = static_cast<size_t>(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::clearThrottlingData(std::string_view sensor_name, + const SensorInfo &sensor_info) { + if (!thermal_throttling_status_map_.count(sensor_name.data())) { + return; + } + std::unique_lock<std::shared_mutex> _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<int>::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()].err_integral = + sensor_info.throttling_info->err_integral_default; + thermal_throttling_status_map_[sensor_name.data()].prev_err = NAN; + return; +} + +bool ThermalThrottling::registerThermalThrottling( + std::string_view sensor_name, const std::shared_ptr<ThrottlingInfo> &throttling_info, + const std::unordered_map<std::string, CdevInfo> &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()].err_integral = + throttling_info->err_integral_default; + thermal_throttling_status_map_[sensor_name.data()].prev_err = NAN; + + 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<int>::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; + 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; + 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_2_0 &temp, + const SensorInfo &sensor_info, + std::chrono::milliseconds time_elapsed_ms, + ThrottlingSeverity curr_severity) { + float p = 0, i = 0, d = 0; + float power_budget = std::numeric_limits<float>::max(); + + if (curr_severity == ThrottlingSeverity::NONE) { + return power_budget; + } + + const auto target_state = getTargetStateOfPID(sensor_info, curr_severity); + + // Compute PID + float err = sensor_info.hot_thresholds[target_state] - temp.value; + p = err * (err < 0 ? sensor_info.throttling_info->k_po[target_state] + : sensor_info.throttling_info->k_pu[target_state]); + i = thermal_throttling_status_map_[temp.name].err_integral * + sensor_info.throttling_info->k_i[target_state]; + if (err < sensor_info.throttling_info->i_cutoff[target_state]) { + float i_next = i + err * sensor_info.throttling_info->k_i[target_state]; + if (abs(i_next) < sensor_info.throttling_info->i_max[target_state]) { + i = i_next; + thermal_throttling_status_map_[temp.name].err_integral += err; + } + } + + if (!std::isnan(thermal_throttling_status_map_[temp.name].prev_err) && + time_elapsed_ms != std::chrono::milliseconds::zero()) { + d = sensor_info.throttling_info->k_d[target_state] * + (err - thermal_throttling_status_map_[temp.name].prev_err) / time_elapsed_ms.count(); + } + + thermal_throttling_status_map_[temp.name].prev_err = err; + // Calculate power budget + power_budget = sensor_info.throttling_info->s_power[target_state] + p + i + d; + 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]; + } + + LOG(INFO) << temp.name << " power_budget=" << power_budget << " err=" << err + << " err_integral=" << thermal_throttling_status_map_[temp.name].err_integral + << " s_power=" << sensor_info.throttling_info->s_power[target_state] + << " time_elapsed_ms=" << time_elapsed_ms.count() << " p=" << p << " i=" << i + << " d=" << d << " control target=" << target_state; + + return power_budget; +} + +// Allocate power budget to binded cooling devices base on the real ODPM power data +bool ThermalThrottling::allocatePowerToCdev( + const Temperature_2_0 &temp, const SensorInfo &sensor_info, + const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms, + const std::unordered_map<std::string, PowerStatus> &power_status_map, + const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) { + 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; + auto total_power_budget = updatePowerBudget(temp, sensor_info, time_elapsed_ms, curr_severity); + std::set<std::string> allocated_cdev; + + std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_); + // Compute total cdev weight + for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) { + const auto cdev_weight = binded_cdev_info_pair.second + .cdev_weight_for_pid[static_cast<size_t>(curr_severity)]; + 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->binded_cdev_info_map) { + float cdev_power_adjustment = 0; + const auto cdev_weight = + binded_cdev_info_pair.second + .cdev_weight_for_pid[static_cast<size_t>(curr_severity)]; + + const CdevInfo &cdev_info = cooling_device_info_map.at(binded_cdev_info_pair.first); + + if (allocated_cdev.count(binded_cdev_info_pair.first)) { + continue; + } + if (std::isnan(cdev_weight) || !cdev_weight) { + allocated_cdev.insert(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; + } + } 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); + } + } else { + // 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)) { + continue; + } + + 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; + } + + const auto curr_state = + thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at( + binded_cdev_info_pair.first); + + if (binded_cdev_info_pair.second.max_release_step != + std::numeric_limits<int>::max() && + cdev_power_adjustment > 0) { + auto target_state = + std::max(curr_state - binded_cdev_info_pair.second.max_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<int>::max() && + cdev_power_adjustment < 0) { + auto target_state = + std::min(curr_state + binded_cdev_info_pair.second.max_throttle_step, + cdev_info.max_state); + 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; + } + } + + return true; +} + +void ThermalThrottling::updateCdevRequestByPower( + std::string sensor_name, + const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) { + size_t i; + + std::unique_lock<std::shared_mutex> _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<int>(i); + } + + return; +} + +void ThermalThrottling::updateCdevRequestBySeverity(std::string_view sensor_name, + const SensorInfo &sensor_info, + ThrottlingSeverity curr_severity) { + std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_); + for (auto const &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) { + thermal_throttling_status_map_[sensor_name.data()].hardlimit_cdev_request_map.at( + binded_cdev_info_pair.first) = + binded_cdev_info_pair.second.limit_info[static_cast<size_t>(curr_severity)]; + 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<std::string, CdevInfo> &cooling_device_info_map, + const std::unordered_map<std::string, PowerStatus> &power_status_map, + const ThrottlingSeverity severity, const SensorInfo &sensor_info) { + ATRACE_CALL(); + std::unique_lock<std::shared_mutex> _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<int>(severity)]) { + is_over_budget = false; + } + } else { + if (avg_power > + binded_cdev_info_pair.second.power_thresholds[static_cast<int>(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<int>(severity)] + << ", avg power = " << avg_power; + + switch (binded_cdev_info_pair.second.release_logic) { + case ReleaseLogic::INCREASE: + if (!is_over_budget) { + if (std::abs(release_step) < static_cast<int>(max_state)) { + release_step--; + } + } else { + release_step = 0; + } + break; + case ReleaseLogic::DECREASE: + if (!is_over_budget) { + if (release_step < static_cast<int>(max_state)) { + release_step++; + } + } else { + release_step = 0; + } + break; + case ReleaseLogic::STEPWISE: + if (!is_over_budget) { + if (release_step < static_cast<int>(max_state)) { + release_step++; + } + } else { + if (std::abs(release_step) < static_cast<int>(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_2_0 &temp, const SensorInfo &sensor_info, + const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms, + const std::unordered_map<std::string, PowerStatus> &power_status_map, + const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) { + if (!thermal_throttling_status_map_.count(temp.name)) { + return; + } + + 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)) { + 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<std::string> *cooling_devices_to_update) { + int release_step = 0; + std::unique_lock<std::shared_mutex> _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; + + for (auto &cdev_request_pair : thermal_throttling_status.cdev_status_map) { + int pid_cdev_request = 0; + int hardlimit_cdev_request = 0; + const auto &binded_cdev_info = + sensor_info.throttling_info->binded_cdev_info_map.at(cdev_request_pair.first); + const auto cdev_ceiling = binded_cdev_info.cdev_ceiling[static_cast<size_t>(curr_severity)]; + const auto cdev_floor = + binded_cdev_info.cdev_floor_with_power_link[static_cast<size_t>(curr_severity)]; + release_step = 0; + + if (thermal_throttling_status.pid_cdev_request_map.count(cdev_request_pair.first)) { + pid_cdev_request = + thermal_throttling_status.pid_cdev_request_map.at(cdev_request_pair.first); + } + + if (thermal_throttling_status.hardlimit_cdev_request_map.count(cdev_request_pair.first)) { + hardlimit_cdev_request = thermal_throttling_status.hardlimit_cdev_request_map.at( + cdev_request_pair.first); + } + + if (cdev_release_map.count(cdev_request_pair.first)) { + release_step = cdev_release_map.at(cdev_request_pair.first); + } + + LOG(VERBOSE) << sensor_name.data() << " binded cooling device " << cdev_request_pair.first + << "'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; + + 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) { + cdev_request_pair.second = request_state; + cooling_devices_to_update->emplace_back(cdev_request_pair.first); + } + } +} + +} // namespace implementation +} // namespace V2_0 +} // namespace thermal +} // namespace hardware +} // namespace android diff --git a/thermal/utils/thermal_throttling.h b/thermal/utils/thermal_throttling.h new file mode 100644 index 0000000..a1c9f6c --- /dev/null +++ b/thermal/utils/thermal_throttling.h @@ -0,0 +1,127 @@ +/* + * 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. + */ + +#pragma once + +#include <android/hardware/thermal/2.0/IThermal.h> + +#include <queue> +#include <shared_mutex> +#include <string> +#include <unordered_map> +#include <unordered_set> + +#include "power_files.h" +#include "thermal_info.h" + +namespace android { +namespace hardware { +namespace thermal { +namespace V2_0 { +namespace implementation { + +using ::android::hardware::hidl_vec; +using ::android::hardware::thermal::V2_0::IThermal; +using Temperature_2_0 = ::android::hardware::thermal::V2_0::Temperature; +using ::android::hardware::thermal::V2_0::TemperatureThreshold; +using ::android::hardware::thermal::V2_0::ThrottlingSeverity; + +struct ThermalThrottlingStatus { + std::unordered_map<std::string, int> pid_power_budget_map; + std::unordered_map<std::string, int> pid_cdev_request_map; + std::unordered_map<std::string, int> hardlimit_cdev_request_map; + std::unordered_map<std::string, int> throttling_release_map; + std::unordered_map<std::string, int> cdev_status_map; + float err_integral; + float prev_err; +}; + +// Return the control temp target of PID algorithm +size_t getTargetStateOfPID(const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity); + +// A helper class for conducting thermal throttling +class ThermalThrottling { + public: + ThermalThrottling() = default; + ~ThermalThrottling() = default; + // Disallow copy and assign. + ThermalThrottling(const ThermalThrottling &) = delete; + void operator=(const ThermalThrottling &) = delete; + + // Clear throttling data + void clearThrottlingData(std::string_view sensor_name, const SensorInfo &sensor_info); + // Register map for throttling algo + bool registerThermalThrottling( + std::string_view sensor_name, const std::shared_ptr<ThrottlingInfo> &throttling_info, + const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map); + // Register map for throttling release algo + bool registerThrottlingReleaseToWatch(std::string_view sensor_name, std::string_view cdev_name, + const BindedCdevInfo &binded_cdev_info); + // Get throttling status map + const std::unordered_map<std::string, ThermalThrottlingStatus> &GetThermalThrottlingStatusMap() + const { + std::shared_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_); + return thermal_throttling_status_map_; + } + // Update thermal throttling request for the specific sensor + void thermalThrottlingUpdate( + const Temperature_2_0 &temp, const SensorInfo &sensor_info, + const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms, + const std::unordered_map<std::string, PowerStatus> &power_status_map, + const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map); + + // Compute the throttling target from all the sensors' request + void computeCoolingDevicesRequest(std::string_view sensor_name, const SensorInfo &sensor_info, + const ThrottlingSeverity curr_severity, + std::vector<std::string> *cooling_devices_to_update); + + private: + // PID algo - get the total power budget + float updatePowerBudget(const Temperature_2_0 &temp, const SensorInfo &sensor_info, + std::chrono::milliseconds time_elapsed_ms, + ThrottlingSeverity curr_severity); + // PID algo - allocate the power to target CDEV according to the ODPM + bool allocatePowerToCdev( + const Temperature_2_0 &temp, const SensorInfo &sensor_info, + const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms, + const std::unordered_map<std::string, PowerStatus> &power_status_map, + const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map); + // PID algo - map the target throttling state according to the power budget + void updateCdevRequestByPower( + std::string sensor_name, + const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map); + // Hard limit algo - assign the throttling state according to the severity + void updateCdevRequestBySeverity(std::string_view sensor_name, const SensorInfo &sensor_info, + ThrottlingSeverity curr_severity); + // Throttling release algo - decide release step according to the predefined power threshold, + // return false if the throttling release is not registered in thermal config + bool throttlingReleaseUpdate( + std::string_view sensor_name, + const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map, + const std::unordered_map<std::string, PowerStatus> &power_status_map, + const ThrottlingSeverity severity, const SensorInfo &sensor_info); + + mutable std::shared_mutex thermal_throttling_status_map_mutex_; + // Thermal throttling status from each sensor + std::unordered_map<std::string, ThermalThrottlingStatus> thermal_throttling_status_map_; + std::vector<std::string> cooling_devices_to_update; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace thermal +} // namespace hardware +} // namespace android |