1 /**
2 ** Copyright 2019, The Android Open Source Project
3 **
4 ** Licensed under the Apache License, Version 2.0 (the "License");
5 ** you may not use this file except in compliance with the License.
6 ** You may obtain a copy of the License at
7 **
8 ** http://www.apache.org/licenses/LICENSE-2.0
9 **
10 ** Unless required by applicable law or agreed to in writing, software
11 ** distributed under the License is distributed on an "AS IS" BASIS,
12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 ** See the License for the specific language governing permissions and
14 ** limitations under the License.
15 */
16
17 #define LOG_TAG "LowMemDetector"
18
19 #include <errno.h>
20 #include <psi/psi.h>
21 #include <string.h>
22 #include <sys/epoll.h>
23
24 #include <jni.h>
25 #include <nativehelper/JNIHelp.h>
26 #include <utils/Log.h>
27
28 namespace android {
29
30 enum pressure_levels {
31 PRESSURE_NONE,
32 PRESSURE_LOW,
33 PRESSURE_MEDIUM,
34 PRESSURE_HIGH,
35 PRESSURE_LEVEL_COUNT = PRESSURE_HIGH
36 };
37
38 // amount of stall in us for each level
39 static constexpr int PSI_LOW_STALL_US = 15000;
40 static constexpr int PSI_MEDIUM_STALL_US = 30000;
41 static constexpr int PSI_HIGH_STALL_US = 50000;
42
43 // stall tracking window size in us
44 static constexpr int PSI_WINDOW_SIZE_US = 1000000;
45
46 static int psi_epollfd = -1;
47
android_server_am_LowMemDetector_init(JNIEnv *,jobject)48 static jint android_server_am_LowMemDetector_init(JNIEnv*, jobject) {
49 int epollfd;
50 int low_psi_fd;
51 int medium_psi_fd;
52 int high_psi_fd;
53
54 epollfd = epoll_create(PRESSURE_LEVEL_COUNT);
55 if (epollfd == -1) {
56 ALOGE("epoll_create failed: %s", strerror(errno));
57 return -1;
58 }
59
60 low_psi_fd = init_psi_monitor(PSI_SOME, PSI_LOW_STALL_US, PSI_WINDOW_SIZE_US);
61 if (low_psi_fd < 0 ||
62 register_psi_monitor(epollfd, low_psi_fd, (void*)PRESSURE_LOW) != 0) {
63 goto low_fail;
64 }
65
66 medium_psi_fd =
67 init_psi_monitor(PSI_FULL, PSI_MEDIUM_STALL_US, PSI_WINDOW_SIZE_US);
68 if (medium_psi_fd < 0 || register_psi_monitor(epollfd, medium_psi_fd,
69 (void*)PRESSURE_MEDIUM) != 0) {
70 goto medium_fail;
71 }
72
73 high_psi_fd =
74 init_psi_monitor(PSI_FULL, PSI_HIGH_STALL_US, PSI_WINDOW_SIZE_US);
75 if (high_psi_fd < 0 ||
76 register_psi_monitor(epollfd, high_psi_fd, (void*)PRESSURE_HIGH) != 0) {
77 goto high_fail;
78 }
79
80 psi_epollfd = epollfd;
81 return 0;
82
83 high_fail:
84 unregister_psi_monitor(epollfd, medium_psi_fd);
85 medium_fail:
86 unregister_psi_monitor(epollfd, low_psi_fd);
87 low_fail:
88 ALOGE("Failed to register psi trigger");
89 close(epollfd);
90 return -1;
91 }
92
android_server_am_LowMemDetector_waitForPressure(JNIEnv *,jobject)93 static jint android_server_am_LowMemDetector_waitForPressure(JNIEnv*, jobject) {
94 static uint32_t pressure_level = PRESSURE_NONE;
95 struct epoll_event events[PRESSURE_LEVEL_COUNT];
96 int nevents = 0;
97
98 if (psi_epollfd < 0) {
99 ALOGE("Memory pressure detector is not initialized");
100 return -1;
101 }
102
103 do {
104 if (pressure_level == PRESSURE_NONE) {
105 /* Wait for events with no timeout */
106 nevents = epoll_wait(psi_epollfd, events, PRESSURE_LEVEL_COUNT, -1);
107 } else {
108 // This is simpler than lmkd. Assume that the memory pressure
109 // state will stay high for at least 1s. Within that 1s window,
110 // the memory pressure state can go up due to a different FD
111 // becoming available or it can go down when that window expires.
112 // Accordingly, there's no polling: just epoll_wait with a 1s timeout.
113 nevents = epoll_wait(psi_epollfd, events, PRESSURE_LEVEL_COUNT, 1000);
114 if (nevents == 0) {
115 pressure_level = PRESSURE_NONE;
116 return pressure_level;
117 }
118 }
119 // keep waiting if interrupted
120 } while (nevents == -1 && errno == EINTR);
121
122 if (nevents == -1) {
123 ALOGE("epoll_wait failed while waiting for psi events: %s", strerror(errno));
124 return -1;
125 }
126
127 // reset pressure_level and raise it based on received events
128 pressure_level = PRESSURE_NONE;
129 for (int i = 0; i < nevents; i++) {
130 if (events[i].events & (EPOLLERR | EPOLLHUP)) {
131 // should never happen unless psi got disabled in kernel
132 ALOGE("Memory pressure events are not available anymore");
133 return -1;
134 }
135 // record the highest reported level
136 if (events[i].data.u32 > pressure_level) {
137 pressure_level = events[i].data.u32;
138 }
139 }
140
141 return pressure_level;
142 }
143
144 static const JNINativeMethod sMethods[] = {
145 /* name, signature, funcPtr */
146 {"init", "()I", (void*)android_server_am_LowMemDetector_init},
147 {"waitForPressure", "()I",
148 (void*)android_server_am_LowMemDetector_waitForPressure},
149 };
150
register_android_server_am_LowMemDetector(JNIEnv * env)151 int register_android_server_am_LowMemDetector(JNIEnv* env) {
152 return jniRegisterNativeMethods(env, "com/android/server/am/LowMemDetector",
153 sMethods, NELEM(sMethods));
154 }
155
156 } // namespace android
157