1 /*
2 * Copyright (C) 2024 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 #define LOG_TAG "powerhal-libperfmgr"
18 #define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
19
20 #include "GpuCapacityNode.h"
21
22 #include <android-base/logging.h>
23 #include <utils/Trace.h>
24
25 #include <charconv>
26
27 #include "perfmgr/HintManager.h"
28
29 namespace aidl {
30 namespace google {
31 namespace hardware {
32 namespace power {
33 namespace impl {
34 namespace pixel {
35
~GpuCapacityNode()36 GpuCapacityNode::~GpuCapacityNode() {
37 fd_interface_->close(frequency_fd_);
38 fd_interface_->close(capacity_headroom_fd_);
39 }
40
GpuCapacityNode(std::unique_ptr<FdInterface> fd_interface,int validated_capacity_headroom_fd,int validated_frequency_fd,std::string_view node_path)41 GpuCapacityNode::GpuCapacityNode(std::unique_ptr<FdInterface> fd_interface,
42 int validated_capacity_headroom_fd, int validated_frequency_fd,
43 std::string_view node_path)
44 : fd_interface_(std::move(fd_interface)),
45 capacity_node_path_(node_path),
46 capacity_headroom_fd_(validated_capacity_headroom_fd),
47 frequency_fd_(validated_frequency_fd) {
48 if (capacity_headroom_fd_ < 0) {
49 LOG(FATAL) << ("precondition violation for GpuCapacityNode: invalid capacity_headroom_fd_");
50 }
51 if (frequency_fd_ < 0) {
52 LOG(FATAL) << ("precondition violation for GpuCapacityNode: invalid frequency_fd_");
53 }
54 }
55
init_gpu_capacity_node(std::unique_ptr<FdInterface> fd_interface,std::string_view gpu_node_dir)56 std::unique_ptr<GpuCapacityNode> GpuCapacityNode::init_gpu_capacity_node(
57 std::unique_ptr<FdInterface> fd_interface, std::string_view gpu_node_dir) {
58 static constexpr auto fd_flags_common = O_CLOEXEC | O_NONBLOCK;
59 auto const capacity_headroom_file = std::string(gpu_node_dir) + "/capacity_headroom";
60 auto const capacity_headroom_fd =
61 fd_interface->open(capacity_headroom_file.c_str(), O_RDWR | fd_flags_common);
62 if (capacity_headroom_fd < 0) {
63 LOG(ERROR) << "could not open gpu capacity path: " << capacity_headroom_file << ": "
64 << strerror(errno);
65 return nullptr;
66 }
67 auto const gpu_freq_file = std::string(gpu_node_dir) + "/cur_freq";
68 auto const frequency_fd = fd_interface->open(gpu_freq_file.c_str(), O_RDONLY | fd_flags_common);
69 if (frequency_fd < 0) {
70 fd_interface->close(capacity_headroom_fd);
71 LOG(ERROR) << "could not open gpu capacity path: " << gpu_freq_file << ": "
72 << strerror(errno);
73 return nullptr;
74 }
75 return std::make_unique<GpuCapacityNode>(std::move(fd_interface), capacity_headroom_fd,
76 frequency_fd, gpu_node_dir);
77 }
78
set_gpu_capacity(Cycles capacity) const79 bool GpuCapacityNode::set_gpu_capacity(Cycles capacity) const {
80 std::lock_guard lk(capacity_mutex_);
81 auto const capacity_str = std::to_string(static_cast<int>(capacity));
82 ATRACE_INT("gpuCapacitySet", static_cast<int>(static_cast<int>(capacity)));
83 auto const rc =
84 fd_interface_->write(capacity_headroom_fd_, capacity_str.c_str(), capacity_str.size());
85 if (!rc) {
86 LOG(ERROR) << "could not write to capacity node: " << capacity_node_path_ << ": "
87 << strerror(errno);
88 }
89 return !rc;
90 }
91
gpu_frequency() const92 std::optional<Frequency> GpuCapacityNode::gpu_frequency() const {
93 std::lock_guard lk(freq_mutex_);
94 std::array<char, 16> buffer;
95 buffer.fill('\0');
96
97 ssize_t bytes_read_total = 0;
98 size_t const effective_size = buffer.size() - 1;
99 do {
100 auto const bytes_read = fd_interface_->read(frequency_fd_, buffer.data() + bytes_read_total,
101 effective_size - bytes_read_total);
102 if (bytes_read == 0) {
103 break;
104 }
105
106 if (bytes_read < 0) {
107 LOG(ERROR) << "could not read gpu frequency:" << bytes_read << ": " << strerror(errno);
108 return {};
109 }
110 bytes_read_total += bytes_read;
111 } while (bytes_read_total < effective_size);
112
113 auto const rc = fd_interface_->lseek(frequency_fd_, 0, SEEK_SET);
114 if (rc < 0) {
115 LOG(ERROR) << "could not seek gpu frequency file: " << strerror(errno);
116 return {};
117 }
118
119 int frequency_raw = 0;
120 auto [ptr, ec] = std::from_chars(buffer.data(), buffer.data() + buffer.size(), frequency_raw);
121 if (ec != std::errc() || frequency_raw <= 0) {
122 LOG(ERROR) << "could not parse gpu frequency" << buffer.data();
123 return {};
124 }
125
126 auto const frequency = Frequency(frequency_raw * 1000);
127 ATRACE_INT("gpuRecordedFreq", static_cast<int>(frequency));
128 return frequency;
129 }
130
createGpuCapacityNode()131 std::optional<std::unique_ptr<GpuCapacityNode>> createGpuCapacityNode() {
132 auto const path = ::android::perfmgr::HintManager::GetInstance()->gpu_sysfs_config_path();
133 if (!path) {
134 return {};
135 }
136 return {GpuCapacityNode::init_gpu_capacity_node(std::make_unique<FdWriter>(), *path)};
137 }
138
139 } // namespace pixel
140 } // namespace impl
141 } // namespace power
142 } // namespace hardware
143 } // namespace google
144 } // namespace aidl
145