mirror of
https://github.com/PixelExperience-Devices/device_xiaomi_sm6250-common.git
synced 2025-04-29 02:27:21 +09:00
sm6250-common: Update thermal HAL
* From hardware/google/pixel at d774cbb949e98627e4172bf8fc11e8d954599aa7. Change-Id: I3a3a0c29575d0595e71a30f1e64e33ca34d2eb27
This commit is contained in:
parent
27f575c973
commit
7f99605ccc
3
atoll.mk
3
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
|
||||
|
2
sepolicy/vendor/file_contexts
vendored
2
sepolicy/vendor/file_contexts
vendored
@ -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
|
||||
|
12
sepolicy/vendor/init-thermal-symlinks.sh.te
vendored
Normal file
12
sepolicy/vendor/init-thermal-symlinks.sh.te
vendored
Normal file
@ -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)
|
@ -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",
|
||||
],
|
||||
}
|
||||
|
@ -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 <typename T, typename U>
|
||||
Return<void> setFailureAndCallback(T _hidl_cb, hidl_vec<U> data, std::string_view debug_msg) {
|
||||
@ -234,16 +233,15 @@ Return<void> Thermal::unregisterThermalChangedCallback(
|
||||
return Void();
|
||||
}
|
||||
|
||||
void Thermal::sendThermalChangedCallback(const std::vector<Temperature_2_0> &temps) {
|
||||
void Thermal::sendThermalChangedCallback(const Temperature_2_0 &t) {
|
||||
std::lock_guard<std::mutex> _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<Temperature_2_0> &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<void> Thermal::debug(const hidl_handle &handle, const hidl_vec<hidl_strin
|
||||
}
|
||||
}
|
||||
{
|
||||
dump_buf << "Monitor:" << std::endl;
|
||||
dump_buf << "SendCallback" << std::endl;
|
||||
dump_buf << " Enabled List: ";
|
||||
const auto &map = thermal_helper_.GetSensorInfoMap();
|
||||
for (const auto &name_info_pair : map) {
|
||||
dump_buf << " Name: " << name_info_pair.first;
|
||||
dump_buf << " Monitor: " << std::boolalpha << name_info_pair.second.is_monitor
|
||||
<< std::noboolalpha << std::endl;
|
||||
if (name_info_pair.second.send_cb) {
|
||||
dump_buf << name_info_pair.first << " ";
|
||||
}
|
||||
}
|
||||
dump_buf << std::endl;
|
||||
}
|
||||
{
|
||||
dump_buf << "SendPowerHint:" << std::endl;
|
||||
dump_buf << "SendPowerHint" << std::endl;
|
||||
dump_buf << " Enabled List: ";
|
||||
const auto &map = thermal_helper_.GetSensorInfoMap();
|
||||
for (const auto &name_info_pair : map) {
|
||||
dump_buf << " Name: " << name_info_pair.first;
|
||||
dump_buf << " SendPowerHint: " << std::boolalpha
|
||||
<< name_info_pair.second.send_powerhint << std::noboolalpha
|
||||
<< std::endl;
|
||||
if (name_info_pair.second.send_powerhint) {
|
||||
dump_buf << name_info_pair.first << " ";
|
||||
}
|
||||
}
|
||||
dump_buf << std::endl;
|
||||
}
|
||||
dumpVirtualSensorInfo(&dump_buf);
|
||||
dumpThrottlingInfo(&dump_buf);
|
||||
dumpThrottlingRequestStatus(&dump_buf);
|
||||
dumpPowerRailInfo(&dump_buf);
|
||||
{
|
||||
dump_buf << "AIDL Power Hal exist: " << std::boolalpha
|
||||
<< thermal_helper_.isAidlPowerHalExist() << std::endl;
|
||||
|
@ -13,8 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
|
||||
#define ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
@ -40,7 +40,7 @@ using ::android::hardware::thermal::V2_0::IThermalChangedCallback;
|
||||
struct CallbackSetting {
|
||||
CallbackSetting(sp<IThermalChangedCallback> 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<IThermalChangedCallback> callback;
|
||||
bool is_filter_type;
|
||||
TemperatureType_2_0 type;
|
||||
@ -65,9 +65,9 @@ class Thermal : public IThermal {
|
||||
getCurrentTemperatures_cb _hidl_cb) override;
|
||||
Return<void> getTemperatureThresholds(bool filterType, TemperatureType_2_0 type,
|
||||
getTemperatureThresholds_cb _hidl_cb) override;
|
||||
Return<void> registerThermalChangedCallback(const sp<IThermalChangedCallback> &callback,
|
||||
bool filterType, TemperatureType_2_0 type,
|
||||
registerThermalChangedCallback_cb _hidl_cb) override;
|
||||
Return<void> registerThermalChangedCallback(
|
||||
const sp<IThermalChangedCallback> &callback, bool filterType, TemperatureType_2_0 type,
|
||||
registerThermalChangedCallback_cb _hidl_cb) override;
|
||||
Return<void> unregisterThermalChangedCallback(
|
||||
const sp<IThermalChangedCallback> &callback,
|
||||
unregisterThermalChangedCallback_cb _hidl_cb) override;
|
||||
@ -78,10 +78,14 @@ class Thermal : public IThermal {
|
||||
Return<void> debug(const hidl_handle &fd, const hidl_vec<hidl_string> &args) override;
|
||||
|
||||
// Helper function for calling callbacks
|
||||
void sendThermalChangedCallback(const std::vector<Temperature_2_0> &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<CallbackSetting> callbacks_;
|
||||
};
|
||||
@ -91,5 +95,3 @@ class Thermal : public IThermal {
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_THERMAL_V2_0_CROSSHATCH_THERMAL_H
|
||||
|
13
thermal/init.thermal.symlinks.sh
Executable file
13
thermal/init.thermal.symlinks.sh
Executable file
@ -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
|
@ -17,6 +17,8 @@
|
||||
#include <hidl/HidlTransportSupport.h>
|
||||
#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<IThermal> service = nullptr;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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 <array>
|
||||
#include <chrono>
|
||||
@ -45,6 +44,7 @@
|
||||
#include <android/hardware/thermal/2.0/IThermal.h>
|
||||
|
||||
#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<void(const std::vector<Temperature_2_0> &temps)>;
|
||||
using NotificationCallback = std::function<void(const Temperature_2_0 &t)>;
|
||||
using NotificationTime = std::chrono::time_point<std::chrono::steady_clock>;
|
||||
using CdevRequestStatus = std::unordered_map<std::string, int>;
|
||||
|
||||
// Get thermal_zone type
|
||||
bool getThermalZoneTypeById(int tz_id, std::string *);
|
||||
|
||||
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<std::string, int> pid_request_map;
|
||||
std::unordered_map<std::string, int> 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<Temperature_1_0> *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<ThrottlingSeverity, ThrottlingSeverity> *throtting_status = nullptr) const;
|
||||
std::pair<ThrottlingSeverity, ThrottlingSeverity> *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<std::string, SensorInfo> &GetSensorInfoMap() const { return sensor_info_map_; }
|
||||
const std::unordered_map<std::string, SensorInfo> &GetSensorInfoMap() const {
|
||||
return sensor_info_map_;
|
||||
}
|
||||
// Get CdevInfo Map
|
||||
const std::unordered_map<std::string, CdevInfo> &GetCdevInfoMap() const {
|
||||
return cooling_device_info_map_;
|
||||
}
|
||||
// Get PowerRailInfo Map
|
||||
const std::unordered_map<std::string, PowerRailInfo> &GetPowerRailInfoMap() const {
|
||||
return power_rail_info_map_;
|
||||
}
|
||||
// Get SensorStatus Map
|
||||
const std::unordered_map<std::string, SensorStatus> &GetSensorStatusMap() const {
|
||||
std::shared_lock<std::shared_mutex> _lock(sensor_status_map_mutex_);
|
||||
return sensor_status_map_;
|
||||
}
|
||||
// Get CdevStatus Map
|
||||
const std::unordered_map<std::string, CdevRequestStatus> &GetCdevStatusMap() const {
|
||||
std::shared_lock<std::shared_mutex> _lock(cdev_status_map_mutex_);
|
||||
return cdev_status_map_;
|
||||
}
|
||||
// Get ThrottlingRelease Map
|
||||
const std::unordered_map<std::string, CdevReleaseStatus> &GetThrottlingReleaseMap() const {
|
||||
return power_files_.GetThrottlingReleaseMap();
|
||||
}
|
||||
|
||||
// Get PowerStatus Map
|
||||
const std::unordered_map<std::string, PowerStatusMap> &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<std::string, std::string> &path_map);
|
||||
bool initializeCoolingDevices(const std::map<std::string, std::string> &path_map);
|
||||
bool initializeTrip(const std::map<std::string, std::string> &path_map);
|
||||
bool initializeSensorMap(const std::unordered_map<std::string, std::string> &path_map);
|
||||
bool initializeCoolingDevices(const std::unordered_map<std::string, std::string> &path_map);
|
||||
void setMinTimeout(SensorInfo *sensor_info);
|
||||
void initializeTrip(const std::unordered_map<std::string, std::string> &path_map,
|
||||
std::set<std::string> *monitored_sensors, bool thermal_genl_enabled);
|
||||
|
||||
// For thermal_watcher_'s polling thread
|
||||
bool thermalWatcherCallbackFunc(const std::set<std::string> &uevent_sensors);
|
||||
// For thermal_watcher_'s polling thread, return the sleep interval
|
||||
std::chrono::milliseconds thermalWatcherCallbackFunc(
|
||||
const std::set<std::string> &uevent_sensors);
|
||||
// Return hot and cold severity status as std::pair
|
||||
std::pair<ThrottlingSeverity, ThrottlingSeverity> getSeverityFromThresholds(
|
||||
const ThrottlingArray &hot_thresholds, const ThrottlingArray &cold_thresholds,
|
||||
const ThrottlingArray &hot_hysteresis, const ThrottlingArray &cold_hysteresis,
|
||||
ThrottlingSeverity prev_hot_severity, ThrottlingSeverity prev_cold_severity,
|
||||
float value) const;
|
||||
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<std::string> *cooling_devices_to_update);
|
||||
void updateCoolingDevices(const std::vector<std::string> &cooling_devices_to_update);
|
||||
sp<ThermalWatcher> thermal_watcher_;
|
||||
PowerFiles power_files_;
|
||||
ThermalFiles thermal_sensors_;
|
||||
ThermalFiles cooling_devices_;
|
||||
bool is_initialized_;
|
||||
const NotificationCallback cb_;
|
||||
const std::map<std::string, CoolingType> cooling_device_info_map_;
|
||||
const std::map<std::string, SensorInfo> sensor_info_map_;
|
||||
std::map<std::string, std::map<ThrottlingSeverity, ThrottlingSeverity>>
|
||||
std::unordered_map<std::string, CdevInfo> cooling_device_info_map_;
|
||||
std::unordered_map<std::string, SensorInfo> sensor_info_map_;
|
||||
std::unordered_map<std::string, PowerRailInfo> power_rail_info_map_;
|
||||
std::unordered_map<std::string, std::map<ThrottlingSeverity, ThrottlingSeverity>>
|
||||
supported_powerhint_map_;
|
||||
PowerHalService power_hal_service_;
|
||||
|
||||
mutable std::shared_mutex sensor_status_map_mutex_;
|
||||
std::map<std::string, SensorStatus> sensor_status_map_;
|
||||
std::unordered_map<std::string, SensorStatus> sensor_status_map_;
|
||||
mutable std::shared_mutex cdev_status_map_mutex_;
|
||||
std::unordered_map<std::string, CdevRequestStatus> cdev_status_map_;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
@ -171,5 +232,3 @@ class ThermalHelper {
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // THERMAL_THERMAL_HELPER_H__
|
||||
|
@ -15,9 +15,10 @@
|
||||
*/
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <cmath>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <json/reader.h>
|
||||
#include <json/value.h>
|
||||
@ -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<int>::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<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path) {
|
||||
std::unordered_map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path) {
|
||||
std::string json_doc;
|
||||
std::map<std::string, SensorInfo> sensors_parsed;
|
||||
std::unordered_map<std::string, SensorInfo> 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<Json::CharReader> 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<std::string> sensors_name_parsed;
|
||||
std::unordered_set<std::string> 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<std::string, SensorInfo> 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<float, kThrottlingSeverityCount> hot_thresholds;
|
||||
hot_thresholds.fill(NAN);
|
||||
std::array<float, kThrottlingSeverityCount> cold_thresholds;
|
||||
@ -115,7 +212,18 @@ std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path)
|
||||
hot_hysteresis.fill(0.0);
|
||||
std::array<float, kThrottlingSeverityCount> cold_hysteresis;
|
||||
cold_hysteresis.fill(0.0);
|
||||
std::vector<std::string> linked_sensors;
|
||||
std::vector<float> 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<std::string, SensorInfo> 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<std::string, SensorInfo> 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<float, kThrottlingSeverityCount> k_po;
|
||||
k_po.fill(0.0);
|
||||
std::array<float, kThrottlingSeverityCount> k_pu;
|
||||
k_pu.fill(0.0);
|
||||
std::array<float, kThrottlingSeverityCount> k_i;
|
||||
k_i.fill(0.0);
|
||||
std::array<float, kThrottlingSeverityCount> k_d;
|
||||
k_d.fill(0.0);
|
||||
std::array<float, kThrottlingSeverityCount> i_max;
|
||||
i_max.fill(NAN);
|
||||
std::array<float, kThrottlingSeverityCount> max_alloc_power;
|
||||
max_alloc_power.fill(NAN);
|
||||
std::array<float, kThrottlingSeverityCount> min_alloc_power;
|
||||
min_alloc_power.fill(NAN);
|
||||
std::array<float, kThrottlingSeverityCount> s_power;
|
||||
s_power.fill(NAN);
|
||||
std::array<float, kThrottlingSeverityCount> 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<std::string, BindedCdevInfo> 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<int>::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<VirtualSensorInfo> virtual_sensor_info;
|
||||
if (is_virtual_sensor) {
|
||||
virtual_sensor_info.reset(new VirtualSensorInfo{linked_sensors, coefficients, offset,
|
||||
trigger_sensor, formula});
|
||||
}
|
||||
|
||||
std::unique_ptr<ThrottlingInfo> throttling_info(
|
||||
new ThrottlingInfo{k_po, k_pu, k_i, k_d, i_max, max_alloc_power, min_alloc_power,
|
||||
s_power, i_cutoff, binded_cdev_info_map});
|
||||
|
||||
sensors_parsed[name] = {
|
||||
.type = sensor_type,
|
||||
@ -232,11 +661,18 @@ std::map<std::string, SensorInfo> 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<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path)
|
||||
return sensors_parsed;
|
||||
}
|
||||
|
||||
std::map<std::string, CoolingType> ParseCoolingDevice(std::string_view config_path) {
|
||||
std::unordered_map<std::string, CdevInfo> ParseCoolingDevice(std::string_view config_path) {
|
||||
std::string json_doc;
|
||||
std::map<std::string, CoolingType> cooling_devices_parsed;
|
||||
std::unordered_map<std::string, CdevInfo> 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<Json::CharReader> 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<std::string> cooling_devices_name_parsed;
|
||||
std::unordered_set<std::string> 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<std::string, CoolingType> 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<float> 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<std::string, CoolingType> ParseCoolingDevice(std::string_view config_pa
|
||||
return cooling_devices_parsed;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, PowerRailInfo> ParsePowerRailInfo(std::string_view config_path) {
|
||||
std::string json_doc;
|
||||
std::unordered_map<std::string, PowerRailInfo> 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<Json::CharReader> 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<std::string> 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<std::string> linked_power_rails;
|
||||
std::vector<float> 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<VirtualPowerRailInfo> 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
|
||||
|
@ -14,11 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef THERMAL_UTILS_CONFIG_PARSER_H__
|
||||
#define THERMAL_UTILS_CONFIG_PARSER_H__
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <android/hardware/thermal/2.0/IThermal.h>
|
||||
|
||||
@ -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<ThrottlingSeverity>().begin(), hidl_enum_range<ThrottlingSeverity>().end());
|
||||
using ThrottlingArray = std::array<float, static_cast<size_t>(kThrottlingSeverityCount)>;
|
||||
using CdevArray = std::array<int, static_cast<size_t>(kThrottlingSeverityCount)>;
|
||||
constexpr std::chrono::milliseconds kMinPollIntervalMs = std::chrono::milliseconds(2000);
|
||||
constexpr std::chrono::milliseconds kUeventPollTimeoutMs = std::chrono::milliseconds(300000);
|
||||
|
||||
enum FormulaOption : uint32_t {
|
||||
COUNT_THRESHOLD = 0,
|
||||
WEIGHTED_AVG,
|
||||
MAXIMUM,
|
||||
MINIMUM,
|
||||
};
|
||||
|
||||
struct VirtualSensorInfo {
|
||||
std::vector<std::string> linked_sensors;
|
||||
std::vector<float> coefficients;
|
||||
float offset;
|
||||
std::string trigger_sensor;
|
||||
FormulaOption formula;
|
||||
};
|
||||
|
||||
struct VirtualPowerRailInfo {
|
||||
std::vector<std::string> linked_power_rails;
|
||||
std::vector<float> 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<std::string, BindedCdevInfo> 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<VirtualSensorInfo> virtual_sensor_info;
|
||||
std::unique_ptr<ThrottlingInfo> throttling_info;
|
||||
};
|
||||
|
||||
std::map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path);
|
||||
std::map<std::string, CoolingType> ParseCoolingDevice(std::string_view config_path);
|
||||
struct CdevInfo {
|
||||
CoolingType_2_0 type;
|
||||
std::string read_path;
|
||||
std::string write_path;
|
||||
std::vector<float> state2power;
|
||||
int max_state;
|
||||
};
|
||||
struct PowerRailInfo {
|
||||
std::string rail;
|
||||
int power_sample_count;
|
||||
std::chrono::milliseconds power_sample_delay;
|
||||
std::unique_ptr<VirtualPowerRailInfo> virtual_power_rail_info;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, SensorInfo> ParseSensorInfo(std::string_view config_path);
|
||||
std::unordered_map<std::string, CdevInfo> ParseCoolingDevice(std::string_view config_path);
|
||||
std::unordered_map<std::string, PowerRailInfo> ParsePowerRailInfo(std::string_view config_path);
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V2_0
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // THERMAL_UTILS_CONFIG_PARSER_H__
|
||||
|
444
thermal/utils/power_files.cpp
Normal file
444
thermal/utils/power_files.cpp
Normal file
@ -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 <dirent.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#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<std::shared_mutex> _lock(throttling_release_map_mutex_);
|
||||
if (!throttling_release_map_.count(sensor_name.data()) ||
|
||||
!power_status_map_.count(sensor_name.data())) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &cdev_release_map = throttling_release_map_.at(sensor_name.data());
|
||||
PowerSample power_sample = {};
|
||||
|
||||
for (auto &power_status_pair : power_status_map_.at(sensor_name.data())) {
|
||||
for (size_t i = 0; i < power_status_pair.second.power_history.size(); ++i) {
|
||||
for (size_t j = 0; j < power_status_pair.second.power_history[i].size(); ++j) {
|
||||
power_status_pair.second.power_history[i].pop();
|
||||
power_status_pair.second.power_history[i].emplace(power_sample);
|
||||
}
|
||||
}
|
||||
power_status_pair.second.last_updated_avg_power = NAN;
|
||||
}
|
||||
|
||||
for (auto &cdev_release_pair : cdev_release_map) {
|
||||
cdev_release_pair.second.release_step = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int PowerFiles::getReleaseStep(std::string_view sensor_name, std::string_view cdev_name) {
|
||||
int release_step = 0;
|
||||
std::shared_lock<std::shared_mutex> _lock(throttling_release_map_mutex_);
|
||||
|
||||
if (throttling_release_map_.count(sensor_name.data()) &&
|
||||
throttling_release_map_[sensor_name.data()].count(cdev_name.data())) {
|
||||
release_step = throttling_release_map_[sensor_name.data()][cdev_name.data()].release_step;
|
||||
}
|
||||
|
||||
return release_step;
|
||||
}
|
||||
|
||||
bool PowerFiles::registerPowerRailsToWatch(std::string_view sensor_name, std::string_view cdev_name,
|
||||
const BindedCdevInfo &binded_cdev_info,
|
||||
const CdevInfo &cdev_info,
|
||||
const PowerRailInfo &power_rail_info) {
|
||||
std::vector<std::queue<PowerSample>> power_history;
|
||||
PowerSample power_sample = {
|
||||
.energy_counter = 0,
|
||||
.duration = 0,
|
||||
};
|
||||
|
||||
if (throttling_release_map_.count(sensor_name.data()) &&
|
||||
throttling_release_map_[sensor_name.data()].count(binded_cdev_info.power_rail)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!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<PowerSample>());
|
||||
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<PowerSample>());
|
||||
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, decltype(&closedir)> 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<PowerSample> *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<float>(deltaEnergy) / static_cast<float>(duration);
|
||||
LOG(VERBOSE) << "Power rail " << power_rail.data() << ", avg power = " << *avg_power
|
||||
<< ", duration = " << duration << ", deltaEnergy = " << deltaEnergy;
|
||||
}
|
||||
|
||||
if (power_sample_update) {
|
||||
power_history->pop();
|
||||
power_history->push(curr_sample);
|
||||
}
|
||||
|
||||
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<float>::lowest();
|
||||
if (avg_power_number * coefficient > avg_power_val)
|
||||
avg_power_val = avg_power_number * coefficient;
|
||||
break;
|
||||
case FormulaOption::MINIMUM:
|
||||
if (i == 0)
|
||||
avg_power_val = std::numeric_limits<float>::max();
|
||||
if (avg_power_number * coefficient < avg_power_val)
|
||||
avg_power_val = avg_power_number * coefficient;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (avg_power_val >= 0) {
|
||||
avg_power_val = avg_power_val + offset;
|
||||
}
|
||||
|
||||
*avg_power = avg_power_val;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool PowerFiles::throttlingReleaseUpdate(std::string_view sensor_name, std::string_view cdev_name,
|
||||
const ThrottlingSeverity severity,
|
||||
const std::chrono::milliseconds time_elapsed_ms,
|
||||
const BindedCdevInfo &binded_cdev_info,
|
||||
const PowerRailInfo &power_rail_info,
|
||||
bool power_sample_update, bool severity_changed) {
|
||||
std::unique_lock<std::shared_mutex> _lock(throttling_release_map_mutex_);
|
||||
float avg_power = -1;
|
||||
|
||||
if (!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<int>(severity)]) {
|
||||
is_over_budget = false;
|
||||
}
|
||||
} else {
|
||||
if (avg_power > binded_cdev_info.power_thresholds[static_cast<int>(severity)]) {
|
||||
is_over_budget = false;
|
||||
}
|
||||
}
|
||||
LOG(INFO) << "Power rail " << binded_cdev_info.power_rail << ": power threshold = "
|
||||
<< binded_cdev_info.power_thresholds[static_cast<int>(severity)]
|
||||
<< ", avg power = " << avg_power;
|
||||
|
||||
switch (binded_cdev_info.release_logic) {
|
||||
case ReleaseLogic::INCREASE:
|
||||
if (!is_over_budget) {
|
||||
if (std::abs(release_status.release_step) <
|
||||
static_cast<int>(release_status.max_release_step)) {
|
||||
release_status.release_step--;
|
||||
}
|
||||
} else {
|
||||
release_status.release_step = 0;
|
||||
}
|
||||
break;
|
||||
case ReleaseLogic::DECREASE:
|
||||
if (!is_over_budget) {
|
||||
if (release_status.release_step <
|
||||
static_cast<int>(release_status.max_release_step)) {
|
||||
release_status.release_step++;
|
||||
}
|
||||
} else {
|
||||
release_status.release_step = 0;
|
||||
}
|
||||
break;
|
||||
case ReleaseLogic::STEPWISE:
|
||||
if (!is_over_budget) {
|
||||
if (release_status.release_step <
|
||||
static_cast<int>(release_status.max_release_step)) {
|
||||
release_status.release_step++;
|
||||
}
|
||||
} else {
|
||||
if (std::abs(release_status.release_step) <
|
||||
static_cast<int>(release_status.max_release_step)) {
|
||||
release_status.release_step--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ReleaseLogic::RELEASE_TO_FLOOR:
|
||||
release_status.release_step = is_over_budget ? 0 : release_status.max_release_step;
|
||||
break;
|
||||
case ReleaseLogic::NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V2_0
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
127
thermal/utils/power_files.h
Normal file
127
thermal/utils/power_files.h
Normal file
@ -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 <queue>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#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<std::queue<PowerSample>> power_history;
|
||||
float last_updated_avg_power;
|
||||
};
|
||||
|
||||
using CdevReleaseStatus = std::unordered_map<std::string, ReleaseStatus>;
|
||||
using PowerStatusMap = std::unordered_map<std::string, PowerStatus>;
|
||||
|
||||
// A helper class for monitoring power rails.
|
||||
class PowerFiles {
|
||||
public:
|
||||
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<PowerSample> *power_history,
|
||||
bool power_sample_update, float *avg_power);
|
||||
bool computeAveragePower(const PowerRailInfo &power_rail_info, PowerStatus *power_status,
|
||||
bool power_sample_update, float *avg_power);
|
||||
|
||||
// Update the throttling release status according to the average power, return true if power
|
||||
// rail is updated.
|
||||
bool throttlingReleaseUpdate(std::string_view sensor_name, std::string_view cdev_name,
|
||||
const ThrottlingSeverity severity,
|
||||
const std::chrono::milliseconds time_elapsed_ms,
|
||||
const BindedCdevInfo &binded_cdev_info,
|
||||
const PowerRailInfo &power_rail_info, bool power_sample_update,
|
||||
bool severity_changed);
|
||||
|
||||
// Get the throttling release status for the targer power rail which is binded in specific
|
||||
// sensor.
|
||||
int getReleaseStep(std::string_view sensor_name, std::string_view cdev_name);
|
||||
|
||||
// Clear the data of throttling_release_map_.
|
||||
void setPowerDataToDefault(std::string_view sensor_name);
|
||||
|
||||
// Get throttling release status map
|
||||
const std::unordered_map<std::string, CdevReleaseStatus> &GetThrottlingReleaseMap() const {
|
||||
std::shared_lock<std::shared_mutex> _lock(throttling_release_map_mutex_);
|
||||
return throttling_release_map_;
|
||||
}
|
||||
|
||||
// Get Power status map
|
||||
const std::unordered_map<std::string, PowerStatusMap> &GetPowerStatusMap() const {
|
||||
std::shared_lock<std::shared_mutex> _lock(power_status_map_mutex_);
|
||||
return power_status_map_;
|
||||
}
|
||||
|
||||
private:
|
||||
// The map to record the energy info for each power rail.
|
||||
std::unordered_map<std::string, PowerSample> energy_info_map_;
|
||||
// The map to record the throttling release status for each thermal sensor.
|
||||
std::unordered_map<std::string, CdevReleaseStatus> throttling_release_map_;
|
||||
mutable std::shared_mutex throttling_release_map_mutex_;
|
||||
// The map to record the power data for each thermal sensor.
|
||||
std::unordered_map<std::string, PowerStatusMap> power_status_map_;
|
||||
mutable std::shared_mutex power_status_map_mutex_;
|
||||
// The set to store the energy source paths
|
||||
std::unordered_set<std::string> energy_path_set_;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V2_0
|
||||
} // namespace thermal
|
||||
} // namespace hardware
|
||||
} // namespace android
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
#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
|
||||
|
@ -14,8 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef THERMAL_UTILS_THERMAL_FILES_H_
|
||||
#define THERMAL_UTILS_THERMAL_FILES_H_
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@ -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_
|
||||
|
@ -15,6 +15,11 @@
|
||||
*/
|
||||
#include <cutils/uevent.h>
|
||||
#include <dirent.h>
|
||||
#include <linux/genetlink.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <netlink/genl/ctrl.h>
|
||||
#include <netlink/genl/genl.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/types.h>
|
||||
@ -23,8 +28,10 @@
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#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<std::string> &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<int *>(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<int *>(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<int *>(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<int *>(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<nl_cb, decltype(&nl_cb_put)> 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<struct HandlerArgs *>(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<nlattr *>(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<char *>(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<nl_msg, decltype(&nlmsg_free)> 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<int *>(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<std::string> &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<std::string> &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<std::string> *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<std::string> *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<std::string> *sensors_set) {
|
||||
int err = 0, done = 0, tz_id = -1;
|
||||
|
||||
std::unique_ptr<nl_cb, decltype(&nl_cb_put)> 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<std::string> sensors;
|
||||
|
||||
auto time_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(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;
|
||||
}
|
||||
|
@ -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 <chrono>
|
||||
#include <condition_variable>
|
||||
@ -40,12 +40,12 @@ namespace implementation {
|
||||
|
||||
using android::base::boot_clock;
|
||||
using android::base::unique_fd;
|
||||
using WatcherCallback = std::function<bool(const std::set<std::string> &name)>;
|
||||
using WatcherCallback = std::function<std::chrono::milliseconds(const std::set<std::string> &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<std::string> &sensors_to_watch, bool uevent_monitor);
|
||||
// For monitoring uevents.
|
||||
void registerFilesToWatch(const std::set<std::string> &sensors_to_watch);
|
||||
// For monitoring thermal genl events.
|
||||
void registerFilesToWatchNl(const std::set<std::string> &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<std::string> *sensor_name);
|
||||
|
||||
// Parse thermal netlink message
|
||||
void parseGenlink(std::set<std::string> *sensor_name);
|
||||
|
||||
// Maps watcher filer descriptor to watched file path.
|
||||
std::unordered_map<int, std::string> 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<std::string> 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_
|
||||
|
11
thermal/xiaomi_atoll-thermal-symlinks.rc
Normal file
11
thermal/xiaomi_atoll-thermal-symlinks.rc
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user