1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "thermal_throttling.h"
18
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21 #include <android-base/properties.h>
22 #include <android-base/stringprintf.h>
23 #include <android-base/strings.h>
24 #include <utils/Trace.h>
25
26 #include <iterator>
27 #include <set>
28 #include <sstream>
29 #include <thread>
30 #include <vector>
31
32 namespace android {
33 namespace hardware {
34 namespace thermal {
35 namespace V2_0 {
36 namespace implementation {
37
38 // To find the next PID target state according to the current thermal severity
getTargetStateOfPID(const SensorInfo & sensor_info,const ThrottlingSeverity curr_severity)39 size_t getTargetStateOfPID(const SensorInfo &sensor_info, const ThrottlingSeverity curr_severity) {
40 size_t target_state = 0;
41
42 for (const auto &severity : hidl_enum_range<ThrottlingSeverity>()) {
43 size_t state = static_cast<size_t>(severity);
44 if (std::isnan(sensor_info.throttling_info->s_power[state])) {
45 continue;
46 }
47 target_state = state;
48 if (severity > curr_severity) {
49 break;
50 }
51 }
52 LOG(VERBOSE) << "PID target state = " << target_state;
53 return target_state;
54 }
55
clearThrottlingData(std::string_view sensor_name,const SensorInfo & sensor_info)56 void ThermalThrottling::clearThrottlingData(std::string_view sensor_name,
57 const SensorInfo &sensor_info) {
58 if (!thermal_throttling_status_map_.count(sensor_name.data())) {
59 return;
60 }
61 std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_);
62
63 for (auto &pid_power_budget_pair :
64 thermal_throttling_status_map_.at(sensor_name.data()).pid_power_budget_map) {
65 pid_power_budget_pair.second = std::numeric_limits<int>::max();
66 }
67
68 for (auto &pid_cdev_request_pair :
69 thermal_throttling_status_map_.at(sensor_name.data()).pid_cdev_request_map) {
70 pid_cdev_request_pair.second = 0;
71 }
72
73 for (auto &hardlimit_cdev_request_pair :
74 thermal_throttling_status_map_.at(sensor_name.data()).hardlimit_cdev_request_map) {
75 hardlimit_cdev_request_pair.second = 0;
76 }
77
78 for (auto &throttling_release_pair :
79 thermal_throttling_status_map_.at(sensor_name.data()).throttling_release_map) {
80 throttling_release_pair.second = 0;
81 }
82
83 thermal_throttling_status_map_[sensor_name.data()].err_integral =
84 sensor_info.throttling_info->err_integral_default;
85 thermal_throttling_status_map_[sensor_name.data()].prev_err = NAN;
86 return;
87 }
88
registerThermalThrottling(std::string_view sensor_name,const std::shared_ptr<ThrottlingInfo> & throttling_info,const std::unordered_map<std::string,CdevInfo> & cooling_device_info_map)89 bool ThermalThrottling::registerThermalThrottling(
90 std::string_view sensor_name, const std::shared_ptr<ThrottlingInfo> &throttling_info,
91 const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) {
92 if (thermal_throttling_status_map_.count(sensor_name.data())) {
93 LOG(ERROR) << "Sensor " << sensor_name.data() << " throttling map has been registered";
94 return false;
95 }
96
97 if (throttling_info == nullptr) {
98 LOG(ERROR) << "Sensor " << sensor_name.data() << " has no throttling info";
99 return false;
100 }
101 thermal_throttling_status_map_[sensor_name.data()].err_integral =
102 throttling_info->err_integral_default;
103 thermal_throttling_status_map_[sensor_name.data()].prev_err = NAN;
104
105 for (auto &binded_cdev_pair : throttling_info->binded_cdev_info_map) {
106 if (!cooling_device_info_map.count(binded_cdev_pair.first)) {
107 LOG(ERROR) << "Could not find " << sensor_name.data() << "'s binded CDEV "
108 << binded_cdev_pair.first;
109 return false;
110 }
111 // Register PID throttling map
112 for (const auto &cdev_weight : binded_cdev_pair.second.cdev_weight_for_pid) {
113 if (!std::isnan(cdev_weight)) {
114 thermal_throttling_status_map_[sensor_name.data()]
115 .pid_power_budget_map[binded_cdev_pair.first] =
116 std::numeric_limits<int>::max();
117 thermal_throttling_status_map_[sensor_name.data()]
118 .pid_cdev_request_map[binded_cdev_pair.first] = 0;
119 thermal_throttling_status_map_[sensor_name.data()]
120 .cdev_status_map[binded_cdev_pair.first] = 0;
121 break;
122 }
123 }
124 // Register hard limit throttling map
125 for (const auto &limit_info : binded_cdev_pair.second.limit_info) {
126 if (limit_info > 0) {
127 thermal_throttling_status_map_[sensor_name.data()]
128 .hardlimit_cdev_request_map[binded_cdev_pair.first] = 0;
129 thermal_throttling_status_map_[sensor_name.data()]
130 .cdev_status_map[binded_cdev_pair.first] = 0;
131 break;
132 }
133 }
134 // Register throttling release map if power threshold is exist
135 if (!binded_cdev_pair.second.power_rail.empty()) {
136 for (const auto &power_threshold : binded_cdev_pair.second.power_thresholds) {
137 if (!std::isnan(power_threshold)) {
138 thermal_throttling_status_map_[sensor_name.data()]
139 .throttling_release_map[binded_cdev_pair.first] = 0;
140 break;
141 }
142 }
143 }
144 }
145 return true;
146 }
147
148 // return power budget based on PID algo
updatePowerBudget(const Temperature_2_0 & temp,const SensorInfo & sensor_info,std::chrono::milliseconds time_elapsed_ms,ThrottlingSeverity curr_severity)149 float ThermalThrottling::updatePowerBudget(const Temperature_2_0 &temp,
150 const SensorInfo &sensor_info,
151 std::chrono::milliseconds time_elapsed_ms,
152 ThrottlingSeverity curr_severity) {
153 float p = 0, i = 0, d = 0;
154 float power_budget = std::numeric_limits<float>::max();
155
156 if (curr_severity == ThrottlingSeverity::NONE) {
157 return power_budget;
158 }
159
160 const auto target_state = getTargetStateOfPID(sensor_info, curr_severity);
161
162 // Compute PID
163 float err = sensor_info.hot_thresholds[target_state] - temp.value;
164 p = err * (err < 0 ? sensor_info.throttling_info->k_po[target_state]
165 : sensor_info.throttling_info->k_pu[target_state]);
166 i = thermal_throttling_status_map_[temp.name].err_integral *
167 sensor_info.throttling_info->k_i[target_state];
168 if (err < sensor_info.throttling_info->i_cutoff[target_state]) {
169 float i_next = i + err * sensor_info.throttling_info->k_i[target_state];
170 if (abs(i_next) < sensor_info.throttling_info->i_max[target_state]) {
171 i = i_next;
172 thermal_throttling_status_map_[temp.name].err_integral += err;
173 }
174 }
175
176 if (!std::isnan(thermal_throttling_status_map_[temp.name].prev_err) &&
177 time_elapsed_ms != std::chrono::milliseconds::zero()) {
178 d = sensor_info.throttling_info->k_d[target_state] *
179 (err - thermal_throttling_status_map_[temp.name].prev_err) / time_elapsed_ms.count();
180 }
181
182 thermal_throttling_status_map_[temp.name].prev_err = err;
183 // Calculate power budget
184 power_budget = sensor_info.throttling_info->s_power[target_state] + p + i + d;
185 if (power_budget < sensor_info.throttling_info->min_alloc_power[target_state]) {
186 power_budget = sensor_info.throttling_info->min_alloc_power[target_state];
187 }
188 if (power_budget > sensor_info.throttling_info->max_alloc_power[target_state]) {
189 power_budget = sensor_info.throttling_info->max_alloc_power[target_state];
190 }
191
192 LOG(INFO) << temp.name << " power_budget=" << power_budget << " err=" << err
193 << " err_integral=" << thermal_throttling_status_map_[temp.name].err_integral
194 << " s_power=" << sensor_info.throttling_info->s_power[target_state]
195 << " time_elapsed_ms=" << time_elapsed_ms.count() << " p=" << p << " i=" << i
196 << " d=" << d << " control target=" << target_state;
197
198 return power_budget;
199 }
200
updateCdevRequestByPower(const Temperature_2_0 & temp,const SensorInfo & sensor_info,const ThrottlingSeverity curr_severity,const std::chrono::milliseconds time_elapsed_ms,const std::unordered_map<std::string,CdevInfo> & cooling_device_info_map)201 bool ThermalThrottling::updateCdevRequestByPower(
202 const Temperature_2_0 &temp, const SensorInfo &sensor_info,
203 const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms,
204 const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) {
205 float total_weight = 0, cdev_power_budget;
206 size_t j;
207
208 const auto target_state = getTargetStateOfPID(sensor_info, curr_severity);
209 auto total_power_budget = updatePowerBudget(temp, sensor_info, time_elapsed_ms, curr_severity);
210
211 std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_);
212 // Compute total cdev weight
213 for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) {
214 const auto cdev_weight = binded_cdev_info_pair.second
215 .cdev_weight_for_pid[static_cast<size_t>(curr_severity)];
216 if (std::isnan(cdev_weight)) {
217 continue;
218 }
219 total_weight += cdev_weight;
220 }
221
222 // Map cdev state by power
223 for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) {
224 const auto cdev_weight = binded_cdev_info_pair.second.cdev_weight_for_pid[target_state];
225 if (!std::isnan(cdev_weight)) {
226 cdev_power_budget = total_power_budget * (cdev_weight / total_weight);
227
228 const CdevInfo &cdev_info_pair =
229 cooling_device_info_map.at(binded_cdev_info_pair.first);
230 for (j = 0; j < cdev_info_pair.state2power.size() - 1; ++j) {
231 if (cdev_power_budget > cdev_info_pair.state2power[j]) {
232 break;
233 }
234 }
235
236 thermal_throttling_status_map_[temp.name].pid_cdev_request_map.at(
237 binded_cdev_info_pair.first) = static_cast<int>(j);
238 LOG(VERBOSE) << "Power allocator: Sensor " << temp.name << " allocate "
239 << cdev_power_budget << "mW to " << binded_cdev_info_pair.first
240 << "(cdev_weight=" << cdev_weight << ") update state to " << j;
241 }
242 }
243 return true;
244 }
245
updateCdevRequestBySeverity(std::string_view sensor_name,const SensorInfo & sensor_info,ThrottlingSeverity curr_severity)246 void ThermalThrottling::updateCdevRequestBySeverity(std::string_view sensor_name,
247 const SensorInfo &sensor_info,
248 ThrottlingSeverity curr_severity) {
249 std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_);
250 for (auto const &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) {
251 thermal_throttling_status_map_[sensor_name.data()].hardlimit_cdev_request_map.at(
252 binded_cdev_info_pair.first) =
253 binded_cdev_info_pair.second.limit_info[static_cast<size_t>(curr_severity)];
254 LOG(VERBOSE) << "Hard Limit: Sensor " << sensor_name.data() << " update cdev "
255 << binded_cdev_info_pair.first << " to "
256 << thermal_throttling_status_map_[sensor_name.data()]
257 .hardlimit_cdev_request_map.at(binded_cdev_info_pair.first);
258 }
259 }
260
throttlingReleaseUpdate(std::string_view sensor_name,const std::unordered_map<std::string,CdevInfo> & cooling_device_info_map,const std::unordered_map<std::string,PowerStatus> & power_status_map,const ThrottlingSeverity severity,const SensorInfo & sensor_info)261 bool ThermalThrottling::throttlingReleaseUpdate(
262 std::string_view sensor_name,
263 const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map,
264 const std::unordered_map<std::string, PowerStatus> &power_status_map,
265 const ThrottlingSeverity severity, const SensorInfo &sensor_info) {
266 ATRACE_CALL();
267 std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_);
268 if (!thermal_throttling_status_map_.count(sensor_name.data())) {
269 return false;
270 }
271 auto &thermal_throttling_status = thermal_throttling_status_map_.at(sensor_name.data());
272 for (const auto &binded_cdev_info_pair : sensor_info.throttling_info->binded_cdev_info_map) {
273 float avg_power = -1;
274
275 if (!thermal_throttling_status.throttling_release_map.count(binded_cdev_info_pair.first) ||
276 !power_status_map.count(binded_cdev_info_pair.second.power_rail)) {
277 return false;
278 }
279
280 const auto max_state = cooling_device_info_map.at(binded_cdev_info_pair.first).max_state;
281
282 auto &release_step =
283 thermal_throttling_status.throttling_release_map.at(binded_cdev_info_pair.first);
284 avg_power =
285 power_status_map.at(binded_cdev_info_pair.second.power_rail).last_updated_avg_power;
286
287 // Return false if we cannot get the AVG power
288 if (std::isnan(avg_power) || avg_power < 0) {
289 release_step = binded_cdev_info_pair.second.throttling_with_power_link ? max_state : 0;
290 continue;
291 }
292
293 bool is_over_budget = true;
294 if (!binded_cdev_info_pair.second.high_power_check) {
295 if (avg_power <
296 binded_cdev_info_pair.second.power_thresholds[static_cast<int>(severity)]) {
297 is_over_budget = false;
298 }
299 } else {
300 if (avg_power >
301 binded_cdev_info_pair.second.power_thresholds[static_cast<int>(severity)]) {
302 is_over_budget = false;
303 }
304 }
305 LOG(INFO) << sensor_name.data() << "'s " << binded_cdev_info_pair.first
306 << " binded power rail " << binded_cdev_info_pair.second.power_rail
307 << ": power threshold = "
308 << binded_cdev_info_pair.second.power_thresholds[static_cast<int>(severity)]
309 << ", avg power = " << avg_power;
310
311 switch (binded_cdev_info_pair.second.release_logic) {
312 case ReleaseLogic::INCREASE:
313 if (!is_over_budget) {
314 if (std::abs(release_step) < static_cast<int>(max_state)) {
315 release_step--;
316 }
317 } else {
318 release_step = 0;
319 }
320 break;
321 case ReleaseLogic::DECREASE:
322 if (!is_over_budget) {
323 if (release_step < static_cast<int>(max_state)) {
324 release_step++;
325 }
326 } else {
327 release_step = 0;
328 }
329 break;
330 case ReleaseLogic::STEPWISE:
331 if (!is_over_budget) {
332 if (release_step < static_cast<int>(max_state)) {
333 release_step++;
334 }
335 } else {
336 if (std::abs(release_step) < static_cast<int>(max_state)) {
337 release_step--;
338 }
339 }
340 break;
341 case ReleaseLogic::RELEASE_TO_FLOOR:
342 release_step = is_over_budget ? 0 : max_state;
343 break;
344 case ReleaseLogic::NONE:
345 default:
346 break;
347 }
348 }
349 return true;
350 }
351
thermalThrottlingUpdate(const Temperature_2_0 & temp,const SensorInfo & sensor_info,const ThrottlingSeverity curr_severity,const std::chrono::milliseconds time_elapsed_ms,const std::unordered_map<std::string,PowerStatus> & power_status_map,const std::unordered_map<std::string,CdevInfo> & cooling_device_info_map)352 void ThermalThrottling::thermalThrottlingUpdate(
353 const Temperature_2_0 &temp, const SensorInfo &sensor_info,
354 const ThrottlingSeverity curr_severity, const std::chrono::milliseconds time_elapsed_ms,
355 const std::unordered_map<std::string, PowerStatus> &power_status_map,
356 const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) {
357 if (!thermal_throttling_status_map_.count(temp.name)) {
358 return;
359 }
360
361 if (thermal_throttling_status_map_[temp.name].pid_power_budget_map.size()) {
362 updateCdevRequestByPower(temp, sensor_info, curr_severity, time_elapsed_ms,
363 cooling_device_info_map);
364 }
365
366 if (thermal_throttling_status_map_[temp.name].hardlimit_cdev_request_map.size()) {
367 updateCdevRequestBySeverity(temp.name.c_str(), sensor_info, curr_severity);
368 }
369
370 if (thermal_throttling_status_map_[temp.name].throttling_release_map.size()) {
371 throttlingReleaseUpdate(temp.name.c_str(), cooling_device_info_map, power_status_map,
372 curr_severity, sensor_info);
373 }
374 }
375
computeCoolingDevicesRequest(std::string_view sensor_name,const SensorInfo & sensor_info,const ThrottlingSeverity curr_severity,std::vector<std::string> * cooling_devices_to_update)376 void ThermalThrottling::computeCoolingDevicesRequest(
377 std::string_view sensor_name, const SensorInfo &sensor_info,
378 const ThrottlingSeverity curr_severity,
379 std::vector<std::string> *cooling_devices_to_update) {
380 int release_step = 0;
381 std::unique_lock<std::shared_mutex> _lock(thermal_throttling_status_map_mutex_);
382
383 if (!thermal_throttling_status_map_.count(sensor_name.data())) {
384 return;
385 }
386
387 auto &thermal_throttling_status = thermal_throttling_status_map_.at(sensor_name.data());
388 const auto &cdev_release_map = thermal_throttling_status.throttling_release_map;
389
390 for (auto &cdev_request_pair : thermal_throttling_status.cdev_status_map) {
391 int pid_cdev_request = 0;
392 int hardlimit_cdev_request = 0;
393 const auto &binded_cdev_info =
394 sensor_info.throttling_info->binded_cdev_info_map.at(cdev_request_pair.first);
395 const auto cdev_ceiling = binded_cdev_info.cdev_ceiling[static_cast<size_t>(curr_severity)];
396 const auto cdev_floor =
397 binded_cdev_info.cdev_floor_with_power_link[static_cast<size_t>(curr_severity)];
398 release_step = 0;
399
400 if (thermal_throttling_status.pid_cdev_request_map.count(cdev_request_pair.first)) {
401 pid_cdev_request =
402 thermal_throttling_status.pid_cdev_request_map.at(cdev_request_pair.first);
403 }
404
405 if (thermal_throttling_status.hardlimit_cdev_request_map.count(cdev_request_pair.first)) {
406 hardlimit_cdev_request = thermal_throttling_status.hardlimit_cdev_request_map.at(
407 cdev_request_pair.first);
408 }
409
410 if (cdev_release_map.count(cdev_request_pair.first)) {
411 release_step = cdev_release_map.at(cdev_request_pair.first);
412 }
413
414 LOG(VERBOSE) << sensor_name.data() << " binded cooling device " << cdev_request_pair.first
415 << "'s pid_request=" << pid_cdev_request
416 << " hardlimit_cdev_request=" << hardlimit_cdev_request
417 << " release_step=" << release_step
418 << " cdev_floor_with_power_link=" << cdev_floor
419 << " cdev_ceiling=" << cdev_ceiling;
420
421 auto request_state = std::max(pid_cdev_request, hardlimit_cdev_request);
422 if (release_step) {
423 if (release_step >= request_state) {
424 request_state = 0;
425 } else {
426 request_state = request_state - release_step;
427 }
428 // Only check the cdev_floor when release step is non zero
429 if (request_state < cdev_floor) {
430 request_state = cdev_floor;
431 }
432 }
433 if (request_state > cdev_ceiling) {
434 request_state = cdev_ceiling;
435 }
436
437 if (cdev_request_pair.second != request_state) {
438 cdev_request_pair.second = request_state;
439 cooling_devices_to_update->emplace_back(cdev_request_pair.first);
440 }
441 }
442 }
443
444 } // namespace implementation
445 } // namespace V2_0
446 } // namespace thermal
447 } // namespace hardware
448 } // namespace android
449