From 84d812ffb4d5ed049137afb9aa19f4de17ec8e7d Mon Sep 17 00:00:00 2001 From: Kalesh Singh Date: Thu, 10 Nov 2022 16:00:00 -0800 Subject: [PATCH] AMS: Proactive Kills - Minimal Implementation Under certain scenarios, PSI triggers may not wake up LMKD in time to relieve system resource contention. This is especially true in the case of MGLRU which makes smarter reclaim decisions and genereates less PSI. Additionally, reclaim of anonymous memory suffers when swap is full and under pressure the anon pages can be reactivated in an attempt to swap. This causes hot/cold page inversion. MGLRU detects this situation and doesn't swap but it leads to a similar scenario where the anon generations are in effect inverted (hot/cold generation inversion) if the swap contention doesn't decrease. To safe guard these cases, impose a minimum free swap threshold. The default is 10% but will be configurable from experiment flags. When swap drops below this limit, the LRU cached app is killed to help relieve swap pressure. Most cached apps contribute to swap usage because of the proactive app compaction, which may not necessarily mean we have memory pressure. Note that Andorid uses ZRAM (in-memory compressed swap). ZRAM size is virtual, so imposing a threshold on swap is not the same as reserving swap space. Bug: 249601646 Test: Run ACT, observe low swap kills Change-Id: I089d37fc067b9a85aabbd70b4513a209dfafa96d Signed-off-by: Kalesh Singh --- .../android/server/am/CachedAppOptimizer.java | 2 +- .../com/android/server/am/OomAdjuster.java | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 2d7b0dc7b536d..e34cd12d0ec39 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -845,7 +845,7 @@ public final class CachedAppOptimizer { /** * Retrieves the free swap percentage. */ - static private native double getFreeSwapPercent(); + static native double getFreeSwapPercent(); /** * Retrieves the total used physical ZRAM diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 68e5a5df95629..3c0fd8f5e4965 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1118,6 +1118,12 @@ public class OomAdjuster { private long mNextNoKillDebugMessageTime; + private double mLastFreeSwapPercent = 1.00; + + private static double getFreeSwapPercent() { + return CachedAppOptimizer.getFreeSwapPercent(); + } + @GuardedBy({"mService", "mProcLock"}) private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed, final long oldTime, final ActiveUids activeUids, @OomAdjReason int oomAdjReason) { @@ -1142,6 +1148,11 @@ public class OomAdjuster { int numEmpty = 0; int numTrimming = 0; + boolean proactiveKillsEnabled = false; // TODO: Configure from phenotype + double lowSwapThresholdPercent = 0.10; // TODO: Configure from phenotype + double freeSwapPercent = proactiveKillsEnabled ? getFreeSwapPercent() : 1.00; + ProcessRecord lruCachedApp = null; + for (int i = numLru - 1; i >= 0; i--) { ProcessRecord app = lruList.get(i); final ProcessStateRecord state = app.mState; @@ -1179,6 +1190,8 @@ public class OomAdjuster { ApplicationExitInfo.REASON_OTHER, ApplicationExitInfo.SUBREASON_TOO_MANY_CACHED, true); + } else if (proactiveKillsEnabled) { + lruCachedApp = app; } break; case PROCESS_STATE_CACHED_EMPTY: @@ -1198,6 +1211,8 @@ public class OomAdjuster { ApplicationExitInfo.REASON_OTHER, ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY, true); + } else if (proactiveKillsEnabled) { + lruCachedApp = app; } } break; @@ -1229,6 +1244,20 @@ public class OomAdjuster { } } + if (proactiveKillsEnabled // Proactive kills enabled? + && doKillExcessiveProcesses // Should kill excessive processes? + && freeSwapPercent < lowSwapThresholdPercent // Swap below threshold? + && lruCachedApp != null // If no cached app, let LMKD decide + // If swap is non-decreasing, give reclaim a chance to catch up + && freeSwapPercent < mLastFreeSwapPercent) { + lruCachedApp.killLocked("swap low and too many cached", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_TOO_MANY_CACHED, + true); + } + + mLastFreeSwapPercent = freeSwapPercent; + return mService.mAppProfiler.updateLowMemStateLSP(numCached, numEmpty, numTrimming); }