From 84d812ffb4d5ed049137afb9aa19f4de17ec8e7d Mon Sep 17 00:00:00 2001 From: Kalesh Singh Date: Thu, 10 Nov 2022 16:00:00 -0800 Subject: [PATCH 1/3] 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); } From b3be3d5bea85f5722ba16492ee446df9f310a5a9 Mon Sep 17 00:00:00 2001 From: Kalesh Singh Date: Thu, 10 Nov 2022 16:53:32 -0800 Subject: [PATCH 2/3] AMS: Add Phenotype key for proactive kills KEY_PROACTIVE_KILLS_ENABLED controls whether proactive kills are enabled or not. Bug: 249601646 Test: adb shell device_config put activity_manager proactive_kills_enabled true Test: adb shell device_config get activity_manager proactive_kills_enabled Change-Id: I173058d77e18294a882714591dcea96dae06c12f Signed-off-by: Kalesh Singh --- .../server/am/ActivityManagerConstants.java | 20 +++++++++++++++++++ .../com/android/server/am/OomAdjuster.java | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 16fe121fecb77..4f207a76a4d5b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -248,6 +248,11 @@ final class ActivityManagerConstants extends ContentObserver { */ private static final String KEY_MAX_PHANTOM_PROCESSES = "max_phantom_processes"; + /** + * Enables proactive killing of cached apps + */ + private static final String KEY_PROACTIVE_KILLS_ENABLED = "proactive_kills_enabled"; + /** * Default value for mFlagBackgroundActivityStartsEnabled if not explicitly set in * Settings.Global. This allows it to be set experimentally unless it has been @@ -874,6 +879,8 @@ final class ActivityManagerConstants extends ContentObserver { */ private static final long DEFAULT_MIN_ASSOC_LOG_DURATION = 5 * 60 * 1000; // 5 mins + private static final boolean DEFAULT_PROACTIVE_KILLS_ENABLED = false; + private static final String KEY_MIN_ASSOC_LOG_DURATION = "min_assoc_log_duration"; public static long MIN_ASSOC_LOG_DURATION = DEFAULT_MIN_ASSOC_LOG_DURATION; @@ -904,6 +911,7 @@ final class ActivityManagerConstants extends ContentObserver { public static boolean BINDER_HEAVY_HITTER_AUTO_SAMPLER_ENABLED; public static int BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE; public static float BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD; + public static boolean PROACTIVE_KILLS_ENABLED = DEFAULT_PROACTIVE_KILLS_ENABLED; private final OnPropertiesChangedListener mOnDeviceConfigChangedListener = new OnPropertiesChangedListener() { @@ -1040,6 +1048,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_MAX_SERVICE_CONNECTIONS_PER_PROCESS: updateMaxServiceConnectionsPerProcess(); break; + case KEY_PROACTIVE_KILLS_ENABLED: + updateProactiveKillsEnabled(); + break; default: break; } @@ -1660,6 +1671,13 @@ final class ActivityManagerConstants extends ContentObserver { CUR_TRIM_CACHED_PROCESSES = (MAX_CACHED_PROCESSES-rawMaxEmptyProcesses)/3; } + private void updateProactiveKillsEnabled() { + PROACTIVE_KILLS_ENABLED = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_PROACTIVE_KILLS_ENABLED, + DEFAULT_PROACTIVE_KILLS_ENABLED); + } + private void updateMinAssocLogDuration() { MIN_ASSOC_LOG_DURATION = DeviceConfig.getLong( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_MIN_ASSOC_LOG_DURATION, @@ -1860,6 +1878,8 @@ final class ActivityManagerConstants extends ContentObserver { pw.print("="); pw.println(mNetworkAccessTimeoutMs); pw.print(" "); pw.print(KEY_MAX_SERVICE_CONNECTIONS_PER_PROCESS); pw.print("="); pw.println(mMaxServiceConnectionsPerProcess); + pw.print(" "); pw.print(KEY_PROACTIVE_KILLS_ENABLED); + pw.print("="); pw.println(PROACTIVE_KILLS_ENABLED); pw.println(); if (mOverrideMaxCachedProcesses >= 0) { diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 3c0fd8f5e4965..96e6ace5c56f2 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1148,7 +1148,7 @@ public class OomAdjuster { int numEmpty = 0; int numTrimming = 0; - boolean proactiveKillsEnabled = false; // TODO: Configure from phenotype + boolean proactiveKillsEnabled = mConstants.PROACTIVE_KILLS_ENABLED; double lowSwapThresholdPercent = 0.10; // TODO: Configure from phenotype double freeSwapPercent = proactiveKillsEnabled ? getFreeSwapPercent() : 1.00; ProcessRecord lruCachedApp = null; From 09138ccf3d11987a358853723dcfa87cc378e1d0 Mon Sep 17 00:00:00 2001 From: Kalesh Singh Date: Fri, 11 Nov 2022 11:48:01 -0800 Subject: [PATCH 3/3] AMS: Add Phenotype key for low swap threshold KEY_LOW_SWAP_THRESHOLD_PERCENT controls the minimum percentage of free swap imposed by proactive kills. Bug: 249601646 Test: adb shell device_config put activity_manager low_swap_threshold_percent 0.50 Test: adb shell device_config get activity_manager low_swap_threshold_percent Change-Id: Ieadd31efc677b43ce008a3b26726b565e9f497ab Signed-off-by: Kalesh Singh --- .../server/am/ActivityManagerConstants.java | 22 +++++++++++++++++++ .../com/android/server/am/OomAdjuster.java | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 4f207a76a4d5b..003f7f0d88fb7 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -253,6 +253,13 @@ final class ActivityManagerConstants extends ContentObserver { */ private static final String KEY_PROACTIVE_KILLS_ENABLED = "proactive_kills_enabled"; + /** + * Trim LRU cached app when swap falls below this minimum percentage. + * + * Depends on KEY_PROACTIVE_KILLS_ENABLED + */ + private static final String KEY_LOW_SWAP_THRESHOLD_PERCENT = "low_swap_threshold_percent"; + /** * Default value for mFlagBackgroundActivityStartsEnabled if not explicitly set in * Settings.Global. This allows it to be set experimentally unless it has been @@ -881,6 +888,8 @@ final class ActivityManagerConstants extends ContentObserver { private static final boolean DEFAULT_PROACTIVE_KILLS_ENABLED = false; + private static final float DEFAULT_LOW_SWAP_THRESHOLD_PERCENT = 0.10f; + private static final String KEY_MIN_ASSOC_LOG_DURATION = "min_assoc_log_duration"; public static long MIN_ASSOC_LOG_DURATION = DEFAULT_MIN_ASSOC_LOG_DURATION; @@ -912,6 +921,7 @@ final class ActivityManagerConstants extends ContentObserver { public static int BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE; public static float BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD; public static boolean PROACTIVE_KILLS_ENABLED = DEFAULT_PROACTIVE_KILLS_ENABLED; + public static float LOW_SWAP_THRESHOLD_PERCENT = DEFAULT_LOW_SWAP_THRESHOLD_PERCENT; private final OnPropertiesChangedListener mOnDeviceConfigChangedListener = new OnPropertiesChangedListener() { @@ -1051,6 +1061,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_PROACTIVE_KILLS_ENABLED: updateProactiveKillsEnabled(); break; + case KEY_LOW_SWAP_THRESHOLD_PERCENT: + updateLowSwapThresholdPercent(); + break; default: break; } @@ -1678,6 +1691,13 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_PROACTIVE_KILLS_ENABLED); } + private void updateLowSwapThresholdPercent() { + LOW_SWAP_THRESHOLD_PERCENT = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_LOW_SWAP_THRESHOLD_PERCENT, + DEFAULT_LOW_SWAP_THRESHOLD_PERCENT); + } + private void updateMinAssocLogDuration() { MIN_ASSOC_LOG_DURATION = DeviceConfig.getLong( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_MIN_ASSOC_LOG_DURATION, @@ -1880,6 +1900,8 @@ final class ActivityManagerConstants extends ContentObserver { pw.print("="); pw.println(mMaxServiceConnectionsPerProcess); pw.print(" "); pw.print(KEY_PROACTIVE_KILLS_ENABLED); pw.print("="); pw.println(PROACTIVE_KILLS_ENABLED); + pw.print(" "); pw.print(KEY_LOW_SWAP_THRESHOLD_PERCENT); + pw.print("="); pw.println(LOW_SWAP_THRESHOLD_PERCENT); pw.println(); if (mOverrideMaxCachedProcesses >= 0) { diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 96e6ace5c56f2..eb2b7d493251e 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1149,7 +1149,7 @@ public class OomAdjuster { int numTrimming = 0; boolean proactiveKillsEnabled = mConstants.PROACTIVE_KILLS_ENABLED; - double lowSwapThresholdPercent = 0.10; // TODO: Configure from phenotype + double lowSwapThresholdPercent = mConstants.LOW_SWAP_THRESHOLD_PERCENT; double freeSwapPercent = proactiveKillsEnabled ? getFreeSwapPercent() : 1.00; ProcessRecord lruCachedApp = null;