1 /*
2 * Copyright 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
17 #define LOG_TAG "powerhal-libperfmgr"
18 #define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
19
20 #include "PowerHintSession.h"
21
22 #include <android-base/logging.h>
23 #include <android-base/parsedouble.h>
24 #include <android-base/properties.h>
25 #include <android-base/stringprintf.h>
26 #include <perfmgr/AdpfConfig.h>
27 #include <private/android_filesystem_config.h>
28 #include <sys/syscall.h>
29 #include <time.h>
30 #include <utils/Trace.h>
31
32 #include <atomic>
33
34 #include "PowerSessionManager.h"
35
36 namespace aidl {
37 namespace google {
38 namespace hardware {
39 namespace power {
40 namespace impl {
41 namespace pixel {
42
43 using ::android::base::StringPrintf;
44 using ::android::perfmgr::AdpfConfig;
45 using ::android::perfmgr::HintManager;
46 using std::chrono::duration_cast;
47 using std::chrono::nanoseconds;
48
49 namespace {
50
ns_to_100us(int64_t ns)51 static inline int64_t ns_to_100us(int64_t ns) {
52 return ns / 100000;
53 }
54
55 } // namespace
56
convertWorkDurationToBoostByPid(const std::vector<WorkDuration> & actualDurations)57 int64_t PowerHintSession::convertWorkDurationToBoostByPid(
58 const std::vector<WorkDuration> &actualDurations) {
59 std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
60 const nanoseconds &targetDuration = mDescriptor->duration;
61 int64_t &integral_error = mDescriptor->integral_error;
62 int64_t &previous_error = mDescriptor->previous_error;
63 uint64_t samplingWindowP = adpfConfig->mSamplingWindowP;
64 uint64_t samplingWindowI = adpfConfig->mSamplingWindowI;
65 uint64_t samplingWindowD = adpfConfig->mSamplingWindowD;
66 int64_t targetDurationNanos = (int64_t)targetDuration.count();
67 int64_t length = actualDurations.size();
68 int64_t p_start =
69 samplingWindowP == 0 || samplingWindowP > length ? 0 : length - samplingWindowP;
70 int64_t i_start =
71 samplingWindowI == 0 || samplingWindowI > length ? 0 : length - samplingWindowI;
72 int64_t d_start =
73 samplingWindowD == 0 || samplingWindowD > length ? 0 : length - samplingWindowD;
74 int64_t dt = ns_to_100us(targetDurationNanos);
75 int64_t err_sum = 0;
76 int64_t derivative_sum = 0;
77 for (int64_t i = std::min({p_start, i_start, d_start}); i < length; i++) {
78 int64_t actualDurationNanos = actualDurations[i].durationNanos;
79 if (std::abs(actualDurationNanos) > targetDurationNanos * 20) {
80 ALOGW("The actual duration is way far from the target (%" PRId64 " >> %" PRId64 ")",
81 actualDurationNanos, targetDurationNanos);
82 }
83 // PID control algorithm
84 int64_t error = ns_to_100us(actualDurationNanos - targetDurationNanos);
85 if (i >= d_start) {
86 derivative_sum += error - previous_error;
87 }
88 if (i >= p_start) {
89 err_sum += error;
90 }
91 if (i >= i_start) {
92 integral_error += error * dt;
93 integral_error = std::min(adpfConfig->getPidIHighDivI(), integral_error);
94 integral_error = std::max(adpfConfig->getPidILowDivI(), integral_error);
95 }
96 previous_error = error;
97 }
98 int64_t pOut = static_cast<int64_t>((err_sum > 0 ? adpfConfig->mPidPo : adpfConfig->mPidPu) *
99 err_sum / (length - p_start));
100 int64_t iOut = static_cast<int64_t>(adpfConfig->mPidI * integral_error);
101 int64_t dOut =
102 static_cast<int64_t>((derivative_sum > 0 ? adpfConfig->mPidDo : adpfConfig->mPidDu) *
103 derivative_sum / dt / (length - d_start));
104
105 int64_t output = pOut + iOut + dOut;
106 if (ATRACE_ENABLED()) {
107 traceSessionVal("pid.err", err_sum / (length - p_start));
108 traceSessionVal("pid.integral", integral_error);
109 traceSessionVal("pid.derivative", derivative_sum / dt / (length - d_start));
110 traceSessionVal("pid.pOut", pOut);
111 traceSessionVal("pid.iOut", iOut);
112 traceSessionVal("pid.dOut", dOut);
113 traceSessionVal("pid.output", output);
114 }
115 return output;
116 }
117
PowerHintSession(int32_t tgid,int32_t uid,const std::vector<int32_t> & threadIds,int64_t durationNanos)118 PowerHintSession::PowerHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds,
119 int64_t durationNanos)
120 : mStaleTimerHandler(sp<StaleTimerHandler>::make(this)),
121 mBoostTimerHandler(sp<BoostTimerHandler>::make(this)) {
122 mDescriptor = new AppHintDesc(tgid, uid, threadIds);
123 mDescriptor->duration = std::chrono::nanoseconds(durationNanos);
124 mIdString = StringPrintf("%" PRId32 "-%" PRId32 "-%" PRIxPTR, mDescriptor->tgid,
125 mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
126
127 mPowerManagerHandler = PowerSessionManager::getInstance();
128 mLastUpdatedTime.store(std::chrono::steady_clock::now());
129 if (ATRACE_ENABLED()) {
130 traceSessionVal("target", mDescriptor->duration.count());
131 traceSessionVal("active", mDescriptor->is_active.load());
132 }
133 PowerSessionManager::getInstance()->addPowerSession(this);
134 // init boost
135 sendHint(SessionHint::CPU_LOAD_RESET);
136 ALOGV("PowerHintSession created: %s", mDescriptor->toString().c_str());
137 }
138
~PowerHintSession()139 PowerHintSession::~PowerHintSession() {
140 close();
141 ALOGV("PowerHintSession deleted: %s", mDescriptor->toString().c_str());
142 if (ATRACE_ENABLED()) {
143 traceSessionVal("target", 0);
144 traceSessionVal("actl_last", 0);
145 traceSessionVal("active", 0);
146 }
147 delete mDescriptor;
148 }
149
traceSessionVal(char const * identifier,int64_t val) const150 void PowerHintSession::traceSessionVal(char const *identifier, int64_t val) const {
151 ATRACE_INT(StringPrintf("adpf.%s-%s", mIdString.c_str(), identifier).c_str(), val);
152 }
153
isAppSession()154 bool PowerHintSession::isAppSession() {
155 // Check if uid is in range reserved for applications
156 return mDescriptor->uid >= AID_APP_START;
157 }
158
updateUniveralBoostMode()159 void PowerHintSession::updateUniveralBoostMode() {
160 if (!isAppSession()) {
161 return;
162 }
163 if (ATRACE_ENABLED()) {
164 const std::string tag = StringPrintf("%s:updateUniveralBoostMode()", mIdString.c_str());
165 ATRACE_BEGIN(tag.c_str());
166 }
167 PowerHintMonitor::getInstance()->getLooper()->sendMessage(mPowerManagerHandler, NULL);
168 if (ATRACE_ENABLED()) {
169 ATRACE_END();
170 }
171 }
172
tryToSendPowerHint(std::string hint)173 void PowerHintSession::tryToSendPowerHint(std::string hint) {
174 if (!mSupportedHints[hint].has_value()) {
175 mSupportedHints[hint] = HintManager::GetInstance()->IsHintSupported(hint);
176 }
177 if (mSupportedHints[hint].value()) {
178 HintManager::GetInstance()->DoHint(hint);
179 }
180 }
181
setSessionUclampMin(int32_t min,bool resetStale)182 int PowerHintSession::setSessionUclampMin(int32_t min, bool resetStale) {
183 {
184 std::lock_guard<std::mutex> guard(mSessionLock);
185 mDescriptor->current_min = min;
186 }
187 if (min != 0 && resetStale) {
188 mStaleTimerHandler->updateTimer();
189 }
190 PowerSessionManager::getInstance()->setUclampMin(this, min);
191
192 if (ATRACE_ENABLED()) {
193 traceSessionVal("min", min);
194 }
195 return 0;
196 }
197
getUclampMin()198 int PowerHintSession::getUclampMin() {
199 return mDescriptor->current_min;
200 }
201
dumpToStream(std::ostream & stream)202 void PowerHintSession::dumpToStream(std::ostream &stream) {
203 stream << "ID.Min.Act.Timeout(" << mIdString;
204 stream << ", " << mDescriptor->current_min;
205 stream << ", " << mDescriptor->is_active.load();
206 stream << ", " << isTimeout() << ")";
207 }
208
pause()209 ndk::ScopedAStatus PowerHintSession::pause() {
210 if (mSessionClosed) {
211 ALOGE("Error: session is dead");
212 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
213 }
214 if (!mDescriptor->is_active.load())
215 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
216 // Reset to default uclamp value.
217 mDescriptor->is_active.store(false);
218 setStale();
219 if (ATRACE_ENABLED()) {
220 traceSessionVal("active", mDescriptor->is_active.load());
221 }
222 updateUniveralBoostMode();
223 PowerSessionManager::getInstance()->removeThreadsFromPowerSession(this);
224 return ndk::ScopedAStatus::ok();
225 }
226
resume()227 ndk::ScopedAStatus PowerHintSession::resume() {
228 if (mSessionClosed) {
229 ALOGE("Error: session is dead");
230 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
231 }
232 if (mDescriptor->is_active.load())
233 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
234 mDescriptor->is_active.store(true);
235 PowerSessionManager::getInstance()->addThreadsFromPowerSession(this);
236 // resume boost
237 setSessionUclampMin(mDescriptor->current_min);
238 if (ATRACE_ENABLED()) {
239 traceSessionVal("active", mDescriptor->is_active.load());
240 }
241 updateUniveralBoostMode();
242 return ndk::ScopedAStatus::ok();
243 }
244
close()245 ndk::ScopedAStatus PowerHintSession::close() {
246 bool sessionClosedExpectedToBe = false;
247 if (!mSessionClosed.compare_exchange_strong(sessionClosedExpectedToBe, true)) {
248 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
249 }
250 // Remove the session from PowerSessionManager first to avoid racing.
251 PowerSessionManager::getInstance()->removePowerSession(this);
252 mStaleTimerHandler->setSessionDead();
253 mBoostTimerHandler->setSessionDead();
254 setSessionUclampMin(0);
255 mDescriptor->is_active.store(false);
256 updateUniveralBoostMode();
257 return ndk::ScopedAStatus::ok();
258 }
259
updateTargetWorkDuration(int64_t targetDurationNanos)260 ndk::ScopedAStatus PowerHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) {
261 if (mSessionClosed) {
262 ALOGE("Error: session is dead");
263 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
264 }
265 if (targetDurationNanos <= 0) {
266 ALOGE("Error: targetDurationNanos(%" PRId64 ") should bigger than 0", targetDurationNanos);
267 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
268 }
269 targetDurationNanos =
270 targetDurationNanos * HintManager::GetInstance()->GetAdpfProfile()->mTargetTimeFactor;
271 ALOGV("update target duration: %" PRId64 " ns", targetDurationNanos);
272
273 mDescriptor->duration = std::chrono::nanoseconds(targetDurationNanos);
274 if (ATRACE_ENABLED()) {
275 traceSessionVal("target", mDescriptor->duration.count());
276 }
277
278 return ndk::ScopedAStatus::ok();
279 }
280
reportActualWorkDuration(const std::vector<WorkDuration> & actualDurations)281 ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
282 const std::vector<WorkDuration> &actualDurations) {
283 if (mSessionClosed) {
284 ALOGE("Error: session is dead");
285 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
286 }
287 if (mDescriptor->duration.count() == 0LL) {
288 ALOGE("Expect to call updateTargetWorkDuration() first.");
289 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
290 }
291 if (actualDurations.size() == 0) {
292 ALOGE("Error: duration.size() shouldn't be %zu.", actualDurations.size());
293 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
294 }
295 if (!mDescriptor->is_active.load()) {
296 ALOGE("Error: shouldn't report duration during pause state.");
297 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
298 }
299 std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
300 mDescriptor->update_count++;
301 bool isFirstFrame = isTimeout();
302 if (ATRACE_ENABLED()) {
303 traceSessionVal("batch_size", actualDurations.size());
304 traceSessionVal("actl_last", actualDurations.back().durationNanos);
305 traceSessionVal("target", mDescriptor->duration.count());
306 traceSessionVal("hint.count", mDescriptor->update_count);
307 traceSessionVal("hint.overtime",
308 actualDurations.back().durationNanos - mDescriptor->duration.count() > 0);
309 }
310
311 mLastUpdatedTime.store(std::chrono::steady_clock::now());
312 if (isFirstFrame) {
313 if (isAppSession()) {
314 tryToSendPowerHint("ADPF_FIRST_FRAME");
315 }
316 updateUniveralBoostMode();
317 }
318
319 disableTemporaryBoost();
320
321 if (!adpfConfig->mPidOn) {
322 setSessionUclampMin(adpfConfig->mUclampMinHigh);
323 return ndk::ScopedAStatus::ok();
324 }
325
326 int64_t output = convertWorkDurationToBoostByPid(actualDurations);
327
328 /* apply to all the threads in the group */
329 int next_min = std::min(static_cast<int>(adpfConfig->mUclampMinHigh),
330 mDescriptor->current_min + static_cast<int>(output));
331 next_min = std::max(static_cast<int>(adpfConfig->mUclampMinLow), next_min);
332 setSessionUclampMin(next_min);
333
334 return ndk::ScopedAStatus::ok();
335 }
336
sendHint(SessionHint hint)337 ndk::ScopedAStatus PowerHintSession::sendHint(SessionHint hint) {
338 if (mSessionClosed) {
339 ALOGE("Error: session is dead");
340 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
341 }
342 disableTemporaryBoost();
343 std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
344 switch (hint) {
345 case SessionHint::CPU_LOAD_UP:
346 mNextUclampMin.store(mDescriptor->current_min);
347 mBoostTimerHandler->updateTimer(mDescriptor->duration * 2);
348 setSessionUclampMin(adpfConfig->mUclampMinHigh);
349 break;
350 case SessionHint::CPU_LOAD_DOWN:
351 setSessionUclampMin(adpfConfig->mUclampMinLow);
352 break;
353 case SessionHint::CPU_LOAD_RESET:
354 mNextUclampMin.store(std::max(adpfConfig->mUclampMinInit,
355 static_cast<uint32_t>(mDescriptor->current_min)));
356 mBoostTimerHandler->updateTimer(duration_cast<nanoseconds>(
357 mDescriptor->duration * adpfConfig->mStaleTimeFactor / 2.0));
358 setSessionUclampMin(adpfConfig->mUclampMinHigh);
359 break;
360 case SessionHint::CPU_LOAD_RESUME:
361 setSessionUclampMin(mDescriptor->current_min);
362 break;
363 default:
364 ALOGE("Error: hint is invalid");
365 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
366 }
367 tryToSendPowerHint(toString(hint));
368 mLastUpdatedTime.store(std::chrono::steady_clock::now());
369 if (ATRACE_ENABLED()) {
370 mLastHintSent = static_cast<int>(hint);
371 traceSessionVal("session_hint", static_cast<int>(hint));
372 }
373 return ndk::ScopedAStatus::ok();
374 }
375
setThreads(const std::vector<int32_t> & threadIds)376 ndk::ScopedAStatus PowerHintSession::setThreads(const std::vector<int32_t> &threadIds) {
377 if (mSessionClosed) {
378 ALOGE("Error: session is dead");
379 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
380 }
381 if (threadIds.size() == 0) {
382 LOG(ERROR) << "Error: threadIds.size() shouldn't be " << threadIds.size();
383 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
384 }
385
386 PowerSessionManager::getInstance()->removeThreadsFromPowerSession(this);
387 mDescriptor->threadIds.resize(threadIds.size());
388 std::copy(threadIds.begin(), threadIds.end(), back_inserter(mDescriptor->threadIds));
389 PowerSessionManager::getInstance()->addThreadsFromPowerSession(this);
390 // init boost
391 setSessionUclampMin(HintManager::GetInstance()->GetAdpfProfile()->mUclampMinInit);
392 return ndk::ScopedAStatus::ok();
393 }
394
toString() const395 std::string AppHintDesc::toString() const {
396 std::string out =
397 StringPrintf("session %" PRIxPTR "\n", reinterpret_cast<uintptr_t>(this) & 0xffff);
398 const int64_t durationNanos = duration.count();
399 out.append(StringPrintf(" duration: %" PRId64 " ns\n", durationNanos));
400 out.append(StringPrintf(" uclamp.min: %d \n", current_min));
401 out.append(StringPrintf(" uid: %d, tgid: %d\n", uid, tgid));
402
403 out.append(" threadIds: [");
404 bool first = true;
405 for (int tid : threadIds) {
406 if (!first) {
407 out.append(", ");
408 }
409 out.append(std::to_string(tid));
410 first = false;
411 }
412 out.append("]\n");
413 return out;
414 }
415
isActive()416 bool PowerHintSession::isActive() {
417 return mDescriptor->is_active.load();
418 }
419
isTimeout()420 bool PowerHintSession::isTimeout() {
421 auto now = std::chrono::steady_clock::now();
422 time_point<steady_clock> staleTime =
423 mLastUpdatedTime.load() +
424 nanoseconds(static_cast<int64_t>(
425 mDescriptor->duration.count() *
426 HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
427 return now >= staleTime;
428 }
429
getTidList() const430 const std::vector<int32_t> &PowerHintSession::getTidList() const {
431 return mDescriptor->threadIds;
432 }
433
disableTemporaryBoost()434 bool PowerHintSession::disableTemporaryBoost() {
435 if (ATRACE_ENABLED()) {
436 if (mLastHintSent != -1) {
437 mLastHintSent = -1;
438 traceSessionVal("session_hint", -1);
439 }
440 }
441
442 // replace temporary uclamp_min value with true min
443 std::optional<int> trueMin = mNextUclampMin.load();
444 if (trueMin.has_value()) {
445 std::lock_guard<std::mutex> guard(mSessionLock);
446 mDescriptor->current_min = *trueMin;
447 mNextUclampMin.store(std::nullopt);
448 return true;
449 }
450
451 return false;
452 }
453
setStale()454 void PowerHintSession::setStale() {
455 // Make sure any temporary boost is disabled
456 disableTemporaryBoost();
457 // Reset to default uclamp value.
458 PowerSessionManager::getInstance()->setUclampMin(this, 0);
459 // Deliver a task to check if all sessions are inactive.
460 updateUniveralBoostMode();
461 if (ATRACE_ENABLED()) {
462 traceSessionVal("min", 0);
463 }
464 }
465
updateTimer(nanoseconds delay)466 void PowerHintSession::SessionTimerHandler::updateTimer(nanoseconds delay) {
467 mTimeout.store(steady_clock::now() + delay);
468 {
469 std::lock_guard<std::mutex> guard(mMessageLock);
470 sp<MessageHandler> selfPtr = sp<MessageHandler>::fromExisting(this);
471 PowerHintMonitor::getInstance()->getLooper()->removeMessages(selfPtr);
472 PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(delay.count(), selfPtr,
473 NULL);
474 }
475 if (ATRACE_ENABLED()) {
476 mSession->traceSessionVal(("timer." + mName).c_str(), 0);
477 }
478 }
479
handleMessage(const Message &)480 void PowerHintSession::SessionTimerHandler::handleMessage(const Message &) {
481 std::lock_guard<std::mutex> guard(mClosedLock);
482 if (mIsSessionDead) {
483 return;
484 }
485 time_point now = steady_clock::now();
486 int64_t next = (mTimeout.load() - now).count();
487 if (next > 0) {
488 // Schedule for the stale timeout check.
489 std::lock_guard<std::mutex> guard(mMessageLock);
490 sp<MessageHandler> selfPtr = sp<MessageHandler>::fromExisting(this);
491 PowerHintMonitor::getInstance()->getLooper()->removeMessages(selfPtr);
492 PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(next, selfPtr, NULL);
493 } else {
494 onTimeout();
495 }
496 if (ATRACE_ENABLED()) {
497 mSession->traceSessionVal(("timer." + mName).c_str(), next > 0 ? 0 : 1);
498 }
499 }
500
setSessionDead()501 void PowerHintSession::SessionTimerHandler::setSessionDead() {
502 std::lock_guard<std::mutex> guard(mClosedLock);
503 mIsSessionDead = true;
504 PowerHintMonitor::getInstance()->getLooper()->removeMessages(
505 sp<MessageHandler>::fromExisting(this));
506 }
507
updateTimer()508 void PowerHintSession::StaleTimerHandler::updateTimer() {
509 SessionTimerHandler::updateTimer(duration_cast<nanoseconds>(
510 mSession->mDescriptor->duration *
511 HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
512 }
513
onTimeout()514 void PowerHintSession::StaleTimerHandler::onTimeout() {
515 mSession->setStale();
516 }
517
onTimeout()518 void PowerHintSession::BoostTimerHandler::onTimeout() {
519 if (mSession->disableTemporaryBoost()) {
520 mSession->setSessionUclampMin(mSession->getUclampMin(), false);
521 }
522 }
523
524 } // namespace pixel
525 } // namespace impl
526 } // namespace power
527 } // namespace hardware
528 } // namespace google
529 } // namespace aidl
530