• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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