ronnie
2022-10-14 1504bb53e29d3d46222c0b3ea994fc494b48e153
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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