| /** |
| ** 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 |