1 /*
2  * Copyright (C) 2021 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 #include <dirent.h>
17 
18 #include <android-base/file.h>
19 #include <android-base/logging.h>
20 #include <android-base/stringprintf.h>
21 #include <android-base/strings.h>
22 
23 #include "power_files.h"
24 
25 namespace android {
26 namespace hardware {
27 namespace thermal {
28 namespace V2_0 {
29 namespace implementation {
30 
31 constexpr std::string_view kDeviceType("iio:device");
32 constexpr std::string_view kIioRootDir("/sys/bus/iio/devices");
33 constexpr std::string_view kEnergyValueNode("energy_value");
34 
35 using android::base::ReadFileToString;
36 using android::base::StringPrintf;
37 
setPowerDataToDefault(std::string_view sensor_name)38 void PowerFiles::setPowerDataToDefault(std::string_view sensor_name) {
39     std::unique_lock<std::shared_mutex> _lock(throttling_release_map_mutex_);
40     if (!throttling_release_map_.count(sensor_name.data()) ||
41         !power_status_map_.count(sensor_name.data())) {
42         return;
43     }
44 
45     auto &cdev_release_map = throttling_release_map_.at(sensor_name.data());
46     PowerSample power_sample = {};
47 
48     for (auto &power_status_pair : power_status_map_.at(sensor_name.data())) {
49         for (size_t i = 0; i < power_status_pair.second.power_history.size(); ++i) {
50             for (size_t j = 0; j < power_status_pair.second.power_history[i].size(); ++j) {
51                 power_status_pair.second.power_history[i].pop();
52                 power_status_pair.second.power_history[i].emplace(power_sample);
53             }
54         }
55         power_status_pair.second.last_updated_avg_power = NAN;
56     }
57 
58     for (auto &cdev_release_pair : cdev_release_map) {
59         cdev_release_pair.second.release_step = 0;
60     }
61 }
62 
getReleaseStep(std::string_view sensor_name,std::string_view cdev_name)63 int PowerFiles::getReleaseStep(std::string_view sensor_name, std::string_view cdev_name) {
64     int release_step = 0;
65     std::shared_lock<std::shared_mutex> _lock(throttling_release_map_mutex_);
66 
67     if (throttling_release_map_.count(sensor_name.data()) &&
68         throttling_release_map_[sensor_name.data()].count(cdev_name.data())) {
69         release_step = throttling_release_map_[sensor_name.data()][cdev_name.data()].release_step;
70     }
71 
72     return release_step;
73 }
74 
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)75 bool PowerFiles::registerPowerRailsToWatch(std::string_view sensor_name, std::string_view cdev_name,
76                                            const BindedCdevInfo &binded_cdev_info,
77                                            const CdevInfo &cdev_info,
78                                            const PowerRailInfo &power_rail_info) {
79     std::vector<std::queue<PowerSample>> power_history;
80     PowerSample power_sample = {
81             .energy_counter = 0,
82             .duration = 0,
83     };
84 
85     if (throttling_release_map_.count(sensor_name.data()) &&
86         throttling_release_map_[sensor_name.data()].count(binded_cdev_info.power_rail)) {
87         return true;
88     }
89 
90     if (!energy_info_map_.size() && !updateEnergyValues()) {
91         LOG(ERROR) << "Faield to update energy info";
92         return false;
93     }
94 
95     if (power_rail_info.virtual_power_rail_info != nullptr &&
96         power_rail_info.virtual_power_rail_info->linked_power_rails.size()) {
97         for (size_t i = 0; i < power_rail_info.virtual_power_rail_info->linked_power_rails.size();
98              ++i) {
99             if (energy_info_map_.count(
100                         power_rail_info.virtual_power_rail_info->linked_power_rails[i])) {
101                 power_history.emplace_back(std::queue<PowerSample>());
102                 for (int j = 0; j < power_rail_info.power_sample_count; j++) {
103                     power_history[i].emplace(power_sample);
104                 }
105             }
106         }
107     } else {
108         if (energy_info_map_.count(power_rail_info.rail)) {
109             power_history.emplace_back(std::queue<PowerSample>());
110             for (int j = 0; j < power_rail_info.power_sample_count; j++) {
111                 power_history[0].emplace(power_sample);
112             }
113         }
114     }
115 
116     if (power_history.size()) {
117         throttling_release_map_[sensor_name.data()][cdev_name.data()] = {
118                 .release_step = 0,
119                 .max_release_step = cdev_info.max_state,
120         };
121         power_status_map_[sensor_name.data()][binded_cdev_info.power_rail] = {
122                 .power_history = power_history,
123                 .time_remaining = power_rail_info.power_sample_delay,
124                 .last_updated_avg_power = NAN,
125         };
126     } else {
127         return false;
128     }
129 
130     LOG(INFO) << "Sensor " << sensor_name.data() << " successfully registers power rail "
131               << binded_cdev_info.power_rail << " for cooling device " << cdev_name.data();
132     return true;
133 }
134 
findEnergySourceToWatch(void)135 bool PowerFiles::findEnergySourceToWatch(void) {
136     std::string devicePath;
137 
138     if (energy_path_set_.size()) {
139         return true;
140     }
141 
142     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kIioRootDir.data()), closedir);
143     if (!dir) {
144         PLOG(ERROR) << "Error opening directory" << kIioRootDir;
145         return false;
146     }
147 
148     // Find any iio:devices that support energy_value
149     while (struct dirent *ent = readdir(dir.get())) {
150         std::string devTypeDir = ent->d_name;
151         if (devTypeDir.find(kDeviceType) != std::string::npos) {
152             devicePath = StringPrintf("%s/%s", kIioRootDir.data(), devTypeDir.data());
153             std::string deviceEnergyContent;
154 
155             if (!ReadFileToString(StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data()),
156                                   &deviceEnergyContent)) {
157             } else if (deviceEnergyContent.size()) {
158                 energy_path_set_.emplace(
159                         StringPrintf("%s/%s", devicePath.data(), kEnergyValueNode.data()));
160             }
161         }
162     }
163 
164     if (!energy_path_set_.size()) {
165         return false;
166     }
167 
168     return true;
169 }
170 
clearEnergyInfoMap(void)171 void PowerFiles::clearEnergyInfoMap(void) {
172     energy_info_map_.clear();
173 }
174 
updateEnergyValues(void)175 bool PowerFiles::updateEnergyValues(void) {
176     std::string deviceEnergyContent;
177     std::string deviceEnergyContents;
178     std::string line;
179 
180     for (const auto &path : energy_path_set_) {
181         if (!android::base::ReadFileToString(path, &deviceEnergyContent)) {
182             LOG(ERROR) << "Failed to read energy content from " << path;
183             return false;
184         } else {
185             deviceEnergyContents.append(deviceEnergyContent);
186         }
187     }
188 
189     std::istringstream energyData(deviceEnergyContents);
190 
191     clearEnergyInfoMap();
192     while (std::getline(energyData, line)) {
193         /* Read rail energy */
194         uint64_t energy_counter = 0;
195         uint64_t duration = 0;
196 
197         /* Format example: CH3(T=358356)[S2M_VDD_CPUCL2], 761330 */
198         auto start_pos = line.find("T=");
199         auto end_pos = line.find(')');
200         if (start_pos != std::string::npos) {
201             duration =
202                     strtoul(line.substr(start_pos + 2, end_pos - start_pos - 2).c_str(), NULL, 10);
203         } else {
204             continue;
205         }
206 
207         start_pos = line.find(")[");
208         end_pos = line.find(']');
209         std::string railName;
210         if (start_pos != std::string::npos) {
211             railName = line.substr(start_pos + 2, end_pos - start_pos - 2);
212         } else {
213             continue;
214         }
215 
216         start_pos = line.find("],");
217         if (start_pos != std::string::npos) {
218             energy_counter = strtoul(line.substr(start_pos + 2).c_str(), NULL, 10);
219         } else {
220             continue;
221         }
222 
223         energy_info_map_[railName] = {
224                 .energy_counter = energy_counter,
225                 .duration = duration,
226         };
227     }
228 
229     return true;
230 }
231 
getAveragePower(std::string_view power_rail,std::queue<PowerSample> * power_history,bool power_sample_update,float * avg_power)232 bool PowerFiles::getAveragePower(std::string_view power_rail,
233                                  std::queue<PowerSample> *power_history, bool power_sample_update,
234                                  float *avg_power) {
235     const auto curr_sample = energy_info_map_.at(power_rail.data());
236     bool ret = true;
237 
238     const auto last_sample = power_history->front();
239     const auto duration = curr_sample.duration - last_sample.duration;
240     const auto deltaEnergy = curr_sample.energy_counter - last_sample.energy_counter;
241 
242     if (!last_sample.duration) {
243         LOG(VERBOSE) << "Power rail " << power_rail.data() << ": the last energy timestamp is zero";
244     } else if (duration <= 0 || deltaEnergy < 0) {
245         LOG(ERROR) << "Power rail " << power_rail.data() << " is invalid: duration = " << duration
246                    << ", deltaEnergy = " << deltaEnergy;
247 
248         ret = false;
249     } else {
250         *avg_power = static_cast<float>(deltaEnergy) / static_cast<float>(duration);
251         LOG(VERBOSE) << "Power rail " << power_rail.data() << ", avg power = " << *avg_power
252                      << ", duration = " << duration << ", deltaEnergy = " << deltaEnergy;
253     }
254 
255     if (power_sample_update) {
256         power_history->pop();
257         power_history->push(curr_sample);
258     }
259 
260     return ret;
261 }
262 
computeAveragePower(const PowerRailInfo & power_rail_info,PowerStatus * power_status,bool power_sample_update,float * avg_power)263 bool PowerFiles::computeAveragePower(const PowerRailInfo &power_rail_info,
264                                      PowerStatus *power_status, bool power_sample_update,
265                                      float *avg_power) {
266     bool ret = true;
267 
268     float avg_power_val = -1;
269     float offset = power_rail_info.virtual_power_rail_info->offset;
270     for (size_t i = 0; i < power_rail_info.virtual_power_rail_info->linked_power_rails.size();
271          i++) {
272         float coefficient = power_rail_info.virtual_power_rail_info->coefficients[i];
273         float avg_power_number = -1;
274         if (!getAveragePower(power_rail_info.virtual_power_rail_info->linked_power_rails[i],
275                              &power_status->power_history[i], power_sample_update,
276                              &avg_power_number)) {
277             ret = false;
278             continue;
279         } else if (avg_power_number < 0) {
280             continue;
281         }
282         switch (power_rail_info.virtual_power_rail_info->formula) {
283             case FormulaOption::COUNT_THRESHOLD:
284                 if ((coefficient < 0 && avg_power_number < -coefficient) ||
285                     (coefficient >= 0 && avg_power_number >= coefficient))
286                     avg_power_val += 1;
287                 break;
288             case FormulaOption::WEIGHTED_AVG:
289                 avg_power_val += avg_power_number * coefficient;
290                 break;
291             case FormulaOption::MAXIMUM:
292                 if (i == 0)
293                     avg_power_val = std::numeric_limits<float>::lowest();
294                 if (avg_power_number * coefficient > avg_power_val)
295                     avg_power_val = avg_power_number * coefficient;
296                 break;
297             case FormulaOption::MINIMUM:
298                 if (i == 0)
299                     avg_power_val = std::numeric_limits<float>::max();
300                 if (avg_power_number * coefficient < avg_power_val)
301                     avg_power_val = avg_power_number * coefficient;
302                 break;
303             default:
304                 break;
305         }
306     }
307     if (avg_power_val >= 0) {
308         avg_power_val = avg_power_val + offset;
309     }
310 
311     *avg_power = avg_power_val;
312     return ret;
313 }
314 
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)315 bool PowerFiles::throttlingReleaseUpdate(std::string_view sensor_name, std::string_view cdev_name,
316                                          const ThrottlingSeverity severity,
317                                          const std::chrono::milliseconds time_elapsed_ms,
318                                          const BindedCdevInfo &binded_cdev_info,
319                                          const PowerRailInfo &power_rail_info,
320                                          bool power_sample_update, bool severity_changed) {
321     std::unique_lock<std::shared_mutex> _lock(throttling_release_map_mutex_);
322     float avg_power = -1;
323 
324     if (!throttling_release_map_.count(sensor_name.data()) ||
325         !throttling_release_map_[sensor_name.data()].count(cdev_name.data()) ||
326         !power_status_map_.count(sensor_name.data()) ||
327         !power_status_map_[sensor_name.data()].count(binded_cdev_info.power_rail)) {
328         return false;
329     }
330 
331     auto &release_status = throttling_release_map_[sensor_name.data()].at(cdev_name.data());
332     auto &power_status = power_status_map_[sensor_name.data()].at(binded_cdev_info.power_rail);
333 
334     if (power_sample_update) {
335         if (time_elapsed_ms > power_status.time_remaining) {
336             power_status.time_remaining = power_rail_info.power_sample_delay;
337         } else {
338             power_status.time_remaining = power_status.time_remaining - time_elapsed_ms;
339             LOG(VERBOSE) << "Power rail " << binded_cdev_info.power_rail
340                          << " : timeout remaining = " << power_status.time_remaining.count();
341             if (!severity_changed) {
342                 return true;
343             } else {
344                 // get the cached average power when thermal severity is changed
345                 power_sample_update = false;
346             }
347         }
348     } else if (!severity_changed &&
349                power_status.time_remaining != power_rail_info.power_sample_delay) {
350         return false;
351     }
352 
353     if (!energy_info_map_.size() && !updateEnergyValues()) {
354         LOG(ERROR) << "Failed to update energy values";
355         release_status.release_step = 0;
356         return false;
357     }
358 
359     if (!power_sample_update && !std::isnan(power_status.last_updated_avg_power)) {
360         avg_power = power_status.last_updated_avg_power;
361     } else {
362         // Return false if we cannot get the average power of the target power rail
363         if (!((power_rail_info.virtual_power_rail_info == nullptr)
364                       ? getAveragePower(binded_cdev_info.power_rail, &power_status.power_history[0],
365                                         power_sample_update, &avg_power)
366                       : computeAveragePower(power_rail_info, &power_status, power_sample_update,
367                                             &avg_power))) {
368             release_status.release_step = 0;
369             if (binded_cdev_info.throttling_with_power_link) {
370                 release_status.release_step = release_status.max_release_step;
371             }
372             return false;
373         } else if (avg_power < 0) {
374             if (binded_cdev_info.throttling_with_power_link) {
375                 release_status.release_step = release_status.max_release_step;
376             }
377             return true;
378         }
379     }
380 
381     power_status.last_updated_avg_power = avg_power;
382     bool is_over_budget = true;
383     if (!binded_cdev_info.high_power_check) {
384         if (avg_power < binded_cdev_info.power_thresholds[static_cast<int>(severity)]) {
385             is_over_budget = false;
386         }
387     } else {
388         if (avg_power > binded_cdev_info.power_thresholds[static_cast<int>(severity)]) {
389             is_over_budget = false;
390         }
391     }
392     LOG(INFO) << "Power rail " << binded_cdev_info.power_rail << ": power threshold = "
393               << binded_cdev_info.power_thresholds[static_cast<int>(severity)]
394               << ", avg power = " << avg_power;
395 
396     switch (binded_cdev_info.release_logic) {
397         case ReleaseLogic::INCREASE:
398             if (!is_over_budget) {
399                 if (std::abs(release_status.release_step) <
400                     static_cast<int>(release_status.max_release_step)) {
401                     release_status.release_step--;
402                 }
403             } else {
404                 release_status.release_step = 0;
405             }
406             break;
407         case ReleaseLogic::DECREASE:
408             if (!is_over_budget) {
409                 if (release_status.release_step <
410                     static_cast<int>(release_status.max_release_step)) {
411                     release_status.release_step++;
412                 }
413             } else {
414                 release_status.release_step = 0;
415             }
416             break;
417         case ReleaseLogic::STEPWISE:
418             if (!is_over_budget) {
419                 if (release_status.release_step <
420                     static_cast<int>(release_status.max_release_step)) {
421                     release_status.release_step++;
422                 }
423             } else {
424                 if (std::abs(release_status.release_step) <
425                     static_cast<int>(release_status.max_release_step)) {
426                     release_status.release_step--;
427                 }
428             }
429             break;
430         case ReleaseLogic::RELEASE_TO_FLOOR:
431             release_status.release_step = is_over_budget ? 0 : release_status.max_release_step;
432             break;
433         case ReleaseLogic::NONE:
434         default:
435             break;
436     }
437     return true;
438 }
439 
440 }  // namespace implementation
441 }  // namespace V2_0
442 }  // namespace thermal
443 }  // namespace hardware
444 }  // namespace android
445