From 7f99605cccc45358dc815d51890c222a650cc9ce Mon Sep 17 00:00:00 2001 From: Alexander Winkowski Date: Tue, 12 Oct 2021 13:00:29 +0200 Subject: [PATCH] sm6250-common: Update thermal HAL * From hardware/google/pixel at d774cbb949e98627e4172bf8fc11e8d954599aa7. Change-Id: I3a3a0c29575d0595e71a30f1e64e33ca34d2eb27 --- atoll.mk | 3 +- sepolicy/vendor/file_contexts | 2 + sepolicy/vendor/init-thermal-symlinks.sh.te | 12 + thermal/Android.bp | 15 +- thermal/Thermal.cpp | 311 ++++++- thermal/Thermal.h | 20 +- thermal/init.thermal.symlinks.sh | 13 + thermal/service.cpp | 3 + thermal/thermal-helper.cpp | 864 +++++++++++++++++--- thermal/thermal-helper.h | 99 ++- thermal/utils/config_parser.cpp | 650 ++++++++++++++- thermal/utils/config_parser.h | 97 ++- thermal/utils/power_files.cpp | 444 ++++++++++ thermal/utils/power_files.h | 127 +++ thermal/utils/thermal_files.cpp | 14 + thermal/utils/thermal_files.h | 6 +- thermal/utils/thermal_watcher.cpp | 417 +++++++++- thermal/utils/thermal_watcher.h | 28 +- thermal/xiaomi_atoll-thermal-symlinks.rc | 11 + 19 files changed, 2884 insertions(+), 252 deletions(-) create mode 100644 sepolicy/vendor/init-thermal-symlinks.sh.te create mode 100755 thermal/init.thermal.symlinks.sh create mode 100644 thermal/utils/power_files.cpp create mode 100644 thermal/utils/power_files.h create mode 100644 thermal/xiaomi_atoll-thermal-symlinks.rc diff --git a/atoll.mk b/atoll.mk index 8e8e4a0..660aa9b 100644 --- a/atoll.mk +++ b/atoll.mk @@ -386,7 +386,8 @@ PRODUCT_BOOT_JARS += \ # Thermal HAL PRODUCT_PACKAGES += \ - android.hardware.thermal@2.0-service.xiaomi_atoll + android.hardware.thermal@2.0-service.xiaomi_atoll \ + thermal_symlinks PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/configs/thermal_info_config.json:$(TARGET_COPY_OUT_VENDOR)/etc/thermal_info_config.json diff --git a/sepolicy/vendor/file_contexts b/sepolicy/vendor/file_contexts index 0e9d3d6..349eb3e 100644 --- a/sepolicy/vendor/file_contexts +++ b/sepolicy/vendor/file_contexts @@ -44,3 +44,5 @@ # Thermal /vendor/bin/hw/android\.hardware\.thermal@2\.0-service\.xiaomi_atoll u:object_r:hal_thermal_default_exec:s0 +/vendor/bin/thermal_symlinks u:object_r:init-thermal-symlinks-sh_exec:s0 +/dev/thermal(/.*)? u:object_r:thermal_link_device:s0 diff --git a/sepolicy/vendor/init-thermal-symlinks.sh.te b/sepolicy/vendor/init-thermal-symlinks.sh.te new file mode 100644 index 0000000..093512c --- /dev/null +++ b/sepolicy/vendor/init-thermal-symlinks.sh.te @@ -0,0 +1,12 @@ +type init-thermal-symlinks-sh, domain; +type init-thermal-symlinks-sh_exec, exec_type, vendor_file_type, file_type; + +init_daemon_domain(init-thermal-symlinks-sh) + +allow init-thermal-symlinks-sh vendor_toolbox_exec:file rx_file_perms; +allow init-thermal-symlinks-sh thermal_link_device:dir rw_dir_perms; +allow init-thermal-symlinks-sh thermal_link_device:lnk_file create_file_perms; +allow init-thermal-symlinks-sh sysfs_thermal:dir r_dir_perms; +allow init-thermal-symlinks-sh sysfs_thermal:file r_file_perms; +allow init-thermal-symlinks-sh sysfs_thermal:lnk_file r_file_perms; +set_prop(init-thermal-symlinks-sh, vendor_thermal_prop) diff --git a/thermal/Android.bp b/thermal/Android.bp index 5f44cfe..c278d87 100644 --- a/thermal/Android.bp +++ b/thermal/Android.bp @@ -16,6 +16,7 @@ cc_binary { "utils/config_parser.cpp", "utils/thermal_files.cpp", "utils/thermal_watcher.cpp", + "utils/power_files.cpp", ], shared_libs: [ "libbase", @@ -23,11 +24,12 @@ cc_binary { "libhidlbase", "libjsoncpp", "libutils", + "libnl", "libbinder_ndk", "android.hardware.thermal@1.0", "android.hardware.thermal@2.0", - "android.hardware.power-ndk_platform", - "pixel-power-ext-ndk_platform" + "android.hardware.power-V1-ndk_platform", + "pixel-power-ext-V1-ndk_platform" ], cflags: [ "-Wall", @@ -45,3 +47,12 @@ cc_binary { "-warnings-as-errors=android-*,clang-analyzer-security*,cert-*" ], } + +sh_binary { + name: "xiaomi_atoll_thermal_symlinks", + src: "init.thermal.symlinks.sh", + vendor: true, + init_rc: [ + "xiaomi_atoll-thermal-symlinks.rc", + ], +} diff --git a/thermal/Thermal.cpp b/thermal/Thermal.cpp index ada526f..80aa53c 100644 --- a/thermal/Thermal.cpp +++ b/thermal/Thermal.cpp @@ -36,7 +36,6 @@ namespace { using ::android::hardware::interfacesEqual; using ::android::hardware::thermal::V1_0::ThermalStatus; using ::android::hardware::thermal::V1_0::ThermalStatusCode; -using ::android::hidl::base::V1_0::IBase; template Return setFailureAndCallback(T _hidl_cb, hidl_vec data, std::string_view debug_msg) { @@ -234,16 +233,15 @@ Return Thermal::unregisterThermalChangedCallback( return Void(); } -void Thermal::sendThermalChangedCallback(const std::vector &temps) { +void Thermal::sendThermalChangedCallback(const Temperature_2_0 &t) { std::lock_guard _lock(thermal_callback_mutex_); - for (auto &t : temps) { - LOG(INFO) << "Sending notification: " - << " Type: " << android::hardware::thermal::V2_0::toString(t.type) - << " Name: " << t.name << " CurrentValue: " << t.value << " ThrottlingStatus: " - << android::hardware::thermal::V2_0::toString(t.throttlingStatus); - thermal_helper_.sendPowerExtHint(t); - callbacks_.erase( + LOG(VERBOSE) << "Sending notification: " + << " Type: " << android::hardware::thermal::V2_0::toString(t.type) + << " Name: " << t.name << " CurrentValue: " << t.value << " ThrottlingStatus: " + << android::hardware::thermal::V2_0::toString(t.throttlingStatus); + + callbacks_.erase( std::remove_if(callbacks_.begin(), callbacks_.end(), [&](const CallbackSetting &c) { if (!c.is_filter_type || t.type == c.type) { @@ -255,6 +253,276 @@ void Thermal::sendThermalChangedCallback(const std::vector &tem return false; }), callbacks_.end()); +} + +void Thermal::dumpVirtualSensorInfo(std::ostringstream *dump_buf) { + *dump_buf << "VirtualSensorInfo:" << std::endl; + const auto &map = thermal_helper_.GetSensorInfoMap(); + for (const auto &sensor_info_pair : map) { + if (sensor_info_pair.second.virtual_sensor_info != nullptr) { + *dump_buf << " Name: " << sensor_info_pair.first << std::endl; + *dump_buf << " LinkedSensorName: ["; + for (size_t i = 0; + i < sensor_info_pair.second.virtual_sensor_info->linked_sensors.size(); i++) { + *dump_buf << sensor_info_pair.second.virtual_sensor_info->linked_sensors[i] << " "; + } + *dump_buf << "]" << std::endl; + *dump_buf << " LinkedSensorCoefficient: ["; + for (size_t i = 0; i < sensor_info_pair.second.virtual_sensor_info->coefficients.size(); + i++) { + *dump_buf << sensor_info_pair.second.virtual_sensor_info->coefficients[i] << " "; + } + *dump_buf << "]" << std::endl; + *dump_buf << " Offset: " << sensor_info_pair.second.virtual_sensor_info->offset + << std::endl; + *dump_buf << " Trigger Sensor: " + << (sensor_info_pair.second.virtual_sensor_info->trigger_sensor.empty() + ? "N/A" + : sensor_info_pair.second.virtual_sensor_info->trigger_sensor) + << std::endl; + *dump_buf << " Formula: "; + switch (sensor_info_pair.second.virtual_sensor_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; + } + } +} + +void Thermal::dumpThrottlingInfo(std::ostringstream *dump_buf) { + *dump_buf << "Throttling Info:" << std::endl; + const auto &map = thermal_helper_.GetSensorInfoMap(); + 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] << " "; + } + *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; + *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: ["; + for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { + *dump_buf << binded_cdev_info_pair.second.cdev_ceiling[i] << " "; + } + *dump_buf << "]" << std::endl; + *dump_buf << " Floor with PowerLink: ["; + for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { + *dump_buf << binded_cdev_info_pair.second.cdev_floor_with_power_link[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.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; + } + } + } + } + } +} + +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(); + *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) + << 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) + << 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; + } + } + } +} + +void Thermal::dumpPowerRailInfo(std::ostringstream *dump_buf) { + const auto &power_rail_info_map = thermal_helper_.GetPowerRailInfoMap(); + const auto &power_status_map = thermal_helper_.GetPowerStatusMap(); + + *dump_buf << "Power Rail Info " << std::endl; + for (const auto &power_rail_pair : power_rail_info_map) { + *dump_buf << " Power Rail: " << power_rail_pair.first << std::endl; + *dump_buf << " Power Sample Count: " << power_rail_pair.second.power_sample_count + << 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_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 << " 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; + } + } + } } } @@ -371,24 +639,31 @@ Return Thermal::debug(const hidl_handle &handle, const hidl_vec #include @@ -40,7 +40,7 @@ using ::android::hardware::thermal::V2_0::IThermalChangedCallback; struct CallbackSetting { CallbackSetting(sp callback, bool is_filter_type, TemperatureType_2_0 type) - : callback(callback), is_filter_type(is_filter_type), type(type) {} + : callback(std::move(callback)), is_filter_type(is_filter_type), type(type) {} sp callback; bool is_filter_type; TemperatureType_2_0 type; @@ -65,9 +65,9 @@ class Thermal : public IThermal { getCurrentTemperatures_cb _hidl_cb) override; Return getTemperatureThresholds(bool filterType, TemperatureType_2_0 type, getTemperatureThresholds_cb _hidl_cb) override; - Return registerThermalChangedCallback(const sp &callback, - bool filterType, TemperatureType_2_0 type, - registerThermalChangedCallback_cb _hidl_cb) override; + Return registerThermalChangedCallback( + const sp &callback, bool filterType, TemperatureType_2_0 type, + registerThermalChangedCallback_cb _hidl_cb) override; Return unregisterThermalChangedCallback( const sp &callback, unregisterThermalChangedCallback_cb _hidl_cb) override; @@ -78,10 +78,14 @@ class Thermal : public IThermal { Return debug(const hidl_handle &fd, const hidl_vec &args) override; // Helper function for calling callbacks - void sendThermalChangedCallback(const std::vector &temps); + void sendThermalChangedCallback(const Temperature_2_0 &t); private: ThermalHelper thermal_helper_; + void dumpVirtualSensorInfo(std::ostringstream *dump_buf); + void dumpThrottlingInfo(std::ostringstream *dump_buf); + void dumpThrottlingRequestStatus(std::ostringstream *dump_buf); + void dumpPowerRailInfo(std::ostringstream *dump_buf); std::mutex thermal_callback_mutex_; std::vector callbacks_; }; @@ -91,5 +95,3 @@ class Thermal : public IThermal { } // namespace thermal } // namespace hardware } // namespace android - -#endif // ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H diff --git a/thermal/init.thermal.symlinks.sh b/thermal/init.thermal.symlinks.sh new file mode 100755 index 0000000..d990897 --- /dev/null +++ b/thermal/init.thermal.symlinks.sh @@ -0,0 +1,13 @@ +#!/vendor/bin/sh + +for f in /sys/class/thermal/thermal_zone* +do + tz_name=`cat $f/type` + ln -s $f /dev/thermal/tz-by-name/$tz_name +done +for f in /sys/class/thermal/cooling_device* +do + cdev_name=`cat $f/type` + ln -s $f /dev/thermal/cdev-by-name/$cdev_name +done +setprop vendor.thermal.link_ready 1 diff --git a/thermal/service.cpp b/thermal/service.cpp index 36166b0..8c56b38 100644 --- a/thermal/service.cpp +++ b/thermal/service.cpp @@ -17,6 +17,8 @@ #include #include "Thermal.h" +constexpr std::string_view kThermalLogTag("pixel-thermal"); + using ::android::OK; using ::android::status_t; @@ -34,6 +36,7 @@ static int shutdown() { } int main(int /* argc */, char ** /* argv */) { + android::base::SetDefaultTag(kThermalLogTag.data()); status_t status; android::sp service = nullptr; diff --git a/thermal/thermal-helper.cpp b/thermal/thermal-helper.cpp index 43f0d92..cbd6393 100644 --- a/thermal/thermal-helper.cpp +++ b/thermal/thermal-helper.cpp @@ -50,8 +50,12 @@ constexpr std::string_view kSensorTripPointTempZeroFile("trip_point_0_temp"); constexpr std::string_view kSensorTripPointHystZeroFile("trip_point_0_hyst"); constexpr std::string_view kUserSpaceSuffix("user_space"); constexpr std::string_view kCoolingDeviceCurStateSuffix("cur_state"); +constexpr std::string_view kCoolingDeviceMaxStateSuffix("max_state"); +constexpr std::string_view kCoolingDeviceState2powerSuffix("state2power_table"); constexpr std::string_view kConfigProperty("vendor.thermal.config"); constexpr std::string_view kConfigDefaultFileName("thermal_info_config.json"); +constexpr std::string_view kThermalGenlProperty("persist.vendor.enable.thermal.genl"); +constexpr std::string_view kThermalDisabledProperty("vendor.disable.thermal.control"); namespace { using android::base::StringPrintf; @@ -66,7 +70,7 @@ using android::hardware::thermal::V2_0::toString; * For Android systems this approach is safer than using cpufeatures, see bug * b/36941727. */ -std::size_t getNumberOfCores() { +static int getNumberOfCores() { std::string file; if (!android::base::ReadFileToString(kCpuPresentFile.data(), &file)) { LOG(ERROR) << "Error reading Cpu present file: " << kCpuPresentFile; @@ -85,31 +89,29 @@ std::size_t getNumberOfCores() { } return static_cast(max_core - min_core + 1); } -const std::size_t kMaxCpus = getNumberOfCores(); +const int kMaxCpus = getNumberOfCores(); void parseCpuUsagesFileAndAssignUsages(hidl_vec *cpu_usages) { - uint64_t cpu_num, user, nice, system, idle; - std::string cpu_name; 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; } std::istringstream stat_data(data); std::string line; while (std::getline(stat_data, line)) { - if (line.find("cpu") == 0 && isdigit(line[3])) { + if (!line.find("cpu") && isdigit(line[3])) { // Split the string using spaces. std::vector words = android::base::Split(line, " "); - cpu_name = words[0]; - cpu_num = std::stoi(cpu_name.substr(3)); + std::string cpu_name = words[0]; + int cpu_num = std::stoi(cpu_name.substr(3)); if (cpu_num < kMaxCpus) { - user = std::stoi(words[1]); - nice = std::stoi(words[2]); - system = std::stoi(words[3]); - idle = std::stoi(words[4]); + uint64_t user = std::stoull(words[1]); + uint64_t nice = std::stoull(words[2]); + uint64_t system = std::stoull(words[3]); + uint64_t idle = std::stoull(words[4]); // Check if the CPU is online by reading the online file. std::string cpu_online_path = @@ -118,11 +120,14 @@ void parseCpuUsagesFileAndAssignUsages(hidl_vec *cpu_usages) { std::string is_online; if (!android::base::ReadFileToString(cpu_online_path, &is_online)) { LOG(ERROR) << "Could not open Cpu online file: " << cpu_online_path; - return; + if (cpu_num != 0) { + return; + } + // Some architecture cannot offline cpu0, so assuming it is online + is_online = "1"; } is_online = android::base::Trim(is_online); - (*cpu_usages)[cpu_num].name = cpu_name; (*cpu_usages)[cpu_num].active = user + nice + system; (*cpu_usages)[cpu_num].total = user + nice + system + idle; (*cpu_usages)[cpu_num].isOnline = (is_online == "1") ? true : false; @@ -134,8 +139,8 @@ void parseCpuUsagesFileAndAssignUsages(hidl_vec *cpu_usages) { } } -std::map parseThermalPathMap(std::string_view prefix) { - std::map path_map; +std::unordered_map parseThermalPathMap(std::string_view prefix) { + std::unordered_map path_map; std::unique_ptr dir(opendir(kThermalSensorsRoot.data()), closedir); if (!dir) { return path_map; @@ -263,22 +268,13 @@ void PowerHalService::setMode(const std::string &type, const ThrottlingSeverity ThermalHelper::ThermalHelper(const NotificationCallback &cb) : thermal_watcher_(new ThermalWatcher( std::bind(&ThermalHelper::thermalWatcherCallbackFunc, this, std::placeholders::_1))), - cb_(cb), - cooling_device_info_map_(ParseCoolingDevice( - "/vendor/etc/" + - android::base::GetProperty(kConfigProperty.data(), kConfigDefaultFileName.data()))), - sensor_info_map_(ParseSensorInfo( - "/vendor/etc/" + - android::base::GetProperty(kConfigProperty.data(), kConfigDefaultFileName.data()))) { - for (auto const &name_status_pair : sensor_info_map_) { - sensor_status_map_[name_status_pair.first] = { - .severity = ThrottlingSeverity::NONE, - .prev_hot_severity = ThrottlingSeverity::NONE, - .prev_cold_severity = ThrottlingSeverity::NONE, - .prev_hint_severity = ThrottlingSeverity::NONE, - }; - } - + cb_(cb) { + const std::string config_path = + "/vendor/etc/" + + android::base::GetProperty(kConfigProperty.data(), kConfigDefaultFileName.data()); + cooling_device_info_map_ = ParseCoolingDevice(config_path); + sensor_info_map_ = ParseSensorInfo(config_path); + power_rail_info_map_ = ParsePowerRailInfo(config_path); auto tz_map = parseThermalPathMap(kSensorPrefix.data()); auto cdev_map = parseThermalPathMap(kCoolingDevicePrefix.data()); @@ -286,17 +282,114 @@ ThermalHelper::ThermalHelper(const NotificationCallback &cb) if (!is_initialized_) { LOG(FATAL) << "ThermalHAL could not be initialized properly."; } - std::set monitored_sensors; - std::transform(sensor_info_map_.cbegin(), sensor_info_map_.cend(), - std::inserter(monitored_sensors, monitored_sensors.begin()), - [](std::pair const &sensor) { - if (sensor.second.is_monitor) - return sensor.first; - else - return std::string(); - }); - thermal_watcher_->registerFilesToWatch(monitored_sensors, initializeTrip(tz_map)); + for (auto const &name_status_pair : sensor_info_map_) { + sensor_status_map_[name_status_pair.first] = { + .severity = ThrottlingSeverity::NONE, + .prev_hot_severity = ThrottlingSeverity::NONE, + .prev_cold_severity = ThrottlingSeverity::NONE, + .prev_hint_severity = ThrottlingSeverity::NONE, + .last_update_time = boot_clock::time_point::min(), + .err_integral = 0.0, + .prev_err = NAN, + }; + + bool invalid_binded_cdev = false; + 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)) { + invalid_binded_cdev = true; + LOG(ERROR) << "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; + } + } + + 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; + } + } + const auto &cdev_info = cooling_device_info_map_.at(binded_cdev_pair.first); + + for (auto &cdev_ceiling : binded_cdev_pair.second.cdev_ceiling) { + if (cdev_ceiling > cdev_info.max_state) { + if (cdev_ceiling != std::numeric_limits::max()) { + LOG(ERROR) << "Sensor " << name_status_pair.first << "'s " + << binded_cdev_pair.first << " cdev_ceiling:" << cdev_ceiling + << " is higher than max state:" << cdev_info.max_state; + } + 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)) { + invalid_binded_cdev = true; + LOG(ERROR) << "Could not find " << binded_cdev_pair.first + << "'s power energy source: " << binded_cdev_pair.second.power_rail; + } + } + } + + if (invalid_binded_cdev) { + name_status_pair.second.throttling_info->binded_cdev_info_map.clear(); + sensor_status_map_[name_status_pair.first].hard_limit_request_map.clear(); + sensor_status_map_[name_status_pair.first].pid_request_map.clear(); + } + + if (name_status_pair.second.virtual_sensor_info != nullptr && + name_status_pair.second.is_monitor) { + if (sensor_info_map_.count( + name_status_pair.second.virtual_sensor_info->trigger_sensor)) { + sensor_info_map_[name_status_pair.second.virtual_sensor_info->trigger_sensor] + .is_monitor = true; + } else { + LOG(FATAL) << name_status_pair.first << " does not have trigger sensor: " + << name_status_pair.second.virtual_sensor_info->trigger_sensor; + } + } + } + + const bool thermal_throttling_disabled = + android::base::GetBoolProperty(kThermalDisabledProperty.data(), false); + + if (thermal_throttling_disabled) { + LOG(INFO) << kThermalDisabledProperty.data() << " is true"; + for (const auto &cdev_pair : cooling_device_info_map_) { + if (cooling_devices_.writeCdevFile(cdev_pair.first, std::to_string(0))) { + LOG(INFO) << "Successfully clear cdev " << cdev_pair.first << " to 0"; + } + } + return; + } + + const bool thermal_genl_enabled = + android::base::GetBoolProperty(kThermalGenlProperty.data(), false); + + std::set monitored_sensors; + initializeTrip(tz_map, &monitored_sensors, thermal_genl_enabled); + + if (thermal_genl_enabled) { + thermal_watcher_->registerFilesToWatchNl(monitored_sensors); + } else { + thermal_watcher_->registerFilesToWatch(monitored_sensors); + } // Need start watching after status map initialized is_initialized_ = thermal_watcher_->startWatchingDeviceFiles(); @@ -311,6 +404,23 @@ ThermalHelper::ThermalHelper(const NotificationCallback &cb) } } +bool getThermalZoneTypeById(int tz_id, std::string *type) { + std::string tz_type; + std::string path = + android::base::StringPrintf("%s/%s%d/%s", kThermalSensorsRoot.data(), + kSensorPrefix.data(), tz_id, kThermalNameFile.data()); + LOG(INFO) << "TZ Path: " << path; + if (!::android::base::ReadFileToString(path, &tz_type)) { + LOG(ERROR) << "Failed to read sensor: " << tz_type; + return false; + } + + // Strip the newline. + *type = ::android::base::Trim(tz_type); + LOG(INFO) << "TZ type: " << *type; + return true; +} + bool ThermalHelper::readCoolingDevice(std::string_view cooling_device, CoolingDevice_2_0 *out) const { // Read the file. If the file can't be read temp will be empty string. @@ -321,7 +431,8 @@ bool ThermalHelper::readCoolingDevice(std::string_view cooling_device, return false; } - const CoolingType &type = cooling_device_info_map_.at(cooling_device.data()); + const CdevInfo &cdev_info = cooling_device_info_map_.at(cooling_device.data()); + const CoolingType &type = cdev_info.type; out->type = type; out->name = cooling_device.data(); @@ -330,18 +441,26 @@ bool ThermalHelper::readCoolingDevice(std::string_view cooling_device, return true; } -bool ThermalHelper::readTemperature(std::string_view sensor_name, Temperature_1_0 *out) const { +bool ThermalHelper::readTemperature(std::string_view sensor_name, Temperature_1_0 *out, + bool is_virtual_sensor) const { // Read the file. If the file can't be read temp will be empty string. std::string temp; - if (!thermal_sensors_.readThermalFile(sensor_name, &temp)) { - LOG(ERROR) << "readTemperature: sensor not found: " << sensor_name; - return false; - } + if (!is_virtual_sensor) { + if (!thermal_sensors_.readThermalFile(sensor_name, &temp)) { + LOG(ERROR) << "readTemperature: sensor not found: " << sensor_name; + return false; + } - if (temp.empty()) { - LOG(ERROR) << "readTemperature: failed to read sensor: " << sensor_name; - return false; + if (temp.empty()) { + LOG(ERROR) << "readTemperature: failed to read sensor: " << sensor_name; + return false; + } + } else { + if (!checkVirtualSensor(sensor_name.data(), &temp)) { + LOG(ERROR) << "readTemperature: failed to read virtual sensor: " << sensor_name; + return false; + } } const SensorInfo &sensor_info = sensor_info_map_.at(sensor_name.data()); @@ -363,18 +482,26 @@ bool ThermalHelper::readTemperature(std::string_view sensor_name, Temperature_1_ bool ThermalHelper::readTemperature( std::string_view sensor_name, Temperature_2_0 *out, - std::pair *throtting_status) const { + std::pair *throtting_status, + bool is_virtual_sensor) const { // Read the file. If the file can't be read temp will be empty string. std::string temp; - if (!thermal_sensors_.readThermalFile(sensor_name, &temp)) { - LOG(ERROR) << "readTemperature: sensor not found: " << sensor_name; - return false; - } + if (!is_virtual_sensor) { + if (!thermal_sensors_.readThermalFile(sensor_name, &temp)) { + LOG(ERROR) << "readTemperature: sensor not found: " << sensor_name; + return false; + } - if (temp.empty()) { - LOG(ERROR) << "readTemperature: failed to read sensor: " << sensor_name; - return false; + if (temp.empty()) { + LOG(ERROR) << "readTemperature: failed to read sensor: " << sensor_name; + return false; + } + } else { + if (!checkVirtualSensor(sensor_name.data(), &temp)) { + LOG(ERROR) << "readTemperature: failed to read virtual sensor: " << sensor_name; + return false; + } } const auto &sensor_info = sensor_info_map_.at(sensor_name.data()); @@ -429,6 +556,207 @@ 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()) { + size_t state = static_cast(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::max(); + + 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; + + 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(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) { + 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(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 *cooling_devices_to_update) { + int release_step = 0; + + std::unique_lock _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(sensor_status.severity)]; + const auto cdev_floor = + binded_cdev_info + .cdev_floor_with_power_link[static_cast(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 &updated_cdev) { + int max_state; + + 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; + } + } + if (cooling_devices_.writeCdevFile(target_cdev, std::to_string(max_state))) { + LOG(VERBOSE) << "Successfully update cdev " << target_cdev << " sysfs to " << max_state; + } + } +} + std::pair ThermalHelper::getSeverityFromThresholds( const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds, const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis, @@ -470,66 +798,161 @@ std::pair ThermalHelper::getSeverityFrom return std::make_pair(ret_hot, ret_cold); } -bool ThermalHelper::initializeSensorMap(const std::map &path_map) { +bool ThermalHelper::initializeSensorMap( + const std::unordered_map &path_map) { for (const auto &sensor_info_pair : sensor_info_map_) { std::string_view sensor_name = sensor_info_pair.first; - if (!path_map.count(sensor_name.data())) { - LOG(ERROR) << "Could not find " << sensor_name << " in sysfs"; + if (sensor_info_pair.second.virtual_sensor_info != nullptr) { continue; } - std::string path = android::base::StringPrintf( - "%s/%s", path_map.at(sensor_name.data()).c_str(), kSensorTempSuffix.data()); + if (!path_map.count(sensor_name.data())) { + LOG(ERROR) << "Could not find " << sensor_name << " in sysfs"; + return false; + } + + std::string path; + if (sensor_info_pair.second.temp_path.empty()) { + path = android::base::StringPrintf("%s/%s", path_map.at(sensor_name.data()).c_str(), + kSensorTempSuffix.data()); + } else { + path = sensor_info_pair.second.temp_path; + } + if (!thermal_sensors_.addThermalFile(sensor_name, path)) { LOG(ERROR) << "Could not add " << sensor_name << "to sensors map"; + return false; } } - if (sensor_info_map_.size() == thermal_sensors_.getNumThermalFiles()) { - return true; - } - return false; + return true; } -bool ThermalHelper::initializeCoolingDevices(const std::map &path_map) { - for (const auto &cooling_device_info_pair : cooling_device_info_map_) { - std::string_view cooling_device_name = cooling_device_info_pair.first; - if (!path_map.count(cooling_device_name.data())) { +bool ThermalHelper::initializeCoolingDevices( + const std::unordered_map &path_map) { + for (auto &cooling_device_info_pair : cooling_device_info_map_) { + std::string cooling_device_name = cooling_device_info_pair.first; + if (!path_map.count(cooling_device_name)) { LOG(ERROR) << "Could not find " << cooling_device_name << " in sysfs"; continue; } - std::string path = android::base::StringPrintf( - "%s/%s", path_map.at(cooling_device_name.data()).c_str(), - kCoolingDeviceCurStateSuffix.data()); - if (!cooling_devices_.addThermalFile(cooling_device_name, path)) { - LOG(ERROR) << "Could not add " << cooling_device_name << "to cooling device map"; + // Add cooling device path for thermalHAL to get current state + std::string_view path = path_map.at(cooling_device_name); + std::string read_path; + if (!cooling_device_info_pair.second.read_path.empty()) { + read_path = cooling_device_info_pair.second.read_path.data(); + } else { + read_path = android::base::StringPrintf("%s/%s", path.data(), + kCoolingDeviceCurStateSuffix.data()); + } + if (!cooling_devices_.addThermalFile(cooling_device_name, read_path)) { + LOG(ERROR) << "Could not add " << cooling_device_name + << " read path to cooling device map"; + continue; + } + + std::string state2power_path = android::base::StringPrintf( + "%s/%s", path.data(), kCoolingDeviceState2powerSuffix.data()); + std::string state2power_str; + if (android::base::ReadFileToString(state2power_path, &state2power_str)) { + LOG(INFO) << "Cooling device " << cooling_device_info_pair.first + << " use state2power read from sysfs"; + cooling_device_info_pair.second.state2power.clear(); + + std::stringstream power(state2power_str); + unsigned int power_number; + int i = 0; + while (power >> power_number) { + cooling_device_info_pair.second.state2power.push_back( + static_cast(power_number)); + LOG(INFO) << "Cooling device " << cooling_device_info_pair.first << " state:" << i + << " power: " << power_number; + i++; + } + } + + // Get max cooling device request state + std::string max_state; + std::string max_state_path = android::base::StringPrintf( + "%s/%s", path.data(), kCoolingDeviceMaxStateSuffix.data()); + if (!android::base::ReadFileToString(max_state_path, &max_state)) { + LOG(ERROR) << cooling_device_info_pair.first + << " could not open max state file:" << max_state_path; + cooling_device_info_pair.second.max_state = std::numeric_limits::max(); + } else { + cooling_device_info_pair.second.max_state = std::stoi(android::base::Trim(max_state)); + LOG(INFO) << "Cooling device " << cooling_device_info_pair.first + << " max state: " << cooling_device_info_pair.second.max_state + << " state2power number: " + << cooling_device_info_pair.second.state2power.size(); + if (cooling_device_info_pair.second.state2power.size() > 0 && + cooling_device_info_pair.second.state2power.size() != + (size_t)cooling_device_info_pair.second.max_state + 1) { + LOG(ERROR) << "Invalid state2power number: " + << cooling_device_info_pair.second.state2power.size() + << ", number should be " << cooling_device_info_pair.second.max_state + 1 + << " (max_state + 1)"; + } + } + + // Add cooling device path for thermalHAL to request state + cooling_device_name = + android::base::StringPrintf("%s_%s", cooling_device_name.c_str(), "w"); + std::string write_path; + if (!cooling_device_info_pair.second.write_path.empty()) { + write_path = cooling_device_info_pair.second.write_path.data(); + } else { + write_path = android::base::StringPrintf("%s/%s", path.data(), + kCoolingDeviceCurStateSuffix.data()); + } + + if (!cooling_devices_.addThermalFile(cooling_device_name, write_path)) { + LOG(ERROR) << "Could not add " << cooling_device_name + << " write path to cooling device map"; continue; } } - if (cooling_device_info_map_.size() == cooling_devices_.getNumThermalFiles()) { - return true; + if (cooling_device_info_map_.size() * 2 != cooling_devices_.getNumThermalFiles()) { + LOG(ERROR) << "Some cooling device can not be initialized"; } - return false; + return true; } -bool ThermalHelper::initializeTrip(const std::map &path_map) { - for (const auto &sensor_info : sensor_info_map_) { - if (sensor_info.second.is_monitor) { - std::string_view sensor_name = sensor_info.first; - std::string_view tz_path = path_map.at(sensor_name.data()); - std::string tz_policy; - std::string path = android::base::StringPrintf("%s/%s", (tz_path.data()), - kSensorPolicyFile.data()); +void ThermalHelper::setMinTimeout(SensorInfo *sensor_info) { + sensor_info->polling_delay = kMinPollIntervalMs; + sensor_info->passive_delay = kMinPollIntervalMs; +} + +void ThermalHelper::initializeTrip(const std::unordered_map &path_map, + std::set *monitored_sensors, + bool thermal_genl_enabled) { + for (auto &sensor_info : sensor_info_map_) { + if (!sensor_info.second.is_monitor || (sensor_info.second.virtual_sensor_info != nullptr)) { + continue; + } + + bool trip_update = false; + std::string_view sensor_name = sensor_info.first; + std::string_view tz_path = path_map.at(sensor_name.data()); + std::string tz_policy; + std::string path = + android::base::StringPrintf("%s/%s", (tz_path.data()), kSensorPolicyFile.data()); + + if (thermal_genl_enabled) { + trip_update = true; + } else { + // Check if thermal zone support uevent notify if (!android::base::ReadFileToString(path, &tz_policy)) { LOG(ERROR) << sensor_name << " could not open tz policy file:" << path; - return false; + } else { + tz_policy = android::base::Trim(tz_policy); + if (tz_policy != kUserSpaceSuffix) { + LOG(ERROR) << sensor_name << " does not support uevent notify"; + } else { + trip_update = true; + } } - // Check if thermal zone support uevent notify - tz_policy = android::base::Trim(tz_policy); - if (tz_policy != kUserSpaceSuffix) { - LOG(ERROR) << sensor_name << " does not support uevent notify"; - return false; - } - + } + if (trip_update) { // Update thermal zone trip point for (size_t i = 0; i < kThrottlingSeverityCount; ++i) { if (!std::isnan(sensor_info.second.hot_thresholds[i]) && @@ -540,9 +963,10 @@ bool ThermalHelper::initializeTrip(const std::map &pat path = android::base::StringPrintf("%s/%s", (tz_path.data()), kSensorTripPointTempZeroFile.data()); if (!android::base::WriteStringToFile(threshold, path)) { - LOG(ERROR) << "fail to update " << sensor_name - << " trip point: " << threshold << path; - return false; + LOG(ERROR) << "fail to update " << sensor_name << " trip point: " << path + << " to " << threshold; + trip_update = false; + break; } // Update trip_point_0_hyst threshold threshold = std::to_string(static_cast( @@ -552,25 +976,35 @@ bool ThermalHelper::initializeTrip(const std::map &pat if (!android::base::WriteStringToFile(threshold, path)) { LOG(ERROR) << "fail to update " << sensor_name << "trip hyst" << threshold << path; - return false; + trip_update = false; + break; } break; } else if (i == kThrottlingSeverityCount - 1) { LOG(ERROR) << sensor_name << ":all thresholds are NAN"; - return false; + trip_update = false; + break; } } + monitored_sensors->insert(sensor_info.first); + } + + if (!trip_update) { + LOG(INFO) << "config Sensor: " << sensor_info.first + << " to default polling interval: " << kMinPollIntervalMs.count(); + setMinTimeout(&sensor_info.second); } } - return true; } + bool ThermalHelper::fillTemperatures(hidl_vec *temperatures) const { temperatures->resize(sensor_info_map_.size()); int current_index = 0; for (const auto &name_info_pair : sensor_info_map_) { Temperature_1_0 temp; - if (readTemperature(name_info_pair.first, &temp)) { + if (readTemperature(name_info_pair.first, &temp, + name_info_pair.second.virtual_sensor_info != nullptr)) { (*temperatures)[current_index] = temp; } else { LOG(ERROR) << __func__ @@ -590,7 +1024,8 @@ bool ThermalHelper::fillCurrentTemperatures(bool filterType, TemperatureType_2_0 if (filterType && name_info_pair.second.type != type) { continue; } - if (readTemperature(name_info_pair.first, &temp)) { + if (readTemperature(name_info_pair.first, &temp, nullptr, + name_info_pair.second.virtual_sensor_info != nullptr)) { ret.emplace_back(std::move(temp)); } else { LOG(ERROR) << __func__ @@ -627,7 +1062,7 @@ bool ThermalHelper::fillCurrentCoolingDevices(bool filterType, CoolingType type, std::vector ret; for (const auto &name_info_pair : cooling_device_info_map_) { CoolingDevice_2_0 value; - if (filterType && name_info_pair.second != type) { + if (filterType && name_info_pair.second.type != type) { continue; } if (readCoolingDevice(name_info_pair.first, &value)) { @@ -643,35 +1078,144 @@ bool ThermalHelper::fillCurrentCoolingDevices(bool filterType, CoolingType type, bool ThermalHelper::fillCpuUsages(hidl_vec *cpu_usages) const { cpu_usages->resize(kMaxCpus); + for (int i = 0; i < kMaxCpus; i++) { + (*cpu_usages)[i].name = StringPrintf("cpu%d", i); + (*cpu_usages)[i].active = 0; + (*cpu_usages)[i].total = 0; + (*cpu_usages)[i].isOnline = false; + } parseCpuUsagesFileAndAssignUsages(cpu_usages); return true; } +bool ThermalHelper::checkVirtualSensor(std::string_view sensor_name, std::string *temp) const { + float temp_val = 0.0; + + const auto &sensor_info = sensor_info_map_.at(sensor_name.data()); + float offset = sensor_info.virtual_sensor_info->offset; + for (size_t i = 0; i < sensor_info.virtual_sensor_info->linked_sensors.size(); i++) { + std::string data; + const auto &linked_sensor_info = + sensor_info_map_.at(sensor_info.virtual_sensor_info->linked_sensors[i].data()); + if (linked_sensor_info.virtual_sensor_info == nullptr) { + if (!thermal_sensors_.readThermalFile( + sensor_info.virtual_sensor_info->linked_sensors[i], &data)) { + continue; + } + } else if (!checkVirtualSensor(sensor_info.virtual_sensor_info->linked_sensors[i], &data)) { + return false; + } + + LOG(VERBOSE) << sensor_name.data() << "'s linked sensor " + << sensor_info.virtual_sensor_info->linked_sensors[i] << ": temp = " << data; + data = ::android::base::Trim(data); + float sensor_reading = std::stof(data); + if (std::isnan(sensor_info.virtual_sensor_info->coefficients[i])) { + return false; + } + float coefficient = sensor_info.virtual_sensor_info->coefficients[i]; + switch (sensor_info.virtual_sensor_info->formula) { + case FormulaOption::COUNT_THRESHOLD: + if ((coefficient < 0 && sensor_reading < -coefficient) || + (coefficient >= 0 && sensor_reading >= coefficient)) + temp_val += 1; + break; + case FormulaOption::WEIGHTED_AVG: + temp_val += sensor_reading * coefficient; + break; + case FormulaOption::MAXIMUM: + if (i == 0) + temp_val = std::numeric_limits::lowest(); + if (sensor_reading * coefficient > temp_val) + temp_val = sensor_reading * coefficient; + break; + case FormulaOption::MINIMUM: + if (i == 0) + temp_val = std::numeric_limits::max(); + if (sensor_reading * coefficient < temp_val) + temp_val = sensor_reading * coefficient; + break; + default: + break; + } + } + *temp = std::to_string(temp_val + offset); + return true; +} + // This is called in the different thread context and will update sensor_status // uevent_sensors is the set of sensors which trigger uevent from thermal core driver. -bool ThermalHelper::thermalWatcherCallbackFunc(const std::set &uevent_sensors) { +std::chrono::milliseconds ThermalHelper::thermalWatcherCallbackFunc( + const std::set &uevent_sensors) { std::vector temps; - bool thermal_triggered = false; + std::vector cooling_devices_to_update; + std::set updated_power_rails; + boot_clock::time_point now = boot_clock::now(); + auto min_sleep_ms = std::chrono::milliseconds::max(); + for (auto &name_status_pair : sensor_status_map_) { + bool force_update = false; + bool severity_changed = false; Temperature_2_0 temp; TemperatureThreshold threshold; SensorStatus &sensor_status = name_status_pair.second; const SensorInfo &sensor_info = sensor_info_map_.at(name_status_pair.first); - // Only send notification on whitelisted sensors + + // Only handle the sensors in allow list if (!sensor_info.is_monitor) { continue; } - // If callback is triggered by uevent, only check the sensors within uevent_sensors - if (uevent_sensors.size() != 0 && - uevent_sensors.find(name_status_pair.first) == uevent_sensors.end()) { - if (sensor_status.severity != ThrottlingSeverity::NONE) { - thermal_triggered = true; + + std::chrono::milliseconds time_elapsed_ms = std::chrono::milliseconds::zero(); + auto sleep_ms = (sensor_status.severity != ThrottlingSeverity::NONE) + ? sensor_info.passive_delay + : sensor_info.polling_delay; + // Check if the sensor need to be updated + if (sensor_status.last_update_time == boot_clock::time_point::min()) { + force_update = true; + LOG(VERBOSE) << "Force update " << name_status_pair.first + << "'s temperature after booting"; + } else { + time_elapsed_ms = std::chrono::duration_cast( + now - sensor_status.last_update_time); + + if (time_elapsed_ms > sleep_ms) { + // Update the sensor because sleep timeout + force_update = true; + } else if (uevent_sensors.size() && + uevent_sensors.find((sensor_info.virtual_sensor_info != nullptr) + ? sensor_info.virtual_sensor_info->trigger_sensor + : name_status_pair.first) != + uevent_sensors.end()) { + // Update the sensor from uevent + force_update = true; + } else if (sensor_info.virtual_sensor_info != nullptr) { + // Update the virtual sensor if it's trigger sensor over the threshold + const auto trigger_sensor_status = + sensor_status_map_.at(sensor_info.virtual_sensor_info->trigger_sensor); + if (trigger_sensor_status.severity != ThrottlingSeverity::NONE) { + force_update = true; + } } + } + + LOG(VERBOSE) << "sensor " << name_status_pair.first + << ": time_elpased=" << time_elapsed_ms.count() + << ", sleep_ms=" << sleep_ms.count() << ", force_update = " << force_update; + + if (!force_update) { + auto timeout_remaining = sleep_ms - time_elapsed_ms; + if (min_sleep_ms > timeout_remaining) { + min_sleep_ms = timeout_remaining; + } + LOG(VERBOSE) << "sensor " << name_status_pair.first + << ": timeout_remaining=" << timeout_remaining.count(); continue; } std::pair throtting_status; - if (!readTemperature(name_status_pair.first, &temp, &throtting_status)) { + if (!readTemperature(name_status_pair.first, &temp, &throtting_status, + (sensor_info.virtual_sensor_info != nullptr))) { LOG(ERROR) << __func__ << ": error reading temperature for sensor: " << name_status_pair.first; continue; @@ -693,19 +1237,89 @@ bool ThermalHelper::thermalWatcherCallbackFunc(const std::set &ueve } 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 + : sensor_info.polling_delay; } } + if (sensor_status.severity != ThrottlingSeverity::NONE) { - thermal_triggered = true; - LOG(INFO) << temp.name << ": " << temp.value; + LOG(INFO) << temp.name << ": " << temp.value << " degC"; + } else { + LOG(VERBOSE) << temp.name << ": " << temp.value << " degC"; } - } - if (!temps.empty() && cb_) { - cb_(temps); + + // 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); + } + + // 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 (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; } - return thermal_triggered; + if (!cooling_devices_to_update.empty()) { + updateCoolingDevices(cooling_devices_to_update); + } + + if (!temps.empty()) { + for (const auto &t : temps) { + if (sensor_info_map_.at(t.name).send_cb && cb_) { + cb_(t); + } + + if (sensor_info_map_.at(t.name).send_powerhint && isAidlPowerHalExist()) { + sendPowerExtHint(t); + } + } + } + + power_files_.clearEnergyInfoMap(); + return min_sleep_ms < kMinPollIntervalMs ? kMinPollIntervalMs : min_sleep_ms; } bool ThermalHelper::connectToPowerHal() { @@ -714,14 +1328,11 @@ bool ThermalHelper::connectToPowerHal() { void ThermalHelper::updateSupportedPowerHints() { for (auto const &name_status_pair : sensor_info_map_) { - if (!name_status_pair.second.send_powerhint) { + if (!(name_status_pair.second.send_powerhint)) { continue; } ThrottlingSeverity current_severity = ThrottlingSeverity::NONE; for (const auto &severity : hidl_enum_range()) { - LOG(ERROR) << "sensor: " << name_status_pair.first - << " current_severity :" << toString(current_severity) << " severity " - << toString(severity); if (severity == ThrottlingSeverity::NONE) { supported_powerhint_map_[name_status_pair.first][ThrottlingSeverity::NONE] = ThrottlingSeverity::NONE; @@ -743,11 +1354,6 @@ void ThermalHelper::updateSupportedPowerHints() { void ThermalHelper::sendPowerExtHint(const Temperature_2_0 &t) { std::lock_guard lock(sensor_status_map_mutex_); - if (!isAidlPowerHalExist()) - return; - - if (!sensor_info_map_.at(t.name).send_powerhint) - return; ThrottlingSeverity prev_hint_severity; prev_hint_severity = sensor_status_map_.at(t.name).prev_hint_severity; diff --git a/thermal/thermal-helper.h b/thermal/thermal-helper.h index 37a2dea..672d352 100644 --- a/thermal/thermal-helper.h +++ b/thermal/thermal-helper.h @@ -27,8 +27,7 @@ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef THERMAL_THERMAL_HELPER_H__ -#define THERMAL_THERMAL_HELPER_H__ +#pragma once #include #include @@ -45,6 +44,7 @@ #include #include "utils/config_parser.h" +#include "utils/power_files.h" #include "utils/thermal_files.h" #include "utils/thermal_watcher.h" @@ -69,14 +69,23 @@ using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType; using ::android::hardware::thermal::V2_0::TemperatureThreshold; using ::android::hardware::thermal::V2_0::ThrottlingSeverity; -using NotificationCallback = std::function &temps)>; +using NotificationCallback = std::function; using NotificationTime = std::chrono::time_point; +using CdevRequestStatus = std::unordered_map; + +// Get thermal_zone type +bool getThermalZoneTypeById(int tz_id, std::string *); struct SensorStatus { ThrottlingSeverity severity; ThrottlingSeverity prev_hot_severity; ThrottlingSeverity prev_cold_severity; ThrottlingSeverity prev_hint_severity; + boot_clock::time_point last_update_time; + std::unordered_map pid_request_map; + std::unordered_map hard_limit_request_map; + float err_integral; + float prev_err; }; class PowerHalService { @@ -99,7 +108,7 @@ class PowerHalService { class ThermalHelper { public: - ThermalHelper(const NotificationCallback &cb); + explicit ThermalHelper(const NotificationCallback &cb); ~ThermalHelper() = default; bool fillTemperatures(hidl_vec *temperatures) const; @@ -118,52 +127,104 @@ class ThermalHelper { bool isInitializedOk() const { return is_initialized_; } // Read the temperature of a single sensor. - bool readTemperature(std::string_view sensor_name, Temperature_1_0 *out) const; + bool readTemperature(std::string_view sensor_name, Temperature_1_0 *out, + bool is_virtual_sensor = false) const; bool readTemperature( std::string_view sensor_name, Temperature_2_0 *out, - std::pair *throtting_status = nullptr) const; + std::pair *throtting_status = nullptr, + bool is_virtual_sensor = false) const; bool readTemperatureThreshold(std::string_view sensor_name, TemperatureThreshold *out) const; // Read the value of a single cooling device. bool readCoolingDevice(std::string_view cooling_device, CoolingDevice_2_0 *out) const; // Get SensorInfo Map - const std::map &GetSensorInfoMap() const { return sensor_info_map_; } + const std::unordered_map &GetSensorInfoMap() const { + return sensor_info_map_; + } + // Get CdevInfo Map + const std::unordered_map &GetCdevInfoMap() const { + return cooling_device_info_map_; + } + // Get PowerRailInfo Map + const std::unordered_map &GetPowerRailInfoMap() const { + return power_rail_info_map_; + } + // Get SensorStatus Map + const std::unordered_map &GetSensorStatusMap() const { + std::shared_lock _lock(sensor_status_map_mutex_); + return sensor_status_map_; + } + // Get CdevStatus Map + const std::unordered_map &GetCdevStatusMap() const { + std::shared_lock _lock(cdev_status_map_mutex_); + return cdev_status_map_; + } + // Get ThrottlingRelease Map + const std::unordered_map &GetThrottlingReleaseMap() const { + return power_files_.GetThrottlingReleaseMap(); + } + + // Get PowerStatus Map + const std::unordered_map &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(); } bool isPowerHalExtConnected() { return power_hal_service_.isPowerHalExtConnected(); } private: - bool initializeSensorMap(const std::map &path_map); - bool initializeCoolingDevices(const std::map &path_map); - bool initializeTrip(const std::map &path_map); + bool initializeSensorMap(const std::unordered_map &path_map); + bool initializeCoolingDevices(const std::unordered_map &path_map); + void setMinTimeout(SensorInfo *sensor_info); + void initializeTrip(const std::unordered_map &path_map, + std::set *monitored_sensors, bool thermal_genl_enabled); - // For thermal_watcher_'s polling thread - bool thermalWatcherCallbackFunc(const std::set &uevent_sensors); + // For thermal_watcher_'s polling thread, return the sleep interval + std::chrono::milliseconds thermalWatcherCallbackFunc( + const std::set &uevent_sensors); // Return hot and cold severity status as std::pair std::pair 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; + bool checkVirtualSensor(std::string_view sensor_name, std::string *temp) const; + // 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 *cooling_devices_to_update); + void updateCoolingDevices(const std::vector &cooling_devices_to_update); sp thermal_watcher_; + PowerFiles power_files_; ThermalFiles thermal_sensors_; ThermalFiles cooling_devices_; bool is_initialized_; const NotificationCallback cb_; - const std::map cooling_device_info_map_; - const std::map sensor_info_map_; - std::map> + std::unordered_map cooling_device_info_map_; + std::unordered_map sensor_info_map_; + std::unordered_map power_rail_info_map_; + std::unordered_map> supported_powerhint_map_; PowerHalService power_hal_service_; mutable std::shared_mutex sensor_status_map_mutex_; - std::map sensor_status_map_; + std::unordered_map sensor_status_map_; + mutable std::shared_mutex cdev_status_map_mutex_; + std::unordered_map cdev_status_map_; }; } // namespace implementation @@ -171,5 +232,3 @@ class ThermalHelper { } // namespace thermal } // namespace hardware } // namespace android - -#endif // THERMAL_THERMAL_HELPER_H__ diff --git a/thermal/utils/config_parser.cpp b/thermal/utils/config_parser.cpp index daba0ee..6216dd7 100644 --- a/thermal/utils/config_parser.cpp +++ b/thermal/utils/config_parser.cpp @@ -15,9 +15,10 @@ */ #include #include +#include #include #include -#include +#include #include #include @@ -30,6 +31,8 @@ namespace thermal { namespace V2_0 { namespace implementation { +constexpr std::string_view kPowerLinkDisabledProperty("vendor.disable.thermal.powerlink"); + using ::android::hardware::hidl_enum_range; using ::android::hardware::thermal::V2_0::toString; using TemperatureType_2_0 = ::android::hardware::thermal::V2_0::TemperatureType; @@ -57,27 +60,103 @@ float getFloatFromValue(const Json::Value &value) { } } +int getIntFromValue(const Json::Value &value) { + if (value.isString()) { + return (value.asString() == "max") ? std::numeric_limits::max() + : std::stoul(value.asString()); + } else { + return value.asInt(); + } +} + +bool getIntFromJsonValues(const Json::Value &values, CdevArray *out, bool inc_check, + bool dec_check) { + CdevArray ret; + + if (inc_check && dec_check) { + LOG(ERROR) << "Cannot enable inc_check and dec_check at the same time"; + return false; + } + + if (values.size() != kThrottlingSeverityCount) { + LOG(ERROR) << "Values size is invalid"; + return false; + } else { + int last; + for (Json::Value::ArrayIndex i = 0; i < kThrottlingSeverityCount; ++i) { + ret[i] = getIntFromValue(values[i]); + if (inc_check && ret[i] < last) { + LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " min=" << last; + return false; + } + if (dec_check && ret[i] > last) { + LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " max=" << last; + return false; + } + last = ret[i]; + LOG(INFO) << "[" << i << "]: " << ret[i]; + } + } + + *out = ret; + return true; +} + +bool getFloatFromJsonValues(const Json::Value &values, ThrottlingArray *out, bool inc_check, + bool dec_check) { + ThrottlingArray ret; + + if (inc_check && dec_check) { + LOG(ERROR) << "Cannot enable inc_check and dec_check at the same time"; + return false; + } + + if (values.size() != kThrottlingSeverityCount) { + LOG(ERROR) << "Values size is invalid"; + return false; + } else { + float last = std::nanf(""); + for (Json::Value::ArrayIndex i = 0; i < kThrottlingSeverityCount; ++i) { + ret[i] = getFloatFromValue(values[i]); + if (inc_check && !std::isnan(last) && !std::isnan(ret[i]) && ret[i] < last) { + LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " min=" << last; + return false; + } + if (dec_check && !std::isnan(last) && !std::isnan(ret[i]) && ret[i] > last) { + LOG(FATAL) << "Invalid array[" << i << "]" << ret[i] << " max=" << last; + return false; + } + last = std::isnan(ret[i]) ? last : ret[i]; + LOG(INFO) << "[" << i << "]: " << ret[i]; + } + } + + *out = ret; + return true; +} } // namespace -std::map ParseSensorInfo(std::string_view config_path) { +std::unordered_map ParseSensorInfo(std::string_view config_path) { std::string json_doc; - std::map sensors_parsed; + std::unordered_map sensors_parsed; if (!android::base::ReadFileToString(config_path.data(), &json_doc)) { LOG(ERROR) << "Failed to read JSON config from " << config_path; return sensors_parsed; } Json::Value root; - Json::Reader reader; + Json::CharReaderBuilder builder; + std::unique_ptr reader(builder.newCharReader()); + std::string errorMessage; - if (!reader.parse(json_doc, root)) { - LOG(ERROR) << "Failed to parse JSON config"; + if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) { + LOG(ERROR) << "Failed to parse JSON config: " << errorMessage; return sensors_parsed; } Json::Value sensors = root["Sensors"]; std::size_t total_parsed = 0; - std::set sensors_name_parsed; + std::unordered_set sensors_name_parsed; for (Json::Value::ArrayIndex i = 0; i < sensors.size(); ++i) { const std::string &name = sensors[i]["Name"].asString(); @@ -107,6 +186,24 @@ std::map ParseSensorInfo(std::string_view config_path) return sensors_parsed; } + bool send_cb = false; + if (sensors[i]["Monitor"].empty() || !sensors[i]["Monitor"].isBool()) { + LOG(INFO) << "Failed to read Sensor[" << name << "]'s Monitor, set to 'false'"; + } else if (sensors[i]["Monitor"].asBool()) { + send_cb = true; + } + LOG(INFO) << "Sensor[" << name << "]'s SendCallback: " << std::boolalpha << send_cb + << std::noboolalpha; + + bool send_powerhint = false; + if (sensors[i]["SendPowerHint"].empty() || !sensors[i]["SendPowerHint"].isBool()) { + LOG(INFO) << "Failed to read Sensor[" << name << "]'s SendPowerHint, set to 'false'"; + } else if (sensors[i]["SendPowerHint"].asBool()) { + send_powerhint = true; + } + LOG(INFO) << "Sensor[" << name << "]'s SendPowerHint: " << std::boolalpha << send_powerhint + << std::noboolalpha; + std::array hot_thresholds; hot_thresholds.fill(NAN); std::array cold_thresholds; @@ -115,7 +212,18 @@ std::map ParseSensorInfo(std::string_view config_path) hot_hysteresis.fill(0.0); std::array cold_hysteresis; cold_hysteresis.fill(0.0); + std::vector linked_sensors; + std::vector coefficients; + float offset = 0; + std::string trigger_sensor; + FormulaOption formula = FormulaOption::COUNT_THRESHOLD; + bool is_virtual_sensor = false; + if (sensors[i]["VirtualSensor"].empty() || !sensors[i]["VirtualSensor"].isBool()) { + LOG(INFO) << "Failed to read Sensor[" << name << "]'s VirtualSensor, set to 'false'"; + } else { + is_virtual_sensor = sensors[i]["VirtualSensor"].asBool(); + } Json::Value values = sensors[i]["HotThreshold"]; if (values.size() != kThrottlingSeverityCount) { LOG(ERROR) << "Invalid " @@ -201,6 +309,63 @@ std::map ParseSensorInfo(std::string_view config_path) } } + if (is_virtual_sensor) { + values = sensors[i]["Combination"]; + if (values.size()) { + linked_sensors.reserve(values.size()); + for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) { + linked_sensors.emplace_back(values[j].asString()); + LOG(INFO) << "Sensor[" << name << "]'s combination[" << j + << "]: " << linked_sensors[j]; + } + } else { + sensors_parsed.clear(); + return sensors_parsed; + } + + values = sensors[i]["Coefficient"]; + if (values.size()) { + coefficients.reserve(values.size()); + for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) { + coefficients.emplace_back(getFloatFromValue(values[j])); + LOG(INFO) << "Sensor[" << name << "]'s coefficient[" << j + << "]: " << coefficients[j]; + } + } else { + sensors_parsed.clear(); + return sensors_parsed; + } + + if (!sensors[i]["Offset"].empty()) { + offset = sensors[i]["Offset"].asFloat(); + } + + if (linked_sensors.size() != coefficients.size()) { + sensors_parsed.clear(); + return sensors_parsed; + } + + trigger_sensor = sensors[i]["TriggerSensor"].asString(); + if (sensors[i]["Formula"].asString().compare("COUNT_THRESHOLD") == 0) { + formula = FormulaOption::COUNT_THRESHOLD; + } else if (sensors[i]["Formula"].asString().compare("WEIGHTED_AVG") == 0) { + formula = FormulaOption::WEIGHTED_AVG; + } else if (sensors[i]["Formula"].asString().compare("MAXIMUM") == 0) { + formula = FormulaOption::MAXIMUM; + } else if (sensors[i]["Formula"].asString().compare("MINIMUM") == 0) { + formula = FormulaOption::MINIMUM; + } else { + sensors_parsed.clear(); + return sensors_parsed; + } + } + + std::string temp_path; + if (!sensors[i]["TempPath"].empty()) { + temp_path = sensors[i]["TempPath"].asString(); + LOG(INFO) << "Sensor[" << name << "]'s TempPath: " << temp_path; + } + float vr_threshold = NAN; vr_threshold = getFloatFromValue(sensors[i]["VrThreshold"]); LOG(INFO) << "Sensor[" << name << "]'s VrThreshold: " << vr_threshold; @@ -208,23 +373,287 @@ std::map ParseSensorInfo(std::string_view config_path) float multiplier = sensors[i]["Multiplier"].asFloat(); LOG(INFO) << "Sensor[" << name << "]'s Multiplier: " << multiplier; - bool is_monitor = false; - if (sensors[i]["Monitor"].empty() || !sensors[i]["Monitor"].isBool()) { - LOG(INFO) << "Failed to read Sensor[" << name << "]'s Monitor, set to 'false'"; + std::chrono::milliseconds polling_delay; + if (sensors[i]["PollingDelay"].empty()) { + polling_delay = kUeventPollTimeoutMs; } else { - is_monitor = sensors[i]["Monitor"].asBool(); + polling_delay = std::chrono::milliseconds(getIntFromValue(sensors[i]["PollingDelay"])); } - LOG(INFO) << "Sensor[" << name << "]'s Monitor: " << std::boolalpha << is_monitor - << std::noboolalpha; + LOG(INFO) << "Sensor[" << name << "]'s Polling delay: " << polling_delay.count(); - bool send_powerhint = false; - if (sensors[i]["SendPowerHint"].empty() || !sensors[i]["SendPowerHint"].isBool()) { - LOG(INFO) << "Failed to read Sensor[" << name << "]'s SendPowerHint, set to 'false'"; + std::chrono::milliseconds passive_delay; + if (sensors[i]["PassiveDelay"].empty()) { + passive_delay = kMinPollIntervalMs; } else { - send_powerhint = sensors[i]["SendPowerHint"].asBool(); + passive_delay = std::chrono::milliseconds(getIntFromValue(sensors[i]["PassiveDelay"])); } - LOG(INFO) << "Sensor[" << name << "]'s SendPowerHint: " << std::boolalpha << send_powerhint - << std::noboolalpha; + LOG(INFO) << "Sensor[" << name << "]'s Passive delay: " << passive_delay.count(); + + bool support_pid = false; + std::array k_po; + k_po.fill(0.0); + std::array k_pu; + k_pu.fill(0.0); + std::array k_i; + k_i.fill(0.0); + std::array k_d; + k_d.fill(0.0); + std::array i_max; + i_max.fill(NAN); + std::array max_alloc_power; + max_alloc_power.fill(NAN); + std::array min_alloc_power; + min_alloc_power.fill(NAN); + std::array s_power; + s_power.fill(NAN); + std::array i_cutoff; + i_cutoff.fill(NAN); + + // Parse PID parameters + if (!sensors[i]["PIDInfo"].empty()) { + LOG(INFO) << "Start to parse" + << " Sensor[" << name << "]'s K_Po"; + if (sensors[i]["PIDInfo"]["K_Po"].empty() || + !getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_Po"], &k_po, false, false)) { + LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Po"; + sensors_parsed.clear(); + return sensors_parsed; + } + LOG(INFO) << "Start to parse" + << " Sensor[" << name << "]'s K_Pu"; + if (sensors[i]["PIDInfo"]["K_Pu"].empty() || + !getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_Pu"], &k_pu, false, false)) { + LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_Pu"; + sensors_parsed.clear(); + return sensors_parsed; + } + LOG(INFO) << "Start to parse" + << " Sensor[" << name << "]'s K_I"; + if (sensors[i]["PIDInfo"]["K_I"].empty() || + !getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_I"], &k_i, false, false)) { + LOG(INFO) << "Sensor[" << name << "]: Failed to parse K_I"; + sensors_parsed.clear(); + return sensors_parsed; + } + LOG(INFO) << "Start to parse" + << " Sensor[" << name << "]'s K_D"; + if (sensors[i]["PIDInfo"]["K_D"].empty() || + !getFloatFromJsonValues(sensors[i]["PIDInfo"]["K_D"], &k_d, false, false)) { + LOG(ERROR) << "Sensor[" << name << "]: Failed to parse K_D"; + sensors_parsed.clear(); + return sensors_parsed; + } + LOG(INFO) << "Start to parse" + << " Sensor[" << name << "]'s I_Max"; + if (sensors[i]["PIDInfo"]["I_Max"].empty() || + !getFloatFromJsonValues(sensors[i]["PIDInfo"]["I_Max"], &i_max, false, false)) { + LOG(ERROR) << "Sensor[" << name << "]: Failed to parse I_Max"; + sensors_parsed.clear(); + return sensors_parsed; + } + LOG(INFO) << "Start to parse" + << " Sensor[" << name << "]'s MaxAllocPower"; + if (sensors[i]["PIDInfo"]["MaxAllocPower"].empty() || + !getFloatFromJsonValues(sensors[i]["PIDInfo"]["MaxAllocPower"], &max_alloc_power, + false, true)) { + LOG(ERROR) << "Sensor[" << name << "]: Failed to parse MaxAllocPower"; + sensors_parsed.clear(); + return sensors_parsed; + } + LOG(INFO) << "Start to parse" + << " Sensor[" << name << "]'s MinAllocPower"; + if (sensors[i]["PIDInfo"]["MinAllocPower"].empty() || + !getFloatFromJsonValues(sensors[i]["PIDInfo"]["MinAllocPower"], &min_alloc_power, + false, true)) { + LOG(ERROR) << "Sensor[" << name << "]: Failed to parse MinAllocPower"; + sensors_parsed.clear(); + return sensors_parsed; + } + LOG(INFO) << "Start to parse" + << " Sensor[" << name << "]'s S_Power"; + if (sensors[i]["PIDInfo"]["S_Power"].empty() || + !getFloatFromJsonValues(sensors[i]["PIDInfo"]["S_Power"], &s_power, false, true)) { + LOG(ERROR) << "Sensor[" << name << "]: Failed to parse S_Power"; + sensors_parsed.clear(); + return sensors_parsed; + } + LOG(INFO) << "Start to parse" + << " Sensor[" << name << "]'s I_Cutoff"; + if (sensors[i]["PIDInfo"]["I_Cutoff"].empty() || + !getFloatFromJsonValues(sensors[i]["PIDInfo"]["I_Cutoff"], &i_cutoff, false, + false)) { + LOG(ERROR) << "Sensor[" << name << "]: Failed to parse I_Cutoff"; + sensors_parsed.clear(); + return sensors_parsed; + } + // Confirm we have at least one valid PID combination + bool valid_pid_combination = false; + for (Json::Value::ArrayIndex j = 0; j < kThrottlingSeverityCount; ++j) { + if (!std::isnan(s_power[j])) { + if (std::isnan(k_po[j]) || std::isnan(k_pu[j]) || std::isnan(k_i[j]) || + std::isnan(k_d[j]) || std::isnan(i_max[j]) || + std::isnan(max_alloc_power[j]) || std::isnan(min_alloc_power[j]) || + std::isnan(i_cutoff[j])) { + valid_pid_combination = false; + break; + } else { + valid_pid_combination = true; + } + } + } + if (!valid_pid_combination) { + LOG(ERROR) << "Sensor[" << name << "]: Invalid PID parameters combinations"; + sensors_parsed.clear(); + return sensors_parsed; + } else { + support_pid = true; + } + } + + // Parse binded cooling device + bool support_hard_limit = false; + std::unordered_map binded_cdev_info_map; + values = sensors[i]["BindedCdevInfo"]; + for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) { + Json::Value sub_values; + const std::string &cdev_name = values[j]["CdevRequest"].asString(); + ThrottlingArray cdev_weight_for_pid; + cdev_weight_for_pid.fill(NAN); + CdevArray cdev_ceiling; + cdev_ceiling.fill(std::numeric_limits::max()); + if (support_pid) { + if (!values[j]["CdevWeightForPID"].empty()) { + LOG(INFO) << "Sensor[" << name << "]: Star to parse " << cdev_name + << "'s CdevWeightForPID"; + if (!getFloatFromJsonValues(values[j]["CdevWeightForPID"], &cdev_weight_for_pid, + false, false)) { + LOG(ERROR) << "Failed to parse CdevWeightForPID"; + sensors_parsed.clear(); + return sensors_parsed; + } + } + if (!values[j]["CdevCeiling"].empty()) { + LOG(INFO) << "Sensor[" << name + << "]: Start to parse CdevCeiling: " << cdev_name; + if (!getIntFromJsonValues(values[j]["CdevCeiling"], &cdev_ceiling, false, + false)) { + LOG(ERROR) << "Failed to parse CdevCeiling"; + sensors_parsed.clear(); + return sensors_parsed; + } + } + } + CdevArray limit_info; + limit_info.fill(0); + ThrottlingArray power_thresholds; + power_thresholds.fill(NAN); + + ReleaseLogic release_logic = ReleaseLogic::NONE; + + sub_values = values[j]["LimitInfo"]; + if (sub_values.size()) { + LOG(INFO) << "Sensor[" << name << "]: Start to parse LimitInfo: " << cdev_name; + if (!getIntFromJsonValues(sub_values, &limit_info, false, false)) { + LOG(ERROR) << "Failed to parse LimitInfo"; + sensors_parsed.clear(); + return sensors_parsed; + } + support_hard_limit = true; + } + + // Parse linked power info + bool is_power_data_invalid = false; + std::string power_rail; + bool high_power_check = false; + bool throttling_with_power_link = false; + CdevArray cdev_floor_with_power_link; + cdev_floor_with_power_link.fill(0); + + const bool power_link_disabled = + android::base::GetBoolProperty(kPowerLinkDisabledProperty.data(), false); + if (!power_link_disabled) { + power_rail = values[j]["BindedPowerRail"].asString(); + + if (values[j]["HighPowerCheck"].asBool()) { + high_power_check = true; + } + LOG(INFO) << "Highpowercheck: " << std::boolalpha << high_power_check; + + if (values[j]["ThrottlingWithPowerLink"].asBool()) { + throttling_with_power_link = true; + } + LOG(INFO) << "ThrottlingwithPowerLink: " << std::boolalpha + << throttling_with_power_link; + + sub_values = values[j]["CdevFloorWithPowerLink"]; + if (sub_values.size()) { + LOG(INFO) << "Sensor[" << name << "]: Start to parse " << cdev_name + << "'s CdevFloorWithPowerLink"; + if (!getIntFromJsonValues(sub_values, &cdev_floor_with_power_link, false, + false)) { + LOG(ERROR) << "Failed to parse CdevFloor"; + is_power_data_invalid = true; + } + } + sub_values = values[j]["PowerThreshold"]; + if (sub_values.size()) { + LOG(INFO) << "Sensor[" << name << "]: Start to parse " << cdev_name + << "'s PowerThreshold"; + if (!getFloatFromJsonValues(sub_values, &power_thresholds, false, false)) { + LOG(ERROR) << "Failed to parse power thresholds"; + is_power_data_invalid = true; + } + + if (values[j]["ReleaseLogic"].asString() == "INCREASE") { + release_logic = ReleaseLogic::INCREASE; + LOG(INFO) << "Release logic: INCREASE"; + } else if (values[j]["ReleaseLogic"].asString() == "DECREASE") { + release_logic = ReleaseLogic::DECREASE; + LOG(INFO) << "Release logic: DECREASE"; + } else if (values[j]["ReleaseLogic"].asString() == "STEPWISE") { + release_logic = ReleaseLogic::STEPWISE; + LOG(INFO) << "Release logic: STEPWISE"; + } else if (values[j]["ReleaseLogic"].asString() == "RELEASE_TO_FLOOR") { + release_logic = ReleaseLogic::RELEASE_TO_FLOOR; + LOG(INFO) << "Release logic: RELEASE_TO_FLOOR"; + } else { + LOG(ERROR) << "Release logic is invalid"; + is_power_data_invalid = true; + } + + if (is_power_data_invalid) { + LOG(ERROR) << cdev_name << "'s power rail " << power_rail << " is invalid"; + sensors_parsed.clear(); + return sensors_parsed; + } + } + } + + binded_cdev_info_map[cdev_name] = { + .limit_info = limit_info, + .power_thresholds = power_thresholds, + .release_logic = release_logic, + .high_power_check = high_power_check, + .throttling_with_power_link = throttling_with_power_link, + .cdev_weight_for_pid = cdev_weight_for_pid, + .cdev_ceiling = cdev_ceiling, + .cdev_floor_with_power_link = cdev_floor_with_power_link, + .power_rail = power_rail, + }; + } + + bool is_monitor = (send_cb | send_powerhint | support_pid | support_hard_limit); + LOG(INFO) << "Sensor[" << name << "]'s Monitor: " << std::boolalpha << is_monitor; + + std::unique_ptr virtual_sensor_info; + if (is_virtual_sensor) { + virtual_sensor_info.reset(new VirtualSensorInfo{linked_sensors, coefficients, offset, + trigger_sensor, formula}); + } + + std::unique_ptr 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}); sensors_parsed[name] = { .type = sensor_type, @@ -232,11 +661,18 @@ std::map ParseSensorInfo(std::string_view config_path) .cold_thresholds = cold_thresholds, .hot_hysteresis = hot_hysteresis, .cold_hysteresis = cold_hysteresis, + .temp_path = temp_path, .vr_threshold = vr_threshold, .multiplier = multiplier, - .is_monitor = is_monitor, + .polling_delay = polling_delay, + .passive_delay = passive_delay, + .send_cb = send_cb, .send_powerhint = send_powerhint, + .is_monitor = is_monitor, + .virtual_sensor_info = std::move(virtual_sensor_info), + .throttling_info = std::move(throttling_info), }; + ++total_parsed; } @@ -244,25 +680,27 @@ std::map ParseSensorInfo(std::string_view config_path) return sensors_parsed; } -std::map ParseCoolingDevice(std::string_view config_path) { +std::unordered_map ParseCoolingDevice(std::string_view config_path) { std::string json_doc; - std::map cooling_devices_parsed; + std::unordered_map cooling_devices_parsed; if (!android::base::ReadFileToString(config_path.data(), &json_doc)) { LOG(ERROR) << "Failed to read JSON config from " << config_path; return cooling_devices_parsed; } Json::Value root; - Json::Reader reader; + Json::CharReaderBuilder builder; + std::unique_ptr reader(builder.newCharReader()); + std::string errorMessage; - if (!reader.parse(json_doc, root)) { + if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) { LOG(ERROR) << "Failed to parse JSON config"; return cooling_devices_parsed; } Json::Value cooling_devices = root["CoolingDevices"]; std::size_t total_parsed = 0; - std::set cooling_devices_name_parsed; + std::unordered_set cooling_devices_name_parsed; for (Json::Value::ArrayIndex i = 0; i < cooling_devices.size(); ++i) { const std::string &name = cooling_devices[i]["Name"].asString(); @@ -292,8 +730,35 @@ std::map ParseCoolingDevice(std::string_view config_pa return cooling_devices_parsed; } - cooling_devices_parsed[name] = cooling_device_type; + const std::string &read_path = cooling_devices[i]["ReadPath"].asString(); + LOG(INFO) << "Cdev Read Path: " << (read_path.empty() ? "default" : read_path); + const std::string &write_path = cooling_devices[i]["WritePath"].asString(); + LOG(INFO) << "Cdev Write Path: " << (write_path.empty() ? "default" : write_path); + + std::vector state2power; + Json::Value values = cooling_devices[i]["State2Power"]; + if (values.size()) { + state2power.reserve(values.size()); + for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) { + state2power.emplace_back(getFloatFromValue(values[j])); + LOG(INFO) << "Cooling device[" << name << "]'s Power2State[" << j + << "]: " << state2power[j]; + } + } else { + LOG(INFO) << "CoolingDevice[" << i << "]'s Name: " << name + << " does not support Power2State"; + } + + const std::string &power_rail = cooling_devices[i]["PowerRail"].asString(); + LOG(INFO) << "Cooling device power rail : " << power_rail; + + cooling_devices_parsed[name] = { + .type = cooling_device_type, + .read_path = read_path, + .write_path = write_path, + .state2power = state2power, + }; ++total_parsed; } @@ -301,6 +766,139 @@ std::map ParseCoolingDevice(std::string_view config_pa return cooling_devices_parsed; } +std::unordered_map ParsePowerRailInfo(std::string_view config_path) { + std::string json_doc; + std::unordered_map power_rails_parsed; + if (!android::base::ReadFileToString(config_path.data(), &json_doc)) { + LOG(ERROR) << "Failed to read JSON config from " << config_path; + return power_rails_parsed; + } + + Json::Value root; + Json::CharReaderBuilder builder; + std::unique_ptr reader(builder.newCharReader()); + std::string errorMessage; + + if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) { + LOG(ERROR) << "Failed to parse JSON config"; + return power_rails_parsed; + } + + Json::Value power_rails = root["PowerRails"]; + std::size_t total_parsed = 0; + std::unordered_set power_rails_name_parsed; + + for (Json::Value::ArrayIndex i = 0; i < power_rails.size(); ++i) { + const std::string &name = power_rails[i]["Name"].asString(); + LOG(INFO) << "PowerRail[" << i << "]'s Name: " << name; + if (name.empty()) { + LOG(ERROR) << "Failed to read " + << "PowerRail[" << i << "]'s Name"; + power_rails_parsed.clear(); + return power_rails_parsed; + } + + std::string rail; + if (power_rails[i]["Rail"].empty()) { + rail = name; + } else { + rail = power_rails[i]["Rail"].asString(); + } + LOG(INFO) << "PowerRail[" << i << "]'s Rail: " << rail; + + std::vector linked_power_rails; + std::vector coefficients; + float offset = 0; + FormulaOption formula = FormulaOption::COUNT_THRESHOLD; + bool is_virtual_power_rail = false; + Json::Value values; + int power_sample_count = 0; + std::chrono::milliseconds power_sample_delay; + + if (!power_rails[i]["VirtualRails"].empty() && power_rails[i]["VirtualRails"].isBool()) { + is_virtual_power_rail = power_rails[i]["VirtualRails"].asBool(); + LOG(INFO) << "PowerRails[" << name << "]'s VirtualRail, set to 'true'"; + } + + if (is_virtual_power_rail) { + values = power_rails[i]["Combination"]; + if (values.size()) { + linked_power_rails.reserve(values.size()); + for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) { + linked_power_rails.emplace_back(values[j].asString()); + LOG(INFO) << "PowerRail[" << name << "]'s combination[" << j + << "]: " << linked_power_rails[j]; + } + } else { + power_rails_parsed.clear(); + return power_rails_parsed; + } + + values = power_rails[i]["Coefficient"]; + if (values.size()) { + coefficients.reserve(values.size()); + for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) { + coefficients.emplace_back(getFloatFromValue(values[j])); + LOG(INFO) << "PowerRail[" << name << "]'s coefficient[" << j + << "]: " << coefficients[j]; + } + } else { + power_rails_parsed.clear(); + return power_rails_parsed; + } + + if (!power_rails[i]["Offset"].empty()) { + offset = power_rails[i]["Offset"].asFloat(); + } + + if (linked_power_rails.size() != coefficients.size()) { + power_rails_parsed.clear(); + return power_rails_parsed; + } + + if (power_rails[i]["Formula"].asString().compare("COUNT_THRESHOLD") == 0) { + formula = FormulaOption::COUNT_THRESHOLD; + } else if (power_rails[i]["Formula"].asString().compare("WEIGHTED_AVG") == 0) { + formula = FormulaOption::WEIGHTED_AVG; + } else if (power_rails[i]["Formula"].asString().compare("MAXIMUM") == 0) { + formula = FormulaOption::MAXIMUM; + } else if (power_rails[i]["Formula"].asString().compare("MINIMUM") == 0) { + formula = FormulaOption::MINIMUM; + } else { + power_rails_parsed.clear(); + return power_rails_parsed; + } + } + + std::unique_ptr virtual_power_rail_info; + if (is_virtual_power_rail) { + virtual_power_rail_info.reset( + new VirtualPowerRailInfo{linked_power_rails, coefficients, offset, formula}); + } + + power_sample_count = power_rails[i]["PowerSampleCount"].asInt(); + LOG(INFO) << "Power sample Count: " << power_sample_count; + + if (!power_rails[i]["PowerSampleDelay"]) { + power_sample_delay = std::chrono::milliseconds::max(); + } else { + power_sample_delay = + std::chrono::milliseconds(getIntFromValue(power_rails[i]["PowerSampleDelay"])); + } + + power_rails_parsed[name] = { + .rail = rail, + .power_sample_count = power_sample_count, + .power_sample_delay = power_sample_delay, + .virtual_power_rail_info = std::move(virtual_power_rail_info), + }; + ++total_parsed; + } + + LOG(INFO) << total_parsed << " PowerRails parsed successfully"; + return power_rails_parsed; +} + } // namespace implementation } // namespace V2_0 } // namespace thermal diff --git a/thermal/utils/config_parser.h b/thermal/utils/config_parser.h index 399fef6..db941c9 100644 --- a/thermal/utils/config_parser.h +++ b/thermal/utils/config_parser.h @@ -14,11 +14,10 @@ * limitations under the License. */ -#ifndef THERMAL_UTILS_CONFIG_PARSER_H__ -#define THERMAL_UTILS_CONFIG_PARSER_H__ +#pragma once -#include #include +#include #include @@ -29,12 +28,73 @@ namespace V2_0 { namespace implementation { using ::android::hardware::hidl_enum_range; -using ::android::hardware::thermal::V2_0::CoolingType; +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().begin(), hidl_enum_range().end()); using ThrottlingArray = std::array(kThrottlingSeverityCount)>; +using CdevArray = std::array(kThrottlingSeverityCount)>; +constexpr std::chrono::milliseconds kMinPollIntervalMs = std::chrono::milliseconds(2000); +constexpr std::chrono::milliseconds kUeventPollTimeoutMs = std::chrono::milliseconds(300000); + +enum FormulaOption : uint32_t { + COUNT_THRESHOLD = 0, + WEIGHTED_AVG, + MAXIMUM, + MINIMUM, +}; + +struct VirtualSensorInfo { + std::vector linked_sensors; + std::vector coefficients; + float offset; + std::string trigger_sensor; + FormulaOption formula; +}; + +struct VirtualPowerRailInfo { + std::vector linked_power_rails; + std::vector coefficients; + float offset; + FormulaOption formula; +}; + +// The method when the ODPM power is lower than threshold +enum ReleaseLogic : uint32_t { + INCREASE = 0, // Increase throttling by step + DECREASE, // Decrease throttling by step + STEPWISE, // Support both increase and decrease logix + RELEASE_TO_FLOOR, // Release throttling to floor directly + NONE, +}; + +struct BindedCdevInfo { + CdevArray limit_info; + ThrottlingArray power_thresholds; + ReleaseLogic release_logic; + ThrottlingArray cdev_weight_for_pid; + CdevArray cdev_ceiling; + CdevArray cdev_floor_with_power_link; + std::string power_rail; + // The flag for activate release logic when power is higher than power threshold + bool high_power_check; + // The flag for only triggering throttling until all power samples are collected + bool throttling_with_power_link; +}; + +struct ThrottlingInfo { + ThrottlingArray k_po; + ThrottlingArray k_pu; + ThrottlingArray k_i; + ThrottlingArray k_d; + ThrottlingArray i_max; + ThrottlingArray max_alloc_power; + ThrottlingArray min_alloc_power; + ThrottlingArray s_power; + ThrottlingArray i_cutoff; + std::unordered_map binded_cdev_info_map; +}; struct SensorInfo { TemperatureType_2_0 type; @@ -42,19 +102,38 @@ struct SensorInfo { ThrottlingArray cold_thresholds; ThrottlingArray hot_hysteresis; ThrottlingArray cold_hysteresis; + std::string temp_path; float vr_threshold; float multiplier; - bool is_monitor; + std::chrono::milliseconds polling_delay; + std::chrono::milliseconds passive_delay; + bool send_cb; bool send_powerhint; + bool is_monitor; + std::unique_ptr virtual_sensor_info; + std::unique_ptr throttling_info; }; -std::map ParseSensorInfo(std::string_view config_path); -std::map ParseCoolingDevice(std::string_view config_path); +struct CdevInfo { + CoolingType_2_0 type; + std::string read_path; + std::string write_path; + std::vector state2power; + int max_state; +}; +struct PowerRailInfo { + std::string rail; + int power_sample_count; + std::chrono::milliseconds power_sample_delay; + std::unique_ptr virtual_power_rail_info; +}; + +std::unordered_map ParseSensorInfo(std::string_view config_path); +std::unordered_map ParseCoolingDevice(std::string_view config_path); +std::unordered_map ParsePowerRailInfo(std::string_view config_path); } // namespace implementation } // namespace V2_0 } // namespace thermal } // namespace hardware } // namespace android - -#endif // THERMAL_UTILS_CONFIG_PARSER_H__ diff --git a/thermal/utils/power_files.cpp b/thermal/utils/power_files.cpp new file mode 100644 index 0000000..b91061a --- /dev/null +++ b/thermal/utils/power_files.cpp @@ -0,0 +1,444 @@ +/* + * 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. + * 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 + +#include +#include +#include +#include + +#include "power_files.h" + +namespace android { +namespace hardware { +namespace thermal { +namespace V2_0 { +namespace implementation { + +constexpr std::string_view kDeviceType("iio:device"); +constexpr std::string_view kIioRootDir("/sys/bus/iio/devices"); +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 _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; + } +} + +int PowerFiles::getReleaseStep(std::string_view sensor_name, std::string_view cdev_name) { + int release_step = 0; + std::shared_lock _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; + } + + 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> 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 (!energy_info_map_.size() && !updateEnergyValues()) { + LOG(ERROR) << "Faield to update energy info"; + 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])) { + power_history.emplace_back(std::queue()); + for (int j = 0; j < power_rail_info.power_sample_count; j++) { + power_history[i].emplace(power_sample); + } + } + } + } else { + if (energy_info_map_.count(power_rail_info.rail)) { + power_history.emplace_back(std::queue()); + for (int j = 0; j < power_rail_info.power_sample_count; j++) { + power_history[0].emplace(power_sample); + } + } + } + + 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; +} + +bool PowerFiles::findEnergySourceToWatch(void) { + std::string devicePath; + + if (energy_path_set_.size()) { + return true; + } + + std::unique_ptr dir(opendir(kIioRootDir.data()), closedir); + if (!dir) { + PLOG(ERROR) << "Error opening directory" << kIioRootDir; + return false; + } + + // Find any iio:devices that support energy_value + while (struct dirent *ent = readdir(dir.get())) { + std::string devTypeDir = ent->d_name; + if (devTypeDir.find(kDeviceType) != std::string::npos) { + devicePath = StringPrintf("%s/%s", kIioRootDir.data(), devTypeDir.data()); + std::string deviceEnergyContent; + + if (!ReadFileToString(StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data()), + &deviceEnergyContent)) { + } else if (deviceEnergyContent.size()) { + energy_path_set_.emplace( + StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data())); + } + } + } + + if (!energy_path_set_.size()) { + return false; + } + + return true; +} + +void PowerFiles::clearEnergyInfoMap(void) { + energy_info_map_.clear(); +} + +bool PowerFiles::updateEnergyValues(void) { + std::string deviceEnergyContent; + std::string deviceEnergyContents; + std::string line; + + for (const auto &path : energy_path_set_) { + if (!android::base::ReadFileToString(path, &deviceEnergyContent)) { + LOG(ERROR) << "Failed to read energy content from " << path; + return false; + } else { + deviceEnergyContents.append(deviceEnergyContent); + } + } + + std::istringstream energyData(deviceEnergyContents); + + clearEnergyInfoMap(); + while (std::getline(energyData, line)) { + /* Read rail energy */ + uint64_t energy_counter = 0; + uint64_t duration = 0; + + /* Format example: CH3(T=358356)[S2M_VDD_CPUCL2], 761330 */ + auto start_pos = line.find("T="); + auto end_pos = line.find(')'); + if (start_pos != std::string::npos) { + duration = + strtoul(line.substr(start_pos + 2, end_pos - start_pos - 2).c_str(), NULL, 10); + } else { + continue; + } + + start_pos = line.find(")["); + end_pos = line.find(']'); + std::string railName; + if (start_pos != std::string::npos) { + railName = line.substr(start_pos + 2, end_pos - start_pos - 2); + } else { + continue; + } + + start_pos = line.find("],"); + if (start_pos != std::string::npos) { + energy_counter = strtoul(line.substr(start_pos + 2).c_str(), NULL, 10); + } else { + continue; + } + + energy_info_map_[railName] = { + .energy_counter = energy_counter, + .duration = duration, + }; + } + + return true; +} + +bool PowerFiles::getAveragePower(std::string_view power_rail, + std::queue *power_history, bool power_sample_update, + float *avg_power) { + const auto curr_sample = energy_info_map_.at(power_rail.data()); + bool ret = true; + + const auto last_sample = power_history->front(); + 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"; + } else if (duration <= 0 || deltaEnergy < 0) { + LOG(ERROR) << "Power rail " << power_rail.data() << " is invalid: duration = " << duration + << ", deltaEnergy = " << deltaEnergy; + + ret = false; + } else { + *avg_power = static_cast(deltaEnergy) / static_cast(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); + } + + return ret; +} + +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::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::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; + } + + *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 _lock(throttling_release_map_mutex_); + float avg_power = -1; + + 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; + } + + 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); + + 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; + } + + if (!energy_info_map_.size() && !updateEnergyValues()) { + LOG(ERROR) << "Failed to update energy values"; + release_status.release_step = 0; + return false; + } + + if (!power_sample_update && !std::isnan(power_status.last_updated_avg_power)) { + avg_power = power_status.last_updated_avg_power; + } 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; + } + return true; + } + } + + 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(severity)]) { + is_over_budget = false; + } + } else { + if (avg_power > binded_cdev_info.power_thresholds[static_cast(severity)]) { + is_over_budget = false; + } + } + LOG(INFO) << "Power rail " << binded_cdev_info.power_rail << ": power threshold = " + << binded_cdev_info.power_thresholds[static_cast(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(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(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(release_status.max_release_step)) { + release_status.release_step++; + } + } else { + if (std::abs(release_status.release_step) < + static_cast(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; + } + return true; +} + +} // namespace implementation +} // namespace V2_0 +} // namespace thermal +} // namespace hardware +} // namespace android diff --git a/thermal/utils/power_files.h b/thermal/utils/power_files.h new file mode 100644 index 0000000..6207ba5 --- /dev/null +++ b/thermal/utils/power_files.h @@ -0,0 +1,127 @@ +/* + * 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. + * 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 +#include +#include +#include +#include + +#include "config_parser.h" + +namespace android { +namespace hardware { +namespace thermal { +namespace V2_0 { +namespace implementation { + +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; + // A vector to record the queues of power sample history. + std::vector> power_history; + float last_updated_avg_power; +}; + +using CdevReleaseStatus = std::unordered_map; +using PowerStatusMap = std::unordered_map; + +// A helper class for monitoring power rails. +class PowerFiles { + public: + PowerFiles() = default; + ~PowerFiles() = default; + // 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 *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 &GetThrottlingReleaseMap() const { + std::shared_lock _lock(throttling_release_map_mutex_); + return throttling_release_map_; + } + + // Get Power status map + const std::unordered_map &GetPowerStatusMap() const { + std::shared_lock _lock(power_status_map_mutex_); + return power_status_map_; + } + + private: + // The map to record the energy info for each power rail. + std::unordered_map energy_info_map_; + // The map to record the throttling release status for each thermal sensor. + std::unordered_map 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 power_status_map_; + mutable std::shared_mutex power_status_map_mutex_; + // The set to store the energy source paths + std::unordered_set energy_path_set_; +}; + +} // 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 c394816..e5eb6be 100644 --- a/thermal/utils/thermal_files.cpp +++ b/thermal/utils/thermal_files.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include "thermal_files.h" @@ -45,6 +46,7 @@ bool ThermalFiles::readThermalFile(std::string_view thermal_name, std::string *d std::string file_path = getThermalFilePath(std::string_view(thermal_name)); *data = ""; if (file_path.empty()) { + PLOG(WARNING) << "Failed to find " << thermal_name << "'s path"; return false; } @@ -58,6 +60,18 @@ bool ThermalFiles::readThermalFile(std::string_view thermal_name, std::string *d return true; } +bool ThermalFiles::writeCdevFile(std::string_view cdev_name, std::string_view data) { + std::string file_path = + getThermalFilePath(android::base::StringPrintf("%s_%s", cdev_name.data(), "w")); + + if (!android::base::WriteStringToFile(data.data(), file_path)) { + PLOG(WARNING) << "Failed to write cdev: " << cdev_name << " to " << data.data(); + return false; + } + + return true; +} + } // namespace implementation } // namespace V2_0 } // namespace thermal diff --git a/thermal/utils/thermal_files.h b/thermal/utils/thermal_files.h index d2c86dd..0c0a852 100644 --- a/thermal/utils/thermal_files.h +++ b/thermal/utils/thermal_files.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef THERMAL_UTILS_THERMAL_FILES_H_ -#define THERMAL_UTILS_THERMAL_FILES_H_ +#pragma once #include #include @@ -40,6 +39,7 @@ class ThermalFiles { // data to empty and return false. If the thermal_name is found and its content // is read, this function will fill in data accordingly then return true. bool readThermalFile(std::string_view thermal_name, std::string *data) const; + bool writeCdevFile(std::string_view thermal_name, std::string_view data); size_t getNumThermalFiles() const { return thermal_name_to_path_map_.size(); } private: @@ -51,5 +51,3 @@ class ThermalFiles { } // namespace thermal } // namespace hardware } // namespace android - -#endif // THERMAL_UTILS_THERMAL_FILES_H_ diff --git a/thermal/utils/thermal_watcher.cpp b/thermal/utils/thermal_watcher.cpp index 31a89a0..9c1f96b 100644 --- a/thermal/utils/thermal_watcher.cpp +++ b/thermal/utils/thermal_watcher.cpp @@ -15,6 +15,11 @@ */ #include #include +#include +#include +#include +#include +#include #include #include #include @@ -23,8 +28,10 @@ #include #include +#include #include +#include "thermal-helper.h" #include "thermal_watcher.h" namespace android { @@ -33,27 +40,360 @@ namespace thermal { namespace V2_0 { namespace implementation { -using std::chrono_literals::operator""ms; +namespace { -void ThermalWatcher::registerFilesToWatch(const std::set &sensors_to_watch, - bool uevent_monitor) { - monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end()); - if (!uevent_monitor) { - is_polling_ = true; - return; +static int nlErrorHandle(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { + int *ret = reinterpret_cast(arg); + *ret = err->error; + LOG(ERROR) << __func__ << "nl_groups: " << nla->nl_groups << ", nl_pid: " << nla->nl_pid; + + return NL_STOP; +} + +static int nlFinishHandle(struct nl_msg *msg, void *arg) { + int *ret = reinterpret_cast(arg); + *ret = 1; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + + LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type; + + return NL_OK; +} + +static int nlAckHandle(struct nl_msg *msg, void *arg) { + int *ret = reinterpret_cast(arg); + *ret = 1; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + + LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type; + + return NL_OK; +} + +static int nlSeqCheckHandle(struct nl_msg *msg, void *arg) { + int *ret = reinterpret_cast(arg); + *ret = 1; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + + LOG(VERBOSE) << __func__ << ": nlmsg type: " << nlh->nlmsg_type; + + return NL_OK; +} + +struct HandlerArgs { + const char *group; + int id; +}; + +static int nlSendMsg(struct nl_sock *sock, struct nl_msg *msg, + int (*rx_handler)(struct nl_msg *, void *), void *data) { + int err, done = 0; + + std::unique_ptr cb(nl_cb_alloc(NL_CB_DEFAULT), nl_cb_put); + + err = nl_send_auto_complete(sock, msg); + if (err < 0) + return err; + + err = 0; + nl_cb_err(cb.get(), NL_CB_CUSTOM, nlErrorHandle, &err); + nl_cb_set(cb.get(), NL_CB_FINISH, NL_CB_CUSTOM, nlFinishHandle, &done); + nl_cb_set(cb.get(), NL_CB_ACK, NL_CB_CUSTOM, nlAckHandle, &done); + + if (rx_handler != NULL) + nl_cb_set(cb.get(), NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data); + + while (err == 0 && done == 0) nl_recvmsgs(sock, cb.get()); + + return err; +} + +static int nlFamilyHandle(struct nl_msg *msg, void *arg) { + struct HandlerArgs *grp = reinterpret_cast(arg); + struct nlattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *mcgrp; + int rem_mcgrp; + + nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[CTRL_ATTR_MCAST_GROUPS]) { + LOG(ERROR) << __func__ << "Multicast group not found"; + return -1; } + + nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) { + struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1]; + + nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX, reinterpret_cast(nla_data(mcgrp)), + nla_len(mcgrp), NULL); + + if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] || !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]) + continue; + + if (strncmp(reinterpret_cast(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])), + grp->group, nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])) != 0) + continue; + + grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]); + + break; + } + + return 0; +} + +static int nlGetMulticastId(struct nl_sock *sock, const char *family, const char *group) { + int err = 0, ctrlid; + struct HandlerArgs grp = { + .group = group, + .id = -ENOENT, + }; + + std::unique_ptr msg(nlmsg_alloc(), nlmsg_free); + + ctrlid = genl_ctrl_resolve(sock, "nlctrl"); + + genlmsg_put(msg.get(), 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0); + + nla_put_string(msg.get(), CTRL_ATTR_FAMILY_NAME, family); + + err = nlSendMsg(sock, msg.get(), nlFamilyHandle, &grp); + if (err) + return err; + + err = grp.id; + LOG(INFO) << group << " multicast_id: " << grp.id; + + return err; +} + +static bool socketAddMembership(struct nl_sock *sock, const char *group) { + int mcid = nlGetMulticastId(sock, THERMAL_GENL_FAMILY_NAME, group); + if (mcid < 0) { + LOG(ERROR) << "Failed to get multicast id: " << group; + return false; + } + + if (nl_socket_add_membership(sock, mcid)) { + LOG(ERROR) << "Failed to add netlink socket membership: " << group; + return false; + } + + LOG(INFO) << "Added netlink socket membership: " << group; + return true; +} + +static int handleEvent(struct nl_msg *n, void *arg) { + struct nlmsghdr *nlh = nlmsg_hdr(n); + struct genlmsghdr *glh = genlmsg_hdr(nlh); + struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1]; + int *tz_id = reinterpret_cast(arg); + + genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); + + if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_UP) { + LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_UP"; + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { + LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + } + if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]) + LOG(INFO) << "Thermal zone trip id: " + << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]); + } + + if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_DOWN) { + LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_DOWN"; + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { + LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + } + if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]) + LOG(INFO) << "Thermal zone trip id: " + << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]); + } + + if (glh->cmd == THERMAL_GENL_EVENT_TZ_GOV_CHANGE) { + LOG(INFO) << "THERMAL_GENL_EVENT_TZ_GOV_CHANGE"; + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { + LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + } + if (attrs[THERMAL_GENL_ATTR_GOV_NAME]) + LOG(INFO) << "Governor name: " << nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]); + } + + if (glh->cmd == THERMAL_GENL_EVENT_TZ_CREATE) { + LOG(INFO) << "THERMAL_GENL_EVENT_TZ_CREATE"; + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { + LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + } + if (attrs[THERMAL_GENL_ATTR_TZ_NAME]) + LOG(INFO) << "Thermal zone name: " << nla_get_string(attrs[THERMAL_GENL_ATTR_TZ_NAME]); + } + + if (glh->cmd == THERMAL_GENL_EVENT_TZ_DELETE) { + LOG(INFO) << "THERMAL_GENL_EVENT_TZ_DELETE"; + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { + LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + } + } + + if (glh->cmd == THERMAL_GENL_EVENT_TZ_DISABLE) { + LOG(INFO) << "THERMAL_GENL_EVENT_TZ_DISABLE"; + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { + LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + } + } + + if (glh->cmd == THERMAL_GENL_EVENT_TZ_ENABLE) { + LOG(INFO) << "THERMAL_GENL_EVENT_TZ_ENABLE"; + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { + LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + } + } + + if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_CHANGE) { + LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_CHANGE"; + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { + LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + } + if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]) + LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]); + if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]) + LOG(INFO) << "Trip type: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]); + if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]) + LOG(INFO) << "Trip temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]); + if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]) + LOG(INFO) << "Trip hyst: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]); + } + + if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_ADD) { + LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_ADD"; + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) + LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]) + LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]); + if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]) + LOG(INFO) << "Trip type: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]); + if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]) + LOG(INFO) << "Trip temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]); + if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]) + LOG(INFO) << "Trip hyst: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]); + } + + if (glh->cmd == THERMAL_GENL_EVENT_TZ_TRIP_DELETE) { + LOG(INFO) << "THERMAL_GENL_EVENT_TZ_TRIP_DELETE"; + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { + LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + } + if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]) + LOG(INFO) << "Trip id:: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]); + } + + if (glh->cmd == THERMAL_GENL_EVENT_CDEV_STATE_UPDATE) { + LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_STATE_UPDATE"; + if (attrs[THERMAL_GENL_ATTR_CDEV_ID]) + LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]); + if (attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE]) + LOG(INFO) << "Cooling device current state: " + << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE]); + } + + if (glh->cmd == THERMAL_GENL_EVENT_CDEV_ADD) { + LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_ADD"; + if (attrs[THERMAL_GENL_ATTR_CDEV_NAME]) + LOG(INFO) << "Cooling device name: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_NAME]); + if (attrs[THERMAL_GENL_ATTR_CDEV_ID]) + LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]); + if (attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE]) + LOG(INFO) << "Cooling device max state: " + << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE]); + } + + if (glh->cmd == THERMAL_GENL_EVENT_CDEV_DELETE) { + LOG(INFO) << "THERMAL_GENL_EVENT_CDEV_DELETE"; + if (attrs[THERMAL_GENL_ATTR_CDEV_ID]) + LOG(INFO) << "Cooling device id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]); + } + + if (glh->cmd == THERMAL_GENL_SAMPLING_TEMP) { + LOG(INFO) << "THERMAL_GENL_SAMPLING_TEMP"; + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) { + LOG(INFO) << "Thermal zone id: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + *tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]); + } + if (attrs[THERMAL_GENL_ATTR_TZ_TEMP]) + LOG(INFO) << "Thermal zone temp: " << nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]); + } + + return 0; +} + +} // namespace + +void ThermalWatcher::registerFilesToWatch(const std::set &sensors_to_watch) { + LOG(INFO) << "Uevent register file to watch..."; + monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end()); + uevent_fd_.reset((TEMP_FAILURE_RETRY(uevent_open_socket(64 * 1024, true)))); if (uevent_fd_.get() < 0) { LOG(ERROR) << "failed to open uevent socket"; - is_polling_ = true; return; } fcntl(uevent_fd_, F_SETFL, O_NONBLOCK); looper_->addFd(uevent_fd_.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr); - is_polling_ = false; - thermal_triggered_ = true; + sleep_ms_ = std::chrono::milliseconds(0); + last_update_time_ = boot_clock::now(); +} + +void ThermalWatcher::registerFilesToWatchNl(const std::set &sensors_to_watch) { + LOG(INFO) << "Thermal genl register file to watch..."; + monitored_sensors_.insert(sensors_to_watch.begin(), sensors_to_watch.end()); + + sk_thermal = nl_socket_alloc(); + if (!sk_thermal) { + LOG(ERROR) << "nl_socket_alloc failed"; + return; + } + + if (genl_connect(sk_thermal)) { + LOG(ERROR) << "genl_connect failed: sk_thermal"; + return; + } + + thermal_genl_fd_.reset(nl_socket_get_fd(sk_thermal)); + if (thermal_genl_fd_.get() < 0) { + LOG(ERROR) << "Failed to create thermal netlink socket"; + return; + } + + if (!socketAddMembership(sk_thermal, THERMAL_GENL_EVENT_GROUP_NAME)) { + return; + } + + /* + * Currently, only the update_temperature() will send thermal genl samlping events + * from kernel. To avoid thermal-hal busy because samlping events are sent + * too frequently, ignore thermal genl samlping events until we figure out how to use it. + * + if (!socketAddMembership(sk_thermal, THERMAL_GENL_SAMPLING_GROUP_NAME)) { + return; + } + */ + + fcntl(thermal_genl_fd_, F_SETFL, O_NONBLOCK); + looper_->addFd(thermal_genl_fd_.get(), 0, Looper::EVENT_INPUT, nullptr, nullptr); + sleep_ms_ = std::chrono::milliseconds(0); last_update_time_ = boot_clock::now(); } @@ -96,9 +436,10 @@ void ThermalWatcher::parseUevent(std::set *sensors_set) { cp = msg; while (*cp) { std::string uevent = cp; + auto findSubSystemThermal = uevent.find("SUBSYSTEM=thermal"); if (!thermal_event) { - if (uevent.find("SUBSYSTEM=") == 0) { - if (uevent.find("SUBSYSTEM=thermal") != std::string::npos) { + if (!uevent.find("SUBSYSTEM=")) { + if (findSubSystemThermal != std::string::npos) { thermal_event = true; } else { break; @@ -122,34 +463,64 @@ void ThermalWatcher::parseUevent(std::set *sensors_set) { } } +// TODO(b/175367921): Consider for potentially adding more type of event in the function +// instead of just add the sensors to the list. +void ThermalWatcher::parseGenlink(std::set *sensors_set) { + int err = 0, done = 0, tz_id = -1; + + std::unique_ptr cb(nl_cb_alloc(NL_CB_DEFAULT), nl_cb_put); + + nl_cb_err(cb.get(), NL_CB_CUSTOM, nlErrorHandle, &err); + nl_cb_set(cb.get(), NL_CB_FINISH, NL_CB_CUSTOM, nlFinishHandle, &done); + nl_cb_set(cb.get(), NL_CB_ACK, NL_CB_CUSTOM, nlAckHandle, &done); + nl_cb_set(cb.get(), NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nlSeqCheckHandle, &done); + nl_cb_set(cb.get(), NL_CB_VALID, NL_CB_CUSTOM, handleEvent, &tz_id); + + while (!done && !err) { + nl_recvmsgs(sk_thermal, cb.get()); + + if (tz_id < 0) { + break; + } + + std::string name; + if (getThermalZoneTypeById(tz_id, &name) && + std::find(monitored_sensors_.begin(), monitored_sensors_.end(), name) != + monitored_sensors_.end()) { + sensors_set->insert(name); + } + } +} + void ThermalWatcher::wake() { looper_->wake(); } bool ThermalWatcher::threadLoop() { LOG(VERBOSE) << "ThermalWatcher polling..."; - // Polling interval 2s - static constexpr int kMinPollIntervalMs = 2000; - // Max uevent timeout 5mins - static constexpr int kUeventPollTimeoutMs = 300000; + int fd; std::set sensors; auto time_elapsed_ms = std::chrono::duration_cast(boot_clock::now() - - last_update_time_) - .count(); - int timeout = (thermal_triggered_ || is_polling_) ? kMinPollIntervalMs : kUeventPollTimeoutMs; - if (time_elapsed_ms < timeout && looper_->pollOnce(timeout, &fd, nullptr, nullptr) >= 0) { - if (fd != uevent_fd_.get()) { + last_update_time_); + + if (time_elapsed_ms < sleep_ms_ && + looper_->pollOnce(sleep_ms_.count(), &fd, nullptr, nullptr) >= 0) { + if (fd != uevent_fd_.get() && fd != thermal_genl_fd_.get()) { return true; + } else if (fd == thermal_genl_fd_.get()) { + parseGenlink(&sensors); + } else if (fd == uevent_fd_.get()) { + parseUevent(&sensors); } - parseUevent(&sensors); // Ignore cb_ if uevent is not from monitored sensors if (sensors.size() == 0) { return true; } } - thermal_triggered_ = cb_(sensors); + + sleep_ms_ = cb_(sensors); last_update_time_ = boot_clock::now(); return true; } diff --git a/thermal/utils/thermal_watcher.h b/thermal/utils/thermal_watcher.h index f8cd044..7f6a44a 100644 --- a/thermal/utils/thermal_watcher.h +++ b/thermal/utils/thermal_watcher.h @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef THERMAL_UTILS_THERMAL_WATCHER_H_ -#define THERMAL_UTILS_THERMAL_WATCHER_H_ + +#pragma once #include #include @@ -40,12 +40,12 @@ namespace implementation { using android::base::boot_clock; using android::base::unique_fd; -using WatcherCallback = std::function &name)>; +using WatcherCallback = std::function &name)>; // A helper class for monitoring thermal files changes. class ThermalWatcher : public ::android::Thread { public: - ThermalWatcher(const WatcherCallback &cb) + explicit ThermalWatcher(const WatcherCallback &cb) : Thread(false), cb_(cb), looper_(new Looper(true)) {} ~ThermalWatcher() = default; @@ -58,7 +58,10 @@ class ThermalWatcher : public ::android::Thread { // Give the file watcher a list of files to start watching. This helper // class will by default wait for modifications to the file with a looper. // This should be called before starting watcher thread. - void registerFilesToWatch(const std::set &sensors_to_watch, bool uevent_monitor); + // For monitoring uevents. + void registerFilesToWatch(const std::set &sensors_to_watch); + // For monitoring thermal genl events. + void registerFilesToWatchNl(const std::set &sensors_to_watch); // Wake up the looper thus the worker thread, immediately. This can be called // in any thread. void wake(); @@ -73,6 +76,9 @@ class ThermalWatcher : public ::android::Thread { // Parse uevent message void parseUevent(std::set *sensor_name); + // Parse thermal netlink message + void parseGenlink(std::set *sensor_name); + // Maps watcher filer descriptor to watched file path. std::unordered_map watch_to_file_path_map_; @@ -86,14 +92,16 @@ class ThermalWatcher : public ::android::Thread { // For uevent socket registration. android::base::unique_fd uevent_fd_; + // For thermal genl socket registration. + android::base::unique_fd thermal_genl_fd_; // Sensor list which monitor flag is enabled. std::set monitored_sensors_; - // Flag to point out if any sensor across the first threshold. - bool thermal_triggered_; - // Flag to point out if device can support uevent notify. - bool is_polling_; + // Sleep interval voting result + std::chrono::milliseconds sleep_ms_; // Timestamp for last thermal update boot_clock::time_point last_update_time_; + // For thermal genl socket object. + struct nl_sock *sk_thermal; }; } // namespace implementation @@ -101,5 +109,3 @@ class ThermalWatcher : public ::android::Thread { } // namespace thermal } // namespace hardware } // namespace android - -#endif // THERMAL_UTILS_THERMAL_WATCHER_H_ diff --git a/thermal/xiaomi_atoll-thermal-symlinks.rc b/thermal/xiaomi_atoll-thermal-symlinks.rc new file mode 100644 index 0000000..a1f6509 --- /dev/null +++ b/thermal/xiaomi_atoll-thermal-symlinks.rc @@ -0,0 +1,11 @@ +on early-boot + mkdir /dev/thermal 0750 system system + mkdir /dev/thermal/tz-by-name 0750 system system + mkdir /dev/thermal/cdev-by-name 0750 system system + start vendor.thermal.symlinks + +service vendor.thermal.symlinks /vendor/bin/thermal_symlinks + class hal + user system + group system + oneshot