psi support for low memory detection inside ActivityManagerService
Add LowMemDetector class and use it in ActivityManagerService to detect memory conditions based on in-kernel psi monitors. Test: boots, detects low memory Bug: 129476847 Change-Id: I7a05ba36196d24ce5147b97c076e38b92dc71e2e Signed-off-by: Tim Murray <timmurray@google.com> Signed-off-by: Suren Baghdasaryan <surenb@google.com>
This commit is contained in:
@@ -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;
|
||||
@@ -2296,6 +2297,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
|
||||
@@ -2344,6 +2346,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
|
||||
@@ -16523,25 +16526,29 @@ 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();
|
||||
// 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) {
|
||||
if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
|
||||
memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
|
||||
} else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
|
||||
memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
|
||||
} else {
|
||||
memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
|
||||
}
|
||||
if (mLowMemDetector != null && mLowMemDetector.isAvailable()) {
|
||||
memFactor = mLowMemDetector.getMemFactor();
|
||||
} else {
|
||||
memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
|
||||
// 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.
|
||||
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) {
|
||||
memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
|
||||
} else {
|
||||
memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
|
||||
}
|
||||
} 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
|
||||
|
||||
92
services/core/java/com/android/server/am/LowMemDetector.java
Normal file
92
services/core/java/com/android/server/am/LowMemDetector.java
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
services/core/jni/com_android_server_am_LowMemDetector.cpp
Normal file
156
services/core/jni/com_android_server_am_LowMemDetector.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
** 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ android_test {
|
||||
|
||||
jni_libs: [
|
||||
"libdexmakerjvmtiagent",
|
||||
"libpsi",
|
||||
"libstaticjvmtiagent",
|
||||
],
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ android_test {
|
||||
"liblog",
|
||||
"liblzma",
|
||||
"libnativehelper",
|
||||
"libpsi",
|
||||
"libui",
|
||||
"libunwindstack",
|
||||
"libutils",
|
||||
|
||||
Reference in New Issue
Block a user