1 /*
2 * Copyright 2023 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 "SessionTaskMap.h"
18
19 #include <algorithm>
20 #include <sstream>
21
22 namespace aidl {
23 namespace google {
24 namespace hardware {
25 namespace power {
26 namespace impl {
27 namespace pixel {
28
add(int64_t sessionId,const SessionValueEntry & sv,const std::vector<pid_t> & taskIds)29 bool SessionTaskMap::add(int64_t sessionId, const SessionValueEntry &sv,
30 const std::vector<pid_t> &taskIds) {
31 if (mSessions.find(sessionId) != mSessions.end()) {
32 return false;
33 }
34
35 auto sessValPtr = std::make_shared<SessionValueEntry>();
36 (*sessValPtr) = sv;
37 sessValPtr->sessionId = sessionId;
38
39 auto &sessEntry = mSessions[sessionId];
40 sessEntry.val = sessValPtr;
41 sessEntry.linkedTasks = taskIds;
42
43 for (auto taskId : taskIds) {
44 mTasks[taskId].push_back(sessValPtr);
45 }
46 return true;
47 }
48
addVote(int64_t sessionId,int voteId,int uclampMin,int uclampMax,std::chrono::steady_clock::time_point startTime,std::chrono::nanoseconds durationNs)49 void SessionTaskMap::addVote(int64_t sessionId, int voteId, int uclampMin, int uclampMax,
50 std::chrono::steady_clock::time_point startTime,
51 std::chrono::nanoseconds durationNs) {
52 auto sessItr = mSessions.find(sessionId);
53 if (sessItr == mSessions.end()) {
54 return;
55 }
56
57 sessItr->second.val->votes->add(voteId,
58 CpuVote(true, startTime, durationNs, uclampMin, uclampMax));
59 }
60
addGpuVote(int64_t sessionId,int voteId,Cycles capacity,std::chrono::steady_clock::time_point startTime,std::chrono::nanoseconds durationNs)61 void SessionTaskMap::addGpuVote(int64_t sessionId, int voteId, Cycles capacity,
62 std::chrono::steady_clock::time_point startTime,
63 std::chrono::nanoseconds durationNs) {
64 auto sessItr = mSessions.find(sessionId);
65 if (sessItr == mSessions.end()) {
66 return;
67 }
68
69 sessItr->second.val->votes->add(voteId, GpuVote(true, startTime, durationNs, capacity));
70 }
71
findSession(int64_t sessionId) const72 std::shared_ptr<SessionValueEntry> SessionTaskMap::findSession(int64_t sessionId) const {
73 auto sessItr = mSessions.find(sessionId);
74 if (sessItr == mSessions.end()) {
75 return nullptr;
76 }
77 return sessItr->second.val;
78 }
79
getTaskVoteRange(pid_t taskId,std::chrono::steady_clock::time_point timeNow,UclampRange & range,std::optional<int32_t> & uclampMaxEfficientBase,std::optional<int32_t> & uclampMaxEfficientOffset) const80 void SessionTaskMap::getTaskVoteRange(pid_t taskId, std::chrono::steady_clock::time_point timeNow,
81 UclampRange &range,
82 std::optional<int32_t> &uclampMaxEfficientBase,
83 std::optional<int32_t> &uclampMaxEfficientOffset) const {
84 auto taskItr = mTasks.find(taskId);
85 if (taskItr == mTasks.end()) {
86 // Assign to default range
87 range = {};
88 return;
89 }
90
91 for (const auto &sessInTask : taskItr->second) {
92 if (!sessInTask->isActive) {
93 continue;
94 }
95 sessInTask->votes->getUclampRange(range, timeNow);
96 if (sessInTask->isPowerEfficient && uclampMaxEfficientBase.has_value()) {
97 range.uclampMax = std::min(range.uclampMax,
98 sessInTask->votes->allTimedOut(timeNow)
99 ? *uclampMaxEfficientBase
100 : range.uclampMin + *uclampMaxEfficientOffset);
101 }
102 }
103 }
104
getSessionsGpuCapacity(std::chrono::steady_clock::time_point time_point) const105 Cycles SessionTaskMap::getSessionsGpuCapacity(
106 std::chrono::steady_clock::time_point time_point) const {
107 Cycles max(0);
108 for (auto const &[_, session] : mSessions) {
109 max = std::max(max,
110 session.val->votes->getGpuCapacityRequest(time_point).value_or(Cycles(0)));
111 }
112 return max;
113 }
114
getSessionIds(pid_t taskId) const115 std::vector<int64_t> SessionTaskMap::getSessionIds(pid_t taskId) const {
116 auto itr = mTasks.find(taskId);
117 if (itr == mTasks.end()) {
118 static const std::vector<int64_t> emptySessionIdVec;
119 return emptySessionIdVec;
120 }
121 std::vector<int64_t> res;
122 res.reserve(itr->second.size());
123 for (const auto &i : itr->second) {
124 res.push_back(i->sessionId);
125 }
126 return res;
127 }
128
getTaskIds(int64_t sessionId)129 std::vector<pid_t> &SessionTaskMap::getTaskIds(int64_t sessionId) {
130 auto taskItr = mSessions.find(sessionId);
131 if (taskItr == mSessions.end()) {
132 static std::vector<pid_t> emptyTaskIdVec;
133 return emptyTaskIdVec;
134 }
135 return taskItr->second.linkedTasks;
136 }
137
isAnyAppSessionActive(std::chrono::steady_clock::time_point timePoint) const138 bool SessionTaskMap::isAnyAppSessionActive(std::chrono::steady_clock::time_point timePoint) const {
139 for (auto &sessionVal : mSessions) {
140 if (!sessionVal.second.val->isAppSession) {
141 continue;
142 }
143 if (!sessionVal.second.val->isActive) {
144 continue;
145 }
146 if (!sessionVal.second.val->votes->allTimedOut(timePoint)) {
147 return true;
148 }
149 }
150 return false;
151 }
152
remove(int64_t sessionId)153 bool SessionTaskMap::remove(int64_t sessionId) {
154 auto sessItr = mSessions.find(sessionId);
155 if (sessItr == mSessions.end()) {
156 return false;
157 }
158
159 // For each task id in linked tasks need to remove the corresponding
160 // task to session mapping in the task map
161 for (const auto taskId : sessItr->second.linkedTasks) {
162 // Used linked task ids to cleanup
163 auto taskItr = mTasks.find(taskId);
164 if (taskItr == mTasks.end()) {
165 // Inconsisent state
166 continue;
167 }
168
169 // Now lookup session id in task's set
170 auto taskSessItr =
171 std::find(taskItr->second.begin(), taskItr->second.end(), sessItr->second.val);
172 if (taskSessItr == taskItr->second.end()) {
173 // Should not happen
174 continue;
175 }
176
177 // Remove session id from task map
178 taskItr->second.erase(taskSessItr);
179 if (taskItr->second.empty()) {
180 mTasks.erase(taskItr);
181 }
182 }
183
184 // Now we can safely remove session entirely since there are no more
185 // mappings in task to session id
186 mSessions.erase(sessItr);
187 return true;
188 }
189
removeDeadTaskSessionMap(int64_t sessionId,pid_t taskId)190 bool SessionTaskMap::removeDeadTaskSessionMap(int64_t sessionId, pid_t taskId) {
191 auto sessItr = mSessions.find(sessionId);
192 if (sessItr == mSessions.end()) {
193 return false;
194 }
195
196 auto taskItr = mTasks.find(taskId);
197 if (taskItr == mTasks.end()) {
198 // Inconsisent state
199 return false;
200 }
201
202 // Now lookup session id in task's set
203 auto taskSessItr =
204 std::find(taskItr->second.begin(), taskItr->second.end(), sessItr->second.val);
205 if (taskSessItr == taskItr->second.end()) {
206 // Should not happen
207 return false;
208 }
209
210 // Remove session id from task map
211 taskItr->second.erase(taskSessItr);
212 if (taskItr->second.empty()) {
213 mTasks.erase(taskItr);
214 }
215
216 return true;
217 }
218
replace(int64_t sessionId,const std::vector<pid_t> & taskIds,std::vector<pid_t> * addedThreads,std::vector<pid_t> * removedThreads)219 bool SessionTaskMap::replace(int64_t sessionId, const std::vector<pid_t> &taskIds,
220 std::vector<pid_t> *addedThreads, std::vector<pid_t> *removedThreads) {
221 auto itr = mSessions.find(sessionId);
222 if (itr == mSessions.end()) {
223 return false;
224 }
225
226 // Make copies of val and threads
227 auto svTmp = itr->second.val;
228 const auto previousTaskIds = itr->second.linkedTasks;
229
230 // Determine newly added threads
231 if (addedThreads) {
232 for (auto tid : taskIds) {
233 auto taskSessItr = mTasks.find(tid);
234 if (taskSessItr == mTasks.end()) {
235 addedThreads->push_back(tid);
236 }
237 }
238 }
239
240 // Remove session from mappings
241 remove(sessionId);
242 // Add session value and task mappings
243 add(sessionId, *svTmp, taskIds);
244
245 // Determine completely removed threads
246 if (removedThreads) {
247 for (auto tid : previousTaskIds) {
248 auto taskSessItr = mTasks.find(tid);
249 if (taskSessItr == mTasks.end()) {
250 removedThreads->push_back(tid);
251 }
252 }
253 }
254
255 return true;
256 }
257
sizeSessions() const258 size_t SessionTaskMap::sizeSessions() const {
259 return mSessions.size();
260 }
261
sizeTasks() const262 size_t SessionTaskMap::sizeTasks() const {
263 return mTasks.size();
264 }
265
idString(int64_t sessionId) const266 const std::string &SessionTaskMap::idString(int64_t sessionId) const {
267 auto sessItr = mSessions.find(sessionId);
268 if (sessItr == mSessions.end()) {
269 static const std::string emptyString;
270 return emptyString;
271 }
272 return sessItr->second.val->idString;
273 }
274
isAppSession(int64_t sessionId) const275 bool SessionTaskMap::isAppSession(int64_t sessionId) const {
276 auto sessItr = mSessions.find(sessionId);
277 if (sessItr == mSessions.end()) {
278 return false;
279 }
280
281 return sessItr->second.val->isAppSession;
282 }
283
284 } // namespace pixel
285 } // namespace impl
286 } // namespace power
287 } // namespace hardware
288 } // namespace google
289 } // namespace aidl
290