Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4ec6b89b authored by T.J. Mercier's avatar T.J. Mercier
Browse files

CachedAppOptimizer: Fallback to process_madvise when memcg doesn't support compaction

Compaction has 3 profiles: FULL, SOME (file pages), and ANON

Even if a kernel has a memory.reclaim memcg interface, it may not
support SOME or ANON profiles if it does not have [1]. The
isProfileValidForProcess function checks for kernel support based on
the profile and works correctly, but it's only called after we have
already committed to using memcg reclaim. So if it returns false then we
don't perform compaction of any kind.

Add a check that verifies memcg supports the requested compaction
profile, and fallback to process_madvise if not.

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=68cd9050d871e4db5433420b5ceb32f5512d18bc

Bug: 377479218
Flag: com.android.server.am.use_memcg_for_compaction
Test: atest com.android.server.am.CachedAppOptimizerTest
Change-Id: I984bc6af5549a7fbaa4d4acacdcc436dc57c81d1
parent e2b04d2e
Loading
Loading
Loading
Loading
+30 −12
Original line number Diff line number Diff line
@@ -98,6 +98,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
@@ -755,6 +756,7 @@ public class CachedAppOptimizer {
    private static native void compactProcess(int pid, int compactionFlags);
    private static native void performNativeMemcgCompaction(int uid, int pid, int compactionFlags);
    private static native void compactNativeProcess(int pid, int compactionFlags);
    private static native boolean compactionFlagsValidForMemcg(int compactionFlags);

    static private native void cancelCompaction();

@@ -1539,6 +1541,17 @@ public class CachedAppOptimizer {
        }
    }

    private static int getCompactionFlags(CompactProfile profile) {
        if (profile == CompactProfile.FULL) {
            return COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG;
        } else if (profile == CompactProfile.SOME) {
            return COMPACT_ACTION_FILE_FLAG;
        } else if (profile == CompactProfile.ANON) {
            return COMPACT_ACTION_ANON_FLAG;
        }
        return 0;
    }

    private final class MemCompactionHandler extends Handler {
        private MemCompactionHandler() {
            super(mCachedAppOptimizerThread.getLooper());
@@ -1671,6 +1684,21 @@ public class CachedAppOptimizer {
            return false;
        }

        private EnumMap<CompactProfile, Boolean> mProfileValidForMemcgMap =
                new EnumMap<>(CompactProfile.class);

        private boolean profileValidForMemcg(CompactProfile profile) {
            Boolean valid = mProfileValidForMemcgMap.get(profile);

            if (valid == null) {
                // Use JNI only once
                valid = new Boolean(compactionFlagsValidForMemcg(getCompactionFlags(profile)));
                mProfileValidForMemcgMap.put(profile, valid);
            }

            return valid.booleanValue();
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
@@ -1776,7 +1804,8 @@ public class CachedAppOptimizer {
                        long zramUsedKbBefore = getUsedZramMemory();
                        long startCpuTime = threadCpuTimeNs();

                        if (Flags.useMemcgForCompaction()) {
                        if (Flags.useMemcgForCompaction() &&
                                profileValidForMemcg(resolvedProfile)) {
                            mProcessDependencies.performMemcgCompaction(resolvedProfile, uid, pid);
                        } else {
                            mProcessDependencies.performCompaction(resolvedProfile, pid);
@@ -2252,17 +2281,6 @@ public class CachedAppOptimizer {
            compactNativeProcess(pid, compactionFlags);
            mPidCompacting = -1;
        }

        private static int getCompactionFlags(CompactProfile profile) {
            if (profile == CompactProfile.FULL) {
                return COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG;
            } else if (profile == CompactProfile.SOME) {
                return COMPACT_ACTION_FILE_FLAG;
            } else if (profile == CompactProfile.ANON) {
                return COMPACT_ACTION_ANON_FLAG;
            }
            return 0;
        }
    }

    static int getUnfreezeReasonCodeFromOomAdjReason(@OomAdjReason int oomAdjReason) {
+32 −3
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@
#include <utils/Trace.h>

#include <algorithm>
#include <optional>

using android::base::StringPrintf;
using android::base::WriteStringToFile;
@@ -429,11 +430,11 @@ static void compactProcess(int pid, int compactionFlags) {
    compactProcess(pid, vmaToAdviseFunc);
}

static void compactMemcg(int uid, int pid, int compactionFlags) {
static std::string profileFromCompactionFlags(int compactionFlags) {
    const bool compactAnon = compactionFlags & COMPACT_ACTION_ANON_FLAG;
    const bool compactFile = compactionFlags & COMPACT_ACTION_FILE_FLAG;

    if (!compactAnon && !compactFile) return;
    if (!compactAnon && !compactFile) return {};
    std::string profile;
    if (compactAnon && compactFile)
        profile = "CompactFull";
@@ -442,7 +443,11 @@ static void compactMemcg(int uid, int pid, int compactionFlags) {
    else if (compactFile)
        profile = "CompactFile";

    if (isProfileValidForProcess(profile, uid, pid)) {
    return profile;
}

static void compactMemcg(int uid, int pid, int compactionFlags) {
    if (std::string profile = profileFromCompactionFlags(compactionFlags); !profile.empty()) {
        SetProcessProfiles(uid, pid, {profile});
    }
}
@@ -529,6 +534,28 @@ static void com_android_server_am_CachedAppOptimizer_compactNativeProcess(JNIEnv
    compactProcess(pid, compactionFlags);
}

static jboolean com_android_server_am_CachedAppOptimizer_compactionFlagsValidForMemcg(
        JNIEnv* env, jobject, jint compactionFlags) {
    static std::array<std::optional<bool>, 3> valid;

    if (compactionFlags >= valid.size() || compactionFlags < 0) {
        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid compaction flags");
        return false;
    }

    if (!valid[compactionFlags]) {
        std::string profile = profileFromCompactionFlags(compactionFlags);
        if (profile.empty()) {
            valid[compactionFlags] = true; // NONE is a no-op
        } else {
            // Only call this once per flag combo, per boot, since it's not exactly cheap
            valid[compactionFlags] = isProfileValidForProcess(profile, getuid(), getpid());
        }
    }

    return *valid[compactionFlags];
}

static const JNINativeMethod sMethods[] = {
        /* name, signature, funcPtr */
        {"cancelCompaction", "()V",
@@ -547,6 +574,8 @@ static const JNINativeMethod sMethods[] = {
         (void*)com_android_server_am_CachedAppOptimizer_compactProcessWithMemcg},
        {"compactNativeProcess", "(II)V",
         (void*)com_android_server_am_CachedAppOptimizer_compactNativeProcess},
        {"compactionFlagsValidForMemcg", "(I)Z",
         (void*)com_android_server_am_CachedAppOptimizer_compactionFlagsValidForMemcg},
};

int register_android_server_am_CachedAppOptimizer(JNIEnv* env)