From 6593e2cee6e85cd2a6922e1d6a5c2c9a4549a927 Mon Sep 17 00:00:00 2001 From: Carlos Galo Date: Wed, 3 Apr 2024 12:34:57 -0700 Subject: [PATCH] Adding kswapd state monitoring with memevent_listener Replacing mechanism of reading vmstats to detect kswapd with memevents instead. Maintain vmstats mechanism if bpf is not supported by current kernel. Test: Verified lmkd receives kswapd state changes Test: m Bug: 330606003 Change-Id: I9b980a8b94e015d1b8e0986fff9113890420d102 Signed-off-by: Carlos Galo --- lmkd.cpp | 61 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/lmkd.cpp b/lmkd.cpp index 9bd331e..e88aedc 100644 --- a/lmkd.cpp +++ b/lmkd.cpp @@ -195,9 +195,10 @@ struct psi_threshold { int threshold_ms; }; -/* Listener for direct reclaim state changes */ +/* Listener for direct reclaim and kswapd state changes */ static std::unique_ptr memevent_listener(nullptr); static struct timespec direct_reclaim_start_tm; +static struct timespec kswapd_start_tm; static int level_oomadj[VMPRESS_LEVEL_COUNT]; static int mpevfd[VMPRESS_LEVEL_COUNT] = { -1, -1, -1 }; @@ -1591,9 +1592,9 @@ static void ctrl_command_handler(int dsock_idx) { * waiting, during boot-up, for BPF programs to be loaded. */ if (init_memevent_listener_monitoring()) { - ALOGI("Using memevents for direct reclaim detection"); + ALOGI("Using memevents for direct reclaim and kswapd detection"); } else { - ALOGI("Using vmstats for direct reclaim detection"); + ALOGI("Using vmstats for direct reclaim and kswapd detection"); if (direct_reclaim_threshold_ms > 0) { ALOGW("Kernel support for direct_reclaim_threshold_ms is not found"); direct_reclaim_threshold_ms = 0; @@ -2673,6 +2674,7 @@ static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_ bool critical_stall = false; bool in_direct_reclaim; long direct_reclaim_duration_ms; + bool in_kswapd_reclaim; if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) { ALOGE("Failed to get current time"); @@ -2725,9 +2727,15 @@ static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_ swap_low_threshold = 0; } - in_direct_reclaim = memevent_listener ? (direct_reclaim_start_tm.tv_sec != 0 || - direct_reclaim_start_tm.tv_nsec != 0) - : (vs.field.pgscan_direct != init_pgscan_direct); + if (memevent_listener) { + in_direct_reclaim = + direct_reclaim_start_tm.tv_sec != 0 || direct_reclaim_start_tm.tv_nsec != 0; + in_kswapd_reclaim = kswapd_start_tm.tv_sec != 0 || kswapd_start_tm.tv_nsec != 0; + } else { + in_direct_reclaim = vs.field.pgscan_direct != init_pgscan_direct; + in_kswapd_reclaim = (vs.field.pgscan_kswapd != init_pgscan_kswapd) || + (vs.field.pgrefill != init_pgrefill); + } /* Identify reclaim state */ if (in_direct_reclaim) { @@ -2736,13 +2744,10 @@ static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_ init_pgrefill = vs.field.pgrefill; direct_reclaim_duration_ms = get_time_diff_ms(&direct_reclaim_start_tm, &curr_tm); reclaim = DIRECT_RECLAIM; - } else if (vs.field.pgscan_kswapd != init_pgscan_kswapd) { + } else if (in_kswapd_reclaim) { init_pgscan_kswapd = vs.field.pgscan_kswapd; init_pgrefill = vs.field.pgrefill; reclaim = KSWAPD_RECLAIM; - } else if (vs.field.pgrefill != init_pgrefill) { - init_pgrefill = vs.field.pgrefill; - reclaim = KSWAPD_RECLAIM; } else if (workingset_refault_file == prev_workingset_refault) { /* * Device is not thrashing and not reclaiming, bail out early until we see these stats @@ -3315,17 +3320,25 @@ static void memevent_listener_notification(int data __unused, uint32_t events __ return; } - /* - * `mem_events` is ordered from oldest to newest, therefore we use - * the last/latest direct reclaim event as the current direct reclaim - * state. - */ - for (const mem_event_t mem_event : mem_events) { - if (mem_event.type == MEM_EVENT_DIRECT_RECLAIM_BEGIN) { - direct_reclaim_start_tm = curr_tm; - } else if (mem_event.type == MEM_EVENT_DIRECT_RECLAIM_END) { - direct_reclaim_start_tm.tv_sec = 0; - direct_reclaim_start_tm.tv_nsec = 0; + for (const mem_event_t& mem_event : mem_events) { + switch (mem_event.type) { + /* Direct Reclaim */ + case MEM_EVENT_DIRECT_RECLAIM_BEGIN: + direct_reclaim_start_tm = curr_tm; + break; + case MEM_EVENT_DIRECT_RECLAIM_END: + direct_reclaim_start_tm.tv_sec = 0; + direct_reclaim_start_tm.tv_nsec = 0; + break; + + /* kswapd */ + case MEM_EVENT_KSWAPD_WAKE: + kswapd_start_tm = curr_tm; + break; + case MEM_EVENT_KSWAPD_SLEEP: + kswapd_start_tm.tv_sec = 0; + kswapd_start_tm.tv_nsec = 0; + break; } } } @@ -3353,6 +3366,12 @@ static bool init_memevent_listener_monitoring() { memevent_listener.reset(); return false; } + if (!memevent_listener->registerEvent(MEM_EVENT_KSWAPD_WAKE) || + !memevent_listener->registerEvent(MEM_EVENT_KSWAPD_SLEEP)) { + ALOGE("Failed to register kswapd memevents"); + memevent_listener.reset(); + return false; + } int memevent_listener_fd = memevent_listener->getRingBufferFd(); if (memevent_listener_fd < 0) {