blob: e41de4d5d3c335c6642b23788ac7ce2c0c73379b [file] [log] [blame]
/**
** 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