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