From 6aa83eadccc7818eec6cb345dba09076a8ab6d8b Mon Sep 17 00:00:00 2001 From: Vladimir Mikhailov Date: Wed, 1 Nov 2023 23:35:34 +0900 Subject: [PATCH] power: Release interaction lock when idle state is detected An idle state node has been added to some of our common kernels (e.g. qcom_msm8998). Use this node to unlock interaction boost before the timeout expires when screen updates are not expected soon. Change-Id: I43d1e2f2cbd6713baa132be7eb475243a8d4a5c6 --- android.hardware.power-service-qti.rc | 5 + power-common.c | 233 +++++++++++++++++++++++--- power-common.h | 7 + 3 files changed, 224 insertions(+), 21 deletions(-) diff --git a/android.hardware.power-service-qti.rc b/android.hardware.power-service-qti.rc index 05c4d33..01f9e70 100644 --- a/android.hardware.power-service-qti.rc +++ b/android.hardware.power-service-qti.rc @@ -2,3 +2,8 @@ service vendor.power /vendor/bin/hw/android.hardware.power-service-qti class hal user system group system + +on post-fs + chmod 0664 /sys/devices/virtual/graphics/fb0/idle_time + chown system graphics /sys/devices/virtual/graphics/fb0/idle_time + write /sys/devices/virtual/graphics/fb0/idle_time 100 diff --git a/power-common.c b/power-common.c index 533df83..b6a88e4 100644 --- a/power-common.c +++ b/power-common.c @@ -27,16 +27,19 @@ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define LOG_NIDEBUG 0 +//#define LOG_NDEBUG 0 #include #include #include +#include +#include #include #include #include #include #include +#include #define LOG_TAG "QTI PowerHAL" #include @@ -48,13 +51,157 @@ #include "power-common.h" #include "utils.h" +#define MAX_LENGTH 64 + static struct hint_handles handles[NUM_HINTS]; static int handleER = 0; +static const char* fb_idle_paths[] = {"/sys/class/drm/card0/device/idle_state", + "/sys/class/graphics/fb0/idle_state"}; + +static pthread_t tid; +static pthread_once_t once = PTHREAD_ONCE_INIT; +static pthread_cond_t interaction_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t interaction_lock = PTHREAD_MUTEX_INITIALIZER; + +static enum INTERACTION_STATE mState = INTERACTION_STATE_UNINITIALIZED; + +static int mIdleFd = 0; +static int mEventFd = 0; + +const int kWaitDuration = 100; /* ms */ const int kMaxLaunchDuration = 5000; /* ms */ const int kMaxInteractiveDuration = 5000; /* ms */ const int kMinInteractiveDuration = 500; /* ms */ +static struct timespec s_previous_boost_timespec; +static int s_previous_duration = 0; +static int prev_interaction_handle = -1; + +int process_boost(int hint_id, int duration, int type) { + ALOGV("%s: acquiring perf lock", __func__); + int boost_handle = perf_hint_enable_with_type(hint_id, duration, type); + if (!CHECK_HANDLE(boost_handle)) { + ALOGE("Failed process_boost for boost_handle"); + } + return boost_handle; +} + +bool release_boost(int boost_handle) { + ALOGV("%s: releasing perf lock %i", __func__, boost_handle); + if (CHECK_HANDLE(boost_handle)) { + release_request(boost_handle); + return true; + } + return false; +} + +int fb_idle_open(void) { + int fd; + int n = sizeof(fb_idle_paths) / sizeof(fb_idle_paths[0]); + for (int i = 0; i < n; i++) { + const char* path = fb_idle_paths[i]; + fd = open(path, O_RDONLY); + if (fd >= 0) + return fd; + } + ALOGE("Unable to open fb idle state path (%d)", errno); + return -1; +} + +void release() { + pthread_mutex_lock(&interaction_lock); + if (mState == INTERACTION_STATE_WAITING) { + if (release_boost(prev_interaction_handle)) { + prev_interaction_handle = -1; + } + mState = INTERACTION_STATE_IDLE; + } else { + // clear any wait aborts pending in event fd + uint64_t val; + ssize_t ret = read(mEventFd, &val, sizeof(val)); + + ALOGW_IF(ret < 0, "%s: failed to clear eventfd (%zd, %d)", + __func__, ret, errno); + } + pthread_mutex_unlock(&interaction_lock); +} + +void abortWaitLocked() { + uint64_t val = 1; + ssize_t ret = write(mEventFd, &val, sizeof(val)); + if (ret != sizeof(val)) + ALOGW("Unable to write to event fd (%zd)", ret); +} + +void waitForIdle(int32_t wait_ms, int32_t timeout_ms) { + char data[MAX_LENGTH]; + ssize_t ret; + struct pollfd pfd[2]; + + ALOGV("%s: wait:%d timeout:%d", __func__, wait_ms, timeout_ms); + + pfd[0].fd = mEventFd; + pfd[0].events = POLLIN; + pfd[1].fd = mIdleFd; + pfd[1].events = POLLPRI | POLLERR; + + ret = poll(pfd, 1, wait_ms); + if (ret > 0) { + ALOGV("%s: wait aborted", __func__); + return; + } else if (ret < 0) { + ALOGE("%s: error in poll while waiting", __func__); + return; + } + + ret = pread(mIdleFd, data, sizeof(data), 0); + if (!ret) { + ALOGE("%s: Unexpected EOF!", __func__); + return; + } + + if (!strncmp(data, "idle", 4)) { + ALOGV("%s: already idle", __func__); + return; + } + + ret = poll(pfd, 2, timeout_ms); + if (ret < 0) + ALOGE("%s: Error on waiting for idle (%zd)", __func__, ret); + else if (ret == 0) + ALOGV("%s: timed out waiting for idle", __func__); + else if (pfd[0].revents) + ALOGV("%s: wait for idle aborted", __func__); + else if (pfd[1].revents) + ALOGV("%s: idle detected", __func__); +} + +void* interaction_routine(void *vargp) { + while (true) { + pthread_mutex_lock(&interaction_lock); + + while (mState == INTERACTION_STATE_IDLE) + pthread_cond_wait(&interaction_cond, &interaction_lock); + + if (mState == INTERACTION_STATE_UNINITIALIZED) { + pthread_mutex_unlock(&interaction_lock); + return NULL; + } + + mState = INTERACTION_STATE_WAITING; + pthread_mutex_unlock(&interaction_lock); + + waitForIdle(kWaitDuration, s_previous_duration); + release(); + } + return NULL; +} + +static void create_once(void) { + pthread_create(&tid, NULL, interaction_routine, NULL); +} + void power_init() { ALOGI("Initing"); @@ -62,17 +209,40 @@ void power_init() { handles[i].handle = 0; handles[i].ref_count = 0; } + + pthread_mutex_lock(&interaction_lock); + if (mState != INTERACTION_STATE_UNINITIALIZED) { + pthread_mutex_unlock(&interaction_lock); + return; + } + + int fd = fb_idle_open(); + if (fd < 0) { + pthread_mutex_unlock(&interaction_lock); + return; + } + mIdleFd = fd; + + mEventFd = eventfd(0, EFD_NONBLOCK); + if (mEventFd < 0) { + ALOGE("Unable to create event fd (%d)", errno); + close(mIdleFd); + pthread_mutex_unlock(&interaction_lock); + return; + } + + mState = INTERACTION_STATE_IDLE; + pthread_once(&once, create_once); + pthread_mutex_unlock(&interaction_lock); } void process_interaction_hint(void* data) { - static struct timespec s_previous_boost_timespec; - static int s_previous_duration = 0; - static int prev_interaction_handle = -1; - struct timespec cur_boost_timespec; long long elapsed_time; int duration = kMinInteractiveDuration; + pthread_mutex_lock(&interaction_lock); + if (data) { int input_duration = *((int*)data); if (input_duration > duration) { @@ -84,26 +254,48 @@ void process_interaction_hint(void* data) { clock_gettime(CLOCK_MONOTONIC, &cur_boost_timespec); elapsed_time = calc_timespan_us(s_previous_boost_timespec, cur_boost_timespec); - // don't hint if it's been less than 250ms since last boost - // also detect if we're doing anything resembling a fling - // support additional boosting in case of flings - if (elapsed_time < 250000 && duration <= kMinInteractiveDuration) { - return; + if (mState == INTERACTION_STATE_UNINITIALIZED) { + // don't hint if it's been less than 250ms since last boost + // also detect if we're doing anything resembling a fling + // support additional boosting in case of flings + if (elapsed_time < 250000 && duration <= kMinInteractiveDuration) { + pthread_mutex_unlock(&interaction_lock); + return; + } } - // also don't hint if previous hint's duration covers this hint's duration - if ((s_previous_duration * 1000) > (elapsed_time + duration * 1000)) { - return; + + if (mState != INTERACTION_STATE_IDLE && duration <= s_previous_duration) { + // don't hint if previous hint's duration covers this hint's duration + if (elapsed_time <= (s_previous_duration - duration) * 1000) { + ALOGV("%s: Previous duration (%d) cover this (%d) elapsed: %lld", + __func__, s_previous_duration, duration, elapsed_time); + pthread_mutex_unlock(&interaction_lock); + return; + } } + s_previous_boost_timespec = cur_boost_timespec; s_previous_duration = duration; - int interaction_handle = - perf_hint_enable_with_type(VENDOR_HINT_SCROLL_BOOST, duration, SCROLL_VERTICAL); + if (mState == INTERACTION_STATE_UNINITIALIZED) { + int interaction_handle = + process_boost(VENDOR_HINT_SCROLL_BOOST, duration, SCROLL_VERTICAL); - if (CHECK_HANDLE(prev_interaction_handle)) { - release_request(prev_interaction_handle); + release_boost(prev_interaction_handle); + prev_interaction_handle = interaction_handle; + pthread_mutex_unlock(&interaction_lock); + return; } - prev_interaction_handle = interaction_handle; + + if (mState == INTERACTION_STATE_WAITING) + abortWaitLocked(); + else if (mState == INTERACTION_STATE_IDLE) + prev_interaction_handle = + process_boost(VENDOR_HINT_SCROLL_BOOST, INT_MAX, SCROLL_VERTICAL); + + mState = INTERACTION_STATE_INTERACTION; + pthread_cond_signal(&interaction_cond); + pthread_mutex_unlock(&interaction_lock); } void process_activity_launch_hint(void* data) { @@ -112,8 +304,7 @@ void process_activity_launch_hint(void* data) { // release lock early if launch has finished if (!data) { - if (CHECK_HANDLE(launch_handle)) { - release_request(launch_handle); + if (release_boost(launch_handle)) { launch_handle = -1; } launch_mode = 0; @@ -121,7 +312,7 @@ void process_activity_launch_hint(void* data) { } if (!launch_mode) { - launch_handle = perf_hint_enable_with_type(VENDOR_HINT_FIRST_LAUNCH_BOOST, + launch_handle = process_boost(VENDOR_HINT_FIRST_LAUNCH_BOOST, kMaxLaunchDuration, LAUNCH_BOOST_V1); if (!CHECK_HANDLE(launch_handle)) { ALOGE("Failed to perform launch boost"); diff --git a/power-common.h b/power-common.h index 02fd557..1566061 100644 --- a/power-common.h +++ b/power-common.h @@ -45,6 +45,13 @@ extern "C" { enum CPU_GOV_CHECK { CPU0 = 0, CPU1 = 1, CPU2 = 2, CPU3 = 3 }; +enum INTERACTION_STATE { + INTERACTION_STATE_UNINITIALIZED, + INTERACTION_STATE_IDLE, + INTERACTION_STATE_INTERACTION, + INTERACTION_STATE_WAITING, +}; + void power_init(void); void power_hint(power_hint_t hint, void* data); bool is_expensive_rendering_supported();