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