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

Commit aaf62b99 authored by Tim Murray's avatar Tim Murray Committed by Android (Google) Code Review
Browse files

Merge "psi support for low memory detection inside ActivityManagerService" into qt-dev

parents f037899d 676d42d9
Loading
Loading
Loading
Loading
+22 −15
Original line number Diff line number Diff line
@@ -544,6 +544,7 @@ public class ActivityManagerService extends IActivityManager.Stub
    private static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds;
    final OomAdjuster mOomAdjuster;
    final LowMemDetector mLowMemDetector;
    /** All system services */
    SystemServiceManager mSystemServiceManager;
@@ -2294,6 +2295,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                ? new ActivityManagerConstants(mContext, this, mHandler) : null;
        final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */);
        mProcessList.init(this, activeUids);
        mLowMemDetector = null;
        mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
        mIntentFirewall = hasHandlerThread
@@ -2342,6 +2344,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        mConstants = new ActivityManagerConstants(mContext, this, mHandler);
        final ActiveUids activeUids = new ActiveUids(this, true /* postChangesToAtm */);
        mProcessList.init(this, activeUids);
        mLowMemDetector = new LowMemDetector(this);
        mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
        // Broadcast policy parameters
@@ -16524,16 +16527,19 @@ public class ActivityManagerService extends IActivityManager.Stub
    final boolean updateLowMemStateLocked(int numCached, int numEmpty, int numTrimming) {
        final int N = mProcessList.getLruSizeLocked();
        final long now = SystemClock.uptimeMillis();
        int memFactor;
        if (mLowMemDetector != null && mLowMemDetector.isAvailable()) {
            memFactor = mLowMemDetector.getMemFactor();
        } else {
            // Now determine the memory trimming level of background processes.
            // Unfortunately we need to start at the back of the list to do this
            // properly.  We only do this if the number of background apps we
            // are managing to keep around is less than half the maximum we desire;
            // if we are keeping a good number around, we'll let them use whatever
            // memory they want.
        final int numCachedAndEmpty = numCached + numEmpty;
        int memFactor;
            if (numCached <= mConstants.CUR_TRIM_CACHED_PROCESSES
                && numEmpty <= mConstants.CUR_TRIM_EMPTY_PROCESSES) {
                final int numCachedAndEmpty = numCached + numEmpty;
                if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
                    memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
                } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
@@ -16544,6 +16550,7 @@ public class ActivityManagerService extends IActivityManager.Stub
            } else {
                memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
            }
        }
        // We always allow the memory level to go up (better).  We only allow it to go
        // down if we are in a state where that is allowed, *and* the total number of processes
        // has gone down since last time.
+92 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.am;

import com.android.internal.annotations.GuardedBy;

/**
 * Detects low memory using PSI.
 *
 * If the kernel doesn't support PSI, then this class is not available.
 */
public final class LowMemDetector {
    private static final String TAG = "LowMemDetector";
    private final ActivityManagerService mAm;
    private final LowMemThread mLowMemThread;
    private boolean mAvailable;

    private final Object mPressureStateLock = new Object();

    @GuardedBy("mPressureStateLock")
    private int mPressureState = MEM_PRESSURE_NONE;

    /* getPressureState return values */
    public static final int MEM_PRESSURE_NONE = 0;
    public static final int MEM_PRESSURE_LOW = 1;
    public static final int MEM_PRESSURE_MEDIUM = 2;
    public static final int MEM_PRESSURE_HIGH = 3;

    LowMemDetector(ActivityManagerService am) {
        mAm = am;
        mLowMemThread = new LowMemThread();
        if (init() != 0) {
            mAvailable = false;
        } else {
            mAvailable = true;
            mLowMemThread.start();
        }
    }

    public boolean isAvailable() {
        return mAvailable;
    }

    /**
     * Returns the current mem factor.
     * Note that getMemFactor returns LowMemDetector.MEM_PRESSURE_XXX
     * which match ProcessStats.ADJ_MEM_FACTOR_XXX values. If they deviate
     * there should be conversion performed here to translate pressure state
     * into memFactor.
     */
    public int getMemFactor() {
        synchronized (mPressureStateLock) {
            return mPressureState;
        }
    }

    private native int init();
    private native int waitForPressure();

    private final class LowMemThread extends Thread {
        public void run() {

            while (true) {
                // sleep waiting for a PSI event
                int newPressureState = waitForPressure();
                if (newPressureState == -1) {
                    // epoll broke, tear this down
                    mAvailable = false;
                    break;
                }
                // got a PSI event? let's update lowmem info
                synchronized (mPressureStateLock) {
                    mPressureState = newPressureState;
                }
            }
        }
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ cc_library_static {
        "com_android_server_PersistentDataBlockService.cpp",
        "com_android_server_GraphicsStatsService.cpp",
        "com_android_server_am_AppCompactor.cpp",
        "com_android_server_am_LowMemDetector.cpp",
        "onload.cpp",
        ":lib_networkStatsFactory_native",
    ],
@@ -104,6 +105,7 @@ cc_defaults {
        "libbpf_android",
        "libnetdbpf",
        "libnetdutils",
        "libpsi",
        "android.hardware.audio.common@2.0",
        "android.hardware.broadcastradio@1.0",
        "android.hardware.broadcastradio@1.1",
+156 −0
Original line number Diff line number Diff line
/**
** Copyright 2019, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#define LOG_TAG "LowMemDetector"

#include <errno.h>
#include <psi/psi.h>
#include <string.h>
#include <sys/epoll.h>

#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include <utils/Log.h>

namespace android {

enum pressure_levels {
    PRESSURE_NONE,
    PRESSURE_LOW,
    PRESSURE_MEDIUM,
    PRESSURE_HIGH,
    PRESSURE_LEVEL_COUNT = PRESSURE_HIGH
};

// amount of stall in us for each level
static constexpr int PSI_LOW_STALL_US = 15000;
static constexpr int PSI_MEDIUM_STALL_US = 30000;
static constexpr int PSI_HIGH_STALL_US = 50000;

// stall tracking window size in us
static constexpr int PSI_WINDOW_SIZE_US = 1000000;

static int psi_epollfd = -1;

static jint android_server_am_LowMemDetector_init(JNIEnv*, jobject) {
    int epollfd;
    int low_psi_fd;
    int medium_psi_fd;
    int high_psi_fd;

    epollfd = epoll_create(PRESSURE_LEVEL_COUNT);
    if (epollfd == -1) {
        ALOGE("epoll_create failed: %s", strerror(errno));
        return -1;
    }

    low_psi_fd = init_psi_monitor(PSI_SOME, PSI_LOW_STALL_US, PSI_WINDOW_SIZE_US);
    if (low_psi_fd < 0 ||
        register_psi_monitor(epollfd, low_psi_fd, (void*)PRESSURE_LOW) != 0) {
        goto low_fail;
    }

    medium_psi_fd =
        init_psi_monitor(PSI_FULL, PSI_MEDIUM_STALL_US, PSI_WINDOW_SIZE_US);
    if (medium_psi_fd < 0 || register_psi_monitor(epollfd, medium_psi_fd,
                                                  (void*)PRESSURE_MEDIUM) != 0) {
        goto medium_fail;
    }

    high_psi_fd =
        init_psi_monitor(PSI_FULL, PSI_HIGH_STALL_US, PSI_WINDOW_SIZE_US);
    if (high_psi_fd < 0 ||
        register_psi_monitor(epollfd, high_psi_fd, (void*)PRESSURE_HIGH) != 0) {
        goto high_fail;
    }

    psi_epollfd = epollfd;
    return 0;

high_fail:
    unregister_psi_monitor(epollfd, medium_psi_fd);
medium_fail:
    unregister_psi_monitor(epollfd, low_psi_fd);
low_fail:
    ALOGE("Failed to register psi trigger");
    close(epollfd);
    return -1;
}

static jint android_server_am_LowMemDetector_waitForPressure(JNIEnv*, jobject) {
    static uint32_t pressure_level = PRESSURE_NONE;
    struct epoll_event events[PRESSURE_LEVEL_COUNT];
    int nevents = 0;

    if (psi_epollfd < 0) {
        ALOGE("Memory pressure detector is not initialized");
        return -1;
    }

    do {
        if (pressure_level == PRESSURE_NONE) {
            /* Wait for events with no timeout */
            nevents = epoll_wait(psi_epollfd, events, PRESSURE_LEVEL_COUNT, -1);
        } else {
            // This is simpler than lmkd. Assume that the memory pressure
            // state will stay high for at least 1s. Within that 1s window,
            // the memory pressure state can go up due to a different FD
            // becoming available or it can go down when that window expires.
            // Accordingly, there's no polling: just epoll_wait with a 1s timeout.
            nevents = epoll_wait(psi_epollfd, events, PRESSURE_LEVEL_COUNT, 1000);
            if (nevents == 0) {
                pressure_level = PRESSURE_NONE;
                return pressure_level;
            }
        }
        // keep waiting if interrupted
    } while (nevents == -1 && errno == EINTR);

    if (nevents == -1) {
        ALOGE("epoll_wait failed while waiting for psi events: %s", strerror(errno));
        return -1;
    }

    // reset pressure_level and raise it based on received events
    pressure_level = PRESSURE_NONE;
    for (int i = 0; i < nevents; i++) {
        if (events[i].events & (EPOLLERR | EPOLLHUP)) {
            // should never happen unless psi got disabled in kernel
            ALOGE("Memory pressure events are not available anymore");
            return -1;
        }
        // record the highest reported level
        if (events[i].data.u32 > pressure_level) {
            pressure_level = events[i].data.u32;
        }
    }

    return pressure_level;
}

static const JNINativeMethod sMethods[] = {
    /* name, signature, funcPtr */
    {"init", "()I", (void*)android_server_am_LowMemDetector_init},
    {"waitForPressure", "()I",
     (void*)android_server_am_LowMemDetector_waitForPressure},
};

int register_android_server_am_LowMemDetector(JNIEnv* env) {
    return jniRegisterNativeMethods(env, "com/android/server/am/LowMemDetector",
                                    sMethods, NELEM(sMethods));
}

} // namespace android
+2 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ int register_android_server_net_NetworkStatsFactory(JNIEnv* env);
int register_android_server_net_NetworkStatsService(JNIEnv* env);
int register_android_server_security_VerityUtils(JNIEnv* env);
int register_android_server_am_AppCompactor(JNIEnv* env);
int register_android_server_am_LowMemDetector(JNIEnv* env);
};

using namespace android;
@@ -105,5 +106,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
    register_android_server_net_NetworkStatsService(env);
    register_android_server_security_VerityUtils(env);
    register_android_server_am_AppCompactor(env);
    register_android_server_am_LowMemDetector(env);
    return JNI_VERSION_1_4;
}
Loading