From 29a3a5b1f45dcebadacf3715fa3b7d6593f6159b Mon Sep 17 00:00:00 2001 From: Edgar Arriaga Date: Wed, 15 Dec 2021 18:14:28 -0800 Subject: [PATCH] Cancel compaction if the process improves its OOM ranking while compaction is running There is a significant delay between the time we issue a compaction and when its done during this time the OOM adjust sometimes changes, however, the current compaction still keeps running, which leads to situations where proc A starts running while compaction for proc A is happening leading to long delays as compaction grabs the mmap_lock thus not allowing for allocations to happen during that time So the aim of this CL is to reduce the likelihood of such situations by avoiding continuing compaction when we detect such OOM adjust improvement outside of cache state. Note that we do not kill the currently running process_madvise, instead we avoid issuing new process_madvise calls so this will likely improve situations where an app has many VMAs. Test: Manual. Checked that JNI callback happens and that OOM adjusts match as well as checked that compaction is cancelled when the improvement in OOM happens. Bug: 208966958 Change-Id: I202a96d884e2a0dc7ac7013eca91a36cd58acc80 --- .../android/server/am/CachedAppOptimizer.java | 33 +++++++++++++++++++ .../com/android/server/am/OomAdjuster.java | 13 ++------ ...m_android_server_am_CachedAppOptimizer.cpp | 22 +++++++++++++ 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 0c94fbbcf74f1..0c9187564bcf2 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -539,6 +539,8 @@ public final class CachedAppOptimizer { */ static private native void compactProcess(int pid, int compactionFlags); + static private native void cancelCompaction(); + /** * Reads the flag value from DeviceConfig to determine whether app compaction * should be enabled, and starts the freeze/compaction thread if needed. @@ -1049,6 +1051,26 @@ public final class CachedAppOptimizer { } } + @GuardedBy({"mService", "mProcLock"}) + void onOomAdjustChanged(int oldAdj, int newAdj, ProcessRecord app) { + // Cancel any currently executing compactions + // if the process moved out of cached state + if (DefaultProcessDependencies.mPidCompacting == app.mPid && newAdj < oldAdj + && newAdj < ProcessList.CACHED_APP_MIN_ADJ) { + cancelCompaction(); + } + + // Perform a minor compaction when a perceptible app becomes the prev/home app + // Perform a major compaction when any app enters cached + if (oldAdj <= ProcessList.PERCEPTIBLE_APP_ADJ + && (newAdj == ProcessList.PREVIOUS_APP_ADJ || newAdj == ProcessList.HOME_APP_ADJ)) { + compactAppSome(app); + } else if (newAdj >= ProcessList.CACHED_APP_MIN_ADJ + && newAdj <= ProcessList.CACHED_APP_MAX_ADJ) { + compactAppFull(app); + } + } + @VisibleForTesting static final class LastCompactionStats { private final long[] mRssAfterCompaction; @@ -1091,6 +1113,13 @@ public final class CachedAppOptimizer { name = proc.processName; opt.setHasPendingCompact(false); + if (mAm.mInternal.isPendingTopUid(proc.uid)) { + // In case the OOM Adjust has not yet been propagated we see if this is + // pending on becoming top app in which case we should not compact. + Slog.e(TAG_AM, "Skip compaction since UID is active for " + name); + return; + } + // don't compact if the process has returned to perceptible // and this is only a cached/home/prev compaction if ((pendingAction == COMPACT_PROCESS_SOME @@ -1500,6 +1529,8 @@ public final class CachedAppOptimizer { * Default implementation for ProcessDependencies, public vor visibility to OomAdjuster class. */ private static final class DefaultProcessDependencies implements ProcessDependencies { + public static int mPidCompacting = -1; + // Get memory RSS from process. @Override public long[] getRss(int pid) { @@ -1509,6 +1540,7 @@ public final class CachedAppOptimizer { // Compact process. @Override public void performCompaction(String action, int pid) throws IOException { + mPidCompacting = pid; if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])) { compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG); } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FILE])) { @@ -1516,6 +1548,7 @@ public final class CachedAppOptimizer { } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) { compactProcess(pid, COMPACT_ACTION_ANON_FLAG); } + mPidCompacting = -1; } } } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 47e24b1cd7d83..174984f398c4d 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -2486,18 +2486,9 @@ public class OomAdjuster { // don't compact during bootup if (mCachedAppOptimizer.useCompaction() && mService.mBooted) { // Cached and prev/home compaction + // reminder: here, setAdj is previous state, curAdj is upcoming state if (state.getCurAdj() != state.getSetAdj()) { - // Perform a minor compaction when a perceptible app becomes the prev/home app - // Perform a major compaction when any app enters cached - // reminder: here, setAdj is previous state, curAdj is upcoming state - if (state.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ - && (state.getCurAdj() == ProcessList.PREVIOUS_APP_ADJ - || state.getCurAdj() == ProcessList.HOME_APP_ADJ)) { - mCachedAppOptimizer.compactAppSome(app); - } else if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ - && state.getCurAdj() <= ProcessList.CACHED_APP_MAX_ADJ) { - mCachedAppOptimizer.compactAppFull(app); - } + mCachedAppOptimizer.onOomAdjustChanged(state.getSetAdj(), state.getCurAdj(), app); } else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE && state.getSetAdj() < ProcessList.FOREGROUND_APP_ADJ // Because these can fire independent of oom_adj/procstate changes, we need diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index 4190a91710fc6..94bc22a05d7ad 100644 --- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -59,6 +59,9 @@ using android::base::unique_fd; namespace android { +static bool cancelRunningCompaction; +static bool compactionInProgress; + // Legacy method for compacting processes, any new code should // use compactProcess instead. static inline void compactProcessProcfs(int pid, const std::string& compactionType) { @@ -83,9 +86,18 @@ static int64_t compactMemory(const std::vector& vmas, int pid, int madviseT // Skip compaction if failed to open pidfd with any error return -errno; } + compactionInProgress = true; + cancelRunningCompaction = false; int64_t totalBytesCompacted = 0; for (int iBase = 0; iBase < vmas.size(); iBase += UIO_MAXIOV) { + if (CC_UNLIKELY(cancelRunningCompaction)) { + // There could be a significant delay betweenwhen a compaction + // is requested and when it is handled during this time + // our OOM adjust could have improved. + cancelRunningCompaction = false; + break; + } int totalVmasToKernel = std::min(UIO_MAXIOV, (int)(vmas.size() - iBase)); for (int iVec = 0, iVma = iBase; iVec < totalVmasToKernel; ++iVec, ++iVma) { vmasToKernel[iVec].iov_base = (void*)vmas[iVma].start; @@ -95,11 +107,13 @@ static int64_t compactMemory(const std::vector& vmas, int pid, int madviseT auto bytesCompacted = process_madvise(pidfd, vmasToKernel, totalVmasToKernel, madviseType, 0); if (CC_UNLIKELY(bytesCompacted == -1)) { + compactionInProgress = false; return -errno; } totalBytesCompacted += bytesCompacted; } + compactionInProgress = false; return totalBytesCompacted; } @@ -228,6 +242,12 @@ static void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, job } } +static void com_android_server_am_CachedAppOptimizer_cancelCompaction(JNIEnv*, jobject) { + if (compactionInProgress) { + cancelRunningCompaction = true; + } +} + static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid, jint compactionFlags) { compactProcessOrFallback(pid, compactionFlags); @@ -279,6 +299,8 @@ static jstring com_android_server_am_CachedAppOptimizer_getFreezerCheckPath(JNIE static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ + {"cancelCompaction", "()V", + (void*)com_android_server_am_CachedAppOptimizer_cancelCompaction}, {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem}, {"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess}, {"freezeBinder", "(IZ)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},