/* * Copyright 2024 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 "ChannelGroup.h" #include #include #include #include #include #include #include #include "AdpfTypes.h" #include "ChannelManager.h" #include "log/log_main.h" #include "tests/mocks/MockPowerHintSession.h" #include "tests/mocks/MockPowerSessionManager.h" namespace aidl::google::hardware::power::impl::pixel { using Tag = ChannelMessage::ChannelMessageContents::Tag; template ChannelGroup::ChannelGroup(int32_t id) : mGroupId(id), mFlagQueue(std::make_shared(1, true)), mGroupThread(std::thread(&ChannelGroup::runChannelGroup, this)) {} template ChannelGroup::~ChannelGroup() { mDestructing = true; EventFlag *flag; EventFlag::createEventFlag(mFlagQueue->getEventFlagWord(), &flag); // Wake up the handler. 0xffffffff wakes on every bit in the flag, // to ensure the wake-up will be handled regardless of other configuration settings, // and even if some of these bits are already set. flag->wake(0xffffffff); mGroupThread.join(); } template int32_t ChannelGroup::getChannelCount() const { return mLiveChannels; } template bool ChannelGroup::removeChannel(int32_t slot) { std::scoped_lock lock(mGroupMutex); if (!mChannels[slot]) { return false; } mChannels[slot] = nullptr; --mLiveChannels; return true; } template std::shared_ptr ChannelGroup::createChannel(int32_t tgid, int32_t uid) { std::scoped_lock lock(mGroupMutex); ALOGV("Creating channel for tgid: %" PRId32 " uid: %" PRId32, tgid, uid); int slot = 0; for (slot = 0; slot < kMaxChannels; ++slot) { if (!mChannels[slot]) { break; } } LOG_ALWAYS_FATAL_IF(slot == kMaxChannels, "Failed to create channel!"); ++mLiveChannels; ChannelManager::ChannelMapValue channelId{ {.groupId = static_cast(mGroupId), .offset = slot}}; mChannels[slot] = std::make_shared(tgid, uid, channelId, slot); ALOGV("Channel created on group: %" PRId32 " slot: %" PRId32, mGroupId, slot); return mChannels[slot]; } template std::shared_ptr ChannelGroup::getChannel( int32_t slot) { std::scoped_lock lock(mGroupMutex); LOG_ALWAYS_FATAL_IF(!mChannels[slot], "Requesting a dead channel!"); return mChannels[slot]; } template void ChannelGroup::getFlagDesc( std::optional *_return_desc) const { *_return_desc = std::make_optional(mFlagQueue->dupeDesc()); } template void ChannelGroup::runChannelGroup() { EventFlag *flag; { std::scoped_lock lock(mGroupMutex); EventFlag::createEventFlag(mFlagQueue->getEventFlagWord(), &flag); } setpriority(PRIO_PROCESS, getpid(), -20); uint32_t flagState = 0; static std::set blocklist = {}; std::vector messages; std::vector durations; durations.reserve(kFMQQueueSize); messages.reserve(kFMQQueueSize); while (!mDestructing) { messages.clear(); flag->wait(kWriteBits, &flagState, 0, true); int64_t now = ::android::uptimeNanos(); if (mDestructing) { return; } { std::scoped_lock lock(mGroupMutex); // Get the rightmost nonzero bit, corresponding to the next active channel for (int channelNum = std::countr_zero(flagState); channelNum < kMaxChannels && !mDestructing; channelNum = std::countr_zero(flagState)) { // Drop the lowest set write bit flagState &= (flagState - 1); auto &channel = mChannels[channelNum]; if (!channel || !channel->isValid()) { continue; } if (blocklist.contains(channel->getUid())) { continue; } int toRead = channel->getQueue()->availableToRead(); if (toRead <= 0) { continue; } messages.resize(toRead); if (!channel->getQueue()->read(messages.data(), toRead)) { // stop messing with your buffer >:( blocklist.insert(channel->getUid()); continue; } flag->wake(mChannels[channelNum]->getReadBitmask()); for (int messageIndex = 0; messageIndex < messages.size() && !mDestructing; ++messageIndex) { ChannelMessage &message = messages[messageIndex]; auto sessionPtr = std::static_pointer_cast( PowerSessionManagerT::getInstance()->getSession(message.sessionID)); if (!sessionPtr) { continue; } switch (message.data.getTag()) { case Tag::hint: { sessionPtr->sendHint(message.data.get()); break; } case Tag::targetDuration: { sessionPtr->updateTargetWorkDuration( message.data.get()); break; } case Tag::workDuration: { durations.clear(); for (; !mDestructing && messageIndex < messages.size() && messages[messageIndex].data.getTag() == Tag::workDuration && messages[messageIndex].sessionID == message.sessionID; ++messageIndex) { ChannelMessage ¤tMessage = messages[messageIndex]; auto &durationData = currentMessage.data.get(); durations.emplace_back(WorkDuration{ .timeStampNanos = currentMessage.timeStampNanos, .durationNanos = durationData.durationNanos, .cpuDurationNanos = durationData.cpuDurationNanos, .gpuDurationNanos = durationData.gpuDurationNanos, .workPeriodStartTimestampNanos = durationData.workPeriodStartTimestampNanos}); } sessionPtr->reportActualWorkDuration(durations); break; } case Tag::mode: { auto mode = message.data.get(); sessionPtr->setMode(mode.modeInt, mode.enabled); break; } default: { ALOGE("Invalid data tag sent: %s", std::to_string(static_cast(message.data.getTag())).c_str()); break; } } } } } } } template class ChannelGroup<>; template class ChannelGroup, testing::NiceMock>; } // namespace aidl::google::hardware::power::impl::pixel