android_device_motorola_sm6.../power-libperfmgr/PowerHintSession.cpp
Will McVicker 33444e5238
sm6375-common: power-libperfmgr: Fix race between closing a session and receiving a boost
We need to be holding the session lock when we dereference mDescriptor
since mDescriptor is destroyed when the session is closed. This patch
takes the session lock and verifies if it's still open during wakeup.

Test: vts run -m VtsHalGraphicsComposerV2_4TargetTest
Bug: 232992641
Change-Id: I4000a85bf2932cfdcddd3006d671a3c91ed32c48
2024-07-18 12:56:42 +05:30

556 lines
22 KiB
C++

/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "powerhal-libperfmgr"
#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#include "PowerHintSession.h"
#include <android-base/logging.h>
#include <android-base/parsedouble.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <perfmgr/AdpfConfig.h>
#include <private/android_filesystem_config.h>
#include <sys/syscall.h>
#include <time.h>
#include <utils/Trace.h>
#include <atomic>
#include "PowerSessionManager.h"
namespace aidl {
namespace google {
namespace hardware {
namespace power {
namespace impl {
namespace pixel {
using ::android::base::StringPrintf;
using ::android::perfmgr::AdpfConfig;
using ::android::perfmgr::HintManager;
using std::chrono::duration_cast;
using std::chrono::nanoseconds;
namespace {
static inline int64_t ns_to_100us(int64_t ns) {
return ns / 100000;
}
static int64_t convertWorkDurationToBoostByPid(std::shared_ptr<AdpfConfig> adpfConfig,
nanoseconds targetDuration,
const std::vector<WorkDuration> &actualDurations,
int64_t *integral_error, int64_t *previous_error,
const std::string &idstr) {
uint64_t samplingWindowP = adpfConfig->mSamplingWindowP;
uint64_t samplingWindowI = adpfConfig->mSamplingWindowI;
uint64_t samplingWindowD = adpfConfig->mSamplingWindowD;
int64_t targetDurationNanos = (int64_t)targetDuration.count();
int64_t length = actualDurations.size();
int64_t p_start =
samplingWindowP == 0 || samplingWindowP > length ? 0 : length - samplingWindowP;
int64_t i_start =
samplingWindowI == 0 || samplingWindowI > length ? 0 : length - samplingWindowI;
int64_t d_start =
samplingWindowD == 0 || samplingWindowD > length ? 0 : length - samplingWindowD;
int64_t dt = ns_to_100us(targetDurationNanos);
int64_t err_sum = 0;
int64_t derivative_sum = 0;
for (int64_t i = std::min({p_start, i_start, d_start}); i < length; i++) {
int64_t actualDurationNanos = actualDurations[i].durationNanos;
if (std::abs(actualDurationNanos) > targetDurationNanos * 20) {
ALOGW("The actual duration is way far from the target (%" PRId64 " >> %" PRId64 ")",
actualDurationNanos, targetDurationNanos);
}
// PID control algorithm
int64_t error = ns_to_100us(actualDurationNanos - targetDurationNanos);
if (i >= d_start) {
derivative_sum += error - (*previous_error);
}
if (i >= p_start) {
err_sum += error;
}
if (i >= i_start) {
*integral_error = *integral_error + error * dt;
*integral_error = std::min(adpfConfig->getPidIHighDivI(), *integral_error);
*integral_error = std::max(adpfConfig->getPidILowDivI(), *integral_error);
}
*previous_error = error;
}
int64_t pOut = static_cast<int64_t>((err_sum > 0 ? adpfConfig->mPidPo : adpfConfig->mPidPu) *
err_sum / (length - p_start));
int64_t iOut = static_cast<int64_t>(adpfConfig->mPidI * (*integral_error));
int64_t dOut =
static_cast<int64_t>((derivative_sum > 0 ? adpfConfig->mPidDo : adpfConfig->mPidDu) *
derivative_sum / dt / (length - d_start));
int64_t output = pOut + iOut + dOut;
if (ATRACE_ENABLED()) {
std::string sz = StringPrintf("adpf.%s-pid.err", idstr.c_str());
ATRACE_INT(sz.c_str(), err_sum / (length - p_start));
sz = StringPrintf("adpf.%s-pid.integral", idstr.c_str());
ATRACE_INT(sz.c_str(), *integral_error);
sz = StringPrintf("adpf.%s-pid.derivative", idstr.c_str());
ATRACE_INT(sz.c_str(), derivative_sum / dt / (length - d_start));
sz = StringPrintf("adpf.%s-pid.pOut", idstr.c_str());
ATRACE_INT(sz.c_str(), pOut);
sz = StringPrintf("adpf.%s-pid.iOut", idstr.c_str());
ATRACE_INT(sz.c_str(), iOut);
sz = StringPrintf("adpf.%s-pid.dOut", idstr.c_str());
ATRACE_INT(sz.c_str(), dOut);
sz = StringPrintf("adpf.%s-pid.output", idstr.c_str());
ATRACE_INT(sz.c_str(), output);
}
return output;
}
} // namespace
PowerHintSession::PowerHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds,
int64_t durationNanos) {
mDescriptor = new AppHintDesc(tgid, uid, threadIds);
mDescriptor->duration = std::chrono::nanoseconds(durationNanos);
mHintTimerHandler = sp<HintTimerHandler>(new HintTimerHandler(this));
mPowerManagerHandler = PowerSessionManager::getInstance();
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-target", idstr.c_str());
ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
sz = StringPrintf("adpf.%s-active", idstr.c_str());
ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
}
PowerSessionManager::getInstance()->addPowerSession(this);
// init boost
setSessionUclampMin(HintManager::GetInstance()->GetAdpfProfile()->mUclampMinInit);
ALOGV("PowerHintSession created: %s", mDescriptor->toString().c_str());
}
PowerHintSession::~PowerHintSession() {
close();
ALOGV("PowerHintSession deleted: %s", mDescriptor->toString().c_str());
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-target", idstr.c_str());
ATRACE_INT(sz.c_str(), 0);
sz = StringPrintf("adpf.%s-actl_last", idstr.c_str());
ATRACE_INT(sz.c_str(), 0);
sz = sz = StringPrintf("adpf.%s-active", idstr.c_str());
ATRACE_INT(sz.c_str(), 0);
}
{
std::lock_guard<std::mutex> guard(mSessionLock);
mSessionClosed.store(true);
}
mHintTimerHandler->setSessionDead();
delete mDescriptor;
}
std::string PowerHintSession::getIdString() const {
std::string idstr = StringPrintf("%" PRId32 "-%" PRId32 "-%" PRIxPTR, mDescriptor->tgid,
mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
return idstr;
}
bool PowerHintSession::isAppSession() {
// Check if uid is in range reserved for applications
return mDescriptor->uid >= AID_APP_START;
}
void PowerHintSession::updateUniveralBoostMode() {
if (!isAppSession()) {
return;
}
if (ATRACE_ENABLED()) {
const std::string tag = StringPrintf("%s:updateUniveralBoostMode()", getIdString().c_str());
ATRACE_BEGIN(tag.c_str());
}
PowerHintMonitor::getInstance()->getLooper()->sendMessage(mPowerManagerHandler, NULL);
if (ATRACE_ENABLED()) {
ATRACE_END();
}
}
int PowerHintSession::setSessionUclampMin(int32_t min) {
{
std::lock_guard<std::mutex> guard(mSessionLock);
mDescriptor->current_min = min;
}
PowerSessionManager::getInstance()->setUclampMin(this, min);
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-min", idstr.c_str());
ATRACE_INT(sz.c_str(), min);
}
return 0;
}
int PowerHintSession::getUclampMin() {
return mDescriptor->current_min;
}
void PowerHintSession::dumpToStream(std::ostream &stream) {
stream << "ID.Min.Act.Stale(" << getIdString();
stream << ", " << mDescriptor->current_min;
stream << ", " << mDescriptor->is_active;
stream << ", " << isStale() << ")";
}
ndk::ScopedAStatus PowerHintSession::pause() {
if (mSessionClosed) {
ALOGE("Error: session is dead");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (!mDescriptor->is_active.load())
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
// Reset to default uclamp value.
mDescriptor->is_active.store(false);
setStale();
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-active", idstr.c_str());
ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
}
updateUniveralBoostMode();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerHintSession::resume() {
if (mSessionClosed) {
ALOGE("Error: session is dead");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (mDescriptor->is_active.load())
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
mDescriptor->is_active.store(true);
mHintTimerHandler->updateHintTimer(0);
// resume boost
setSessionUclampMin(mDescriptor->current_min);
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-active", idstr.c_str());
ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
}
updateUniveralBoostMode();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerHintSession::close() {
bool sessionClosedExpectedToBe = false;
if (!mSessionClosed.compare_exchange_strong(sessionClosedExpectedToBe, true)) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
setSessionUclampMin(0);
PowerSessionManager::getInstance()->removePowerSession(this);
updateUniveralBoostMode();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) {
if (mSessionClosed) {
ALOGE("Error: session is dead");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (targetDurationNanos <= 0) {
ALOGE("Error: targetDurationNanos(%" PRId64 ") should bigger than 0", targetDurationNanos);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
targetDurationNanos =
targetDurationNanos * HintManager::GetInstance()->GetAdpfProfile()->mTargetTimeFactor;
ALOGV("update target duration: %" PRId64 " ns", targetDurationNanos);
mDescriptor->duration = std::chrono::nanoseconds(targetDurationNanos);
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-target", idstr.c_str());
ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
const std::vector<WorkDuration> &actualDurations) {
if (mSessionClosed) {
ALOGE("Error: session is dead");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (mDescriptor->duration.count() == 0LL) {
ALOGE("Expect to call updateTargetWorkDuration() first.");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (actualDurations.size() == 0) {
ALOGE("Error: duration.size() shouldn't be %zu.", actualDurations.size());
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (!mDescriptor->is_active.load()) {
ALOGE("Error: shouldn't report duration during pause state.");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
mDescriptor->update_count++;
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-batch_size", idstr.c_str());
ATRACE_INT(sz.c_str(), actualDurations.size());
sz = StringPrintf("adpf.%s-actl_last", idstr.c_str());
ATRACE_INT(sz.c_str(), actualDurations.back().durationNanos);
sz = StringPrintf("adpf.%s-target", idstr.c_str());
ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
sz = StringPrintf("adpf.%s-hint.count", idstr.c_str());
ATRACE_INT(sz.c_str(), mDescriptor->update_count);
sz = StringPrintf("adpf.%s-hint.overtime", idstr.c_str());
ATRACE_INT(sz.c_str(),
actualDurations.back().durationNanos - mDescriptor->duration.count() > 0);
}
mHintTimerHandler->updateHintTimer(actualDurations);
if (!adpfConfig->mPidOn) {
setSessionUclampMin(adpfConfig->mUclampMinHigh);
return ndk::ScopedAStatus::ok();
}
int64_t output = convertWorkDurationToBoostByPid(
adpfConfig, mDescriptor->duration, actualDurations, &(mDescriptor->integral_error),
&(mDescriptor->previous_error), getIdString());
/* apply to all the threads in the group */
int next_min = std::min(static_cast<int>(adpfConfig->mUclampMinHigh),
mDescriptor->current_min + static_cast<int>(output));
next_min = std::max(static_cast<int>(adpfConfig->mUclampMinLow), next_min);
setSessionUclampMin(next_min);
return ndk::ScopedAStatus::ok();
}
std::string AppHintDesc::toString() const {
std::string out =
StringPrintf("session %" PRIxPTR "\n", reinterpret_cast<uintptr_t>(this) & 0xffff);
const int64_t durationNanos = duration.count();
out.append(StringPrintf(" duration: %" PRId64 " ns\n", durationNanos));
out.append(StringPrintf(" uclamp.min: %d \n", current_min));
out.append(StringPrintf(" uid: %d, tgid: %d\n", uid, tgid));
out.append(" threadIds: [");
bool first = true;
for (int tid : threadIds) {
if (!first) {
out.append(", ");
}
out.append(std::to_string(tid));
first = false;
}
out.append("]\n");
return out;
}
bool PowerHintSession::isActive() {
return mDescriptor->is_active.load();
}
bool PowerHintSession::isStale() {
auto now = std::chrono::steady_clock::now();
return now >= mHintTimerHandler->getStaleTime();
}
const std::vector<int> &PowerHintSession::getTidList() const {
return mDescriptor->threadIds;
}
void PowerHintSession::setStale() {
// Reset to default uclamp value.
PowerSessionManager::getInstance()->setUclampMin(this, 0);
// Deliver a task to check if all sessions are inactive.
updateUniveralBoostMode();
}
void PowerHintSession::wakeup() {
std::lock_guard<std::mutex> guard(mSessionLock);
// We only wake up non-paused and stale sessions
if (mSessionClosed || !isActive() || !isStale())
return;
if (ATRACE_ENABLED()) {
std::string tag =
StringPrintf("wakeup.%s(a:%d,s:%d)", getIdString().c_str(), isActive(), isStale());
ATRACE_NAME(tag.c_str());
}
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
int min = std::max(mDescriptor->current_min, static_cast<int>(adpfConfig->mUclampMinInit));
mDescriptor->current_min = min;
PowerSessionManager::getInstance()->setUclampMinLocked(this, min);
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mHintTimerHandler);
PowerHintMonitor::getInstance()->getLooper()->sendMessage(
mHintTimerHandler, Message(static_cast<int>(HintTimerHandler::POKE)));
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-min", idstr.c_str());
ATRACE_INT(sz.c_str(), min);
}
}
void PowerHintSession::HintTimerHandler::updateHintTimer(int64_t actualDurationNs) {
std::lock_guard<std::mutex> guard(mStaleLock);
PowerHintSession::HintTimerHandler::updateHintTimerLocked(actualDurationNs);
}
void PowerHintSession::HintTimerHandler::updateHintTimerLocked(int64_t actualDurationNs) {
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
HintTimerState prevState = mState;
mState = MONITORING;
auto now = std::chrono::steady_clock::now();
mLastUpdatedTime.store(now);
nanoseconds nextStartDur = nanoseconds((mSession->mDescriptor->work_period
? mSession->mDescriptor->work_period
: mSession->mDescriptor->duration.count()) -
actualDurationNs);
mNextStartTime.store(actualDurationNs <= 0 ? now : now + nextStartDur);
if (prevState != MONITORING) {
int64_t next =
static_cast<int64_t>(duration_cast<nanoseconds>(getEarlyBoostTime() - now).count());
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mHintTimerHandler);
PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(
next, mSession->mHintTimerHandler, NULL);
if (prevState == STALE) {
mSession->updateUniveralBoostMode();
}
}
if (ATRACE_ENABLED()) {
const std::string idstr = mSession->getIdString();
std::string sz = StringPrintf("adpf.%s-timer.state", idstr.c_str());
ATRACE_INT(sz.c_str(), mState);
sz = StringPrintf("adpf.%s-timer.nextvsync", idstr.c_str());
ATRACE_INT(sz.c_str(), nextStartDur.count());
sz = StringPrintf("adpf.%s-timer.nexthint", idstr.c_str());
ATRACE_INT(sz.c_str(),
(int64_t)(nextStartDur.count() + mSession->mDescriptor->duration.count() *
adpfConfig->mEarlyBoostTimeFactor));
}
}
void PowerHintSession::HintTimerHandler::updateHintTimer(
const std::vector<WorkDuration> &actualDurations) {
if (actualDurations.size() == 0)
return;
if (actualDurations.size() >= 2) {
const WorkDuration &last = actualDurations[actualDurations.size() - 2];
mSession->mDescriptor->last_start = last.timeStampNanos - last.durationNanos;
}
const WorkDuration &current = actualDurations.back();
int64_t curr_start = current.timeStampNanos - current.durationNanos;
if (!mSession->mDescriptor->last_start) {
mSession->mDescriptor->last_start = curr_start;
updateHintTimer(current.durationNanos);
return;
}
int64_t period = curr_start - mSession->mDescriptor->last_start;
mSession->mDescriptor->last_start = curr_start;
if (period > 0 && period < mSession->mDescriptor->duration.count() * 2) {
// Accounting workload period with moving average for the last 10 workload.
mSession->mDescriptor->work_period =
0.9 * mSession->mDescriptor->work_period + 0.1 * period;
if (ATRACE_ENABLED()) {
const std::string idstr = mSession->getIdString();
std::string sz = StringPrintf("adpf.%s-timer.period", idstr.c_str());
ATRACE_INT(sz.c_str(), period);
}
}
updateHintTimer(current.durationNanos);
}
time_point<steady_clock> PowerHintSession::HintTimerHandler::getEarlyBoostTime() {
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
if (!adpfConfig->mEarlyBoostOn) {
return getStaleTime();
}
int64_t earlyBoostTimeoutNs =
(int64_t)mSession->mDescriptor->duration.count() * adpfConfig->mEarlyBoostTimeFactor;
return mNextStartTime.load() + nanoseconds(earlyBoostTimeoutNs);
}
time_point<steady_clock> PowerHintSession::HintTimerHandler::getStaleTime() {
return mLastUpdatedTime.load() +
nanoseconds(static_cast<int64_t>(
mSession->mDescriptor->duration.count() *
HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
}
PowerHintSession::HintTimerHandler::~HintTimerHandler() {
ATRACE_CALL();
}
void PowerHintSession::HintTimerHandler::handleMessage(const Message &msg) {
std::lock_guard<std::mutex> guard(mStaleLock);
if (mIsSessionDead) {
return;
}
if (msg.what == POKE) {
updateHintTimerLocked(0);
return;
}
std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
auto now = std::chrono::steady_clock::now();
auto staleTime = getStaleTime();
auto earlyBoostTime = getEarlyBoostTime();
if (adpfConfig->mEarlyBoostOn && now < earlyBoostTime) {
int64_t next =
static_cast<int64_t>(duration_cast<nanoseconds>(earlyBoostTime - now).count());
mState = MONITORING;
// Schedule for the early hint check.
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mHintTimerHandler);
PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(
next, mSession->mHintTimerHandler, static_cast<int>(HintTimerHandler::TIMER));
if (ATRACE_ENABLED()) {
const std::string idstr = mSession->getIdString();
std::string sz = StringPrintf("adpf.%s-timer.nexthint", idstr.c_str());
ATRACE_INT(sz.c_str(), next);
}
} else if (now >= staleTime) { // Check if the session is stale.
mSession->setStale();
mState = STALE;
} else { // Check if it's time to do early boost.
if (adpfConfig->mEarlyBoostOn) {
mState = EARLY_BOOST;
mSession->setSessionUclampMin(adpfConfig->mUclampMinHigh);
}
int64_t next = static_cast<int64_t>(duration_cast<nanoseconds>(staleTime - now).count());
// Schedule for the stale timeout check.
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mHintTimerHandler);
PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(
next, mSession->mHintTimerHandler, static_cast<int>(HintTimerHandler::TIMER));
}
if (ATRACE_ENABLED()) {
const std::string idstr = mSession->getIdString();
std::string sz = StringPrintf("adpf.%s-timer.state", idstr.c_str());
ATRACE_INT(sz.c_str(), mState);
}
}
void PowerHintSession::HintTimerHandler::setSessionDead() {
std::lock_guard<std::mutex> guard(mStaleLock);
PowerHintMonitor::getInstance()->getLooper()->removeMessages(mSession->mHintTimerHandler);
mIsSessionDead = true;
}
} // namespace pixel
} // namespace impl
} // namespace power
} // namespace hardware
} // namespace google
} // namespace aidl