lmkd: Add property re-initialization support
Add --reinit command-line option to allow updating lmkd properties. For
example to enable debug logging in the running lmkd process user should
issue:
setprop ro.lmk.debug true
lmkd --reinit
Bug: 155149944
Test: lmkd_unit_test after resetting lmkd properties
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
Change-Id: Ic60331f3368f5a7fdfe09ad7d47c7ccf0a497685
diff --git a/lmkd.cpp b/lmkd.cpp
index 3b8a6fa..d022f34 100644
--- a/lmkd.cpp
+++ b/lmkd.cpp
@@ -42,6 +42,7 @@
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
#include <cutils/sockets.h>
+#include <liblmkd_utils.h>
#include <lmkd.h>
#include <log/log.h>
#include <log/log_event_list.h>
@@ -142,6 +143,8 @@
/* ro.lmk.psi_complete_stall_ms property defaults */
#define DEF_COMPLETE_STALL 700
+#define LMKD_REINIT_PROP "lmkd.reinit"
+
static inline int sys_pidfd_open(pid_t pid, unsigned int flags) {
return syscall(__NR_pidfd_open, pid, flags);
}
@@ -526,6 +529,10 @@
/* PAGE_SIZE / 1024 */
static long page_k;
+static void update_props();
+static bool init_monitors();
+static void destroy_monitors();
+
static int clamp(int low, int high, int value) {
return max(min(value, high), low);
}
@@ -1364,6 +1371,7 @@
int nargs;
int targets;
int kill_cnt;
+ int result;
len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE, &cred);
if (len <= 0)
@@ -1419,6 +1427,29 @@
/* This command code is NOT expected at all */
ALOGE("Received unexpected command code %d", cmd);
break;
+ case LMK_UPDATE_PROPS:
+ if (nargs != 0)
+ goto wronglen;
+ update_props();
+ if (!use_inkernel_interface) {
+ /* Reinitialize monitors to apply new settings */
+ destroy_monitors();
+ result = init_monitors() ? 0 : -1;
+ } else {
+ result = 0;
+ }
+ len = lmkd_pack_set_update_props_repl(packet, result);
+ if (ctrl_data_write(dsock_idx, (char *)packet, len) != len) {
+ ALOGE("Failed to report operation results");
+ }
+ if (!result) {
+ ALOGI("Properties reinitilized");
+ } else {
+ /* New settings can't be supported, crash to be restarted */
+ ALOGE("New configuration is not supported. Exiting...");
+ exit(1);
+ }
+ break;
default:
ALOGE("Received unknown command code %d", cmd);
return;
@@ -1923,9 +1954,9 @@
if (pidfd_supported) {
/* unregister fd */
- if (epoll_ctl(epollfd, EPOLL_CTL_DEL, last_kill_pid_or_fd, &epev) != 0) {
+ if (epoll_ctl(epollfd, EPOLL_CTL_DEL, last_kill_pid_or_fd, &epev)) {
+ // Log an error and keep going
ALOGE("epoll_ctl for last killed process failed; errno=%d", errno);
- return;
}
maxevents--;
close(last_kill_pid_or_fd);
@@ -2709,10 +2740,15 @@
static void destroy_mp_psi(enum vmpressure_level level) {
int fd = mpevfd[level];
+ if (fd < 0) {
+ return;
+ }
+
if (unregister_psi_monitor(epollfd, fd) < 0) {
ALOGE("Failed to unregister psi monitor for %s memory pressure; errno=%d",
level_name[level], errno);
}
+ maxevents--;
destroy_psi_monitor(fd);
mpevfd[level] = -1;
}
@@ -2815,11 +2851,60 @@
return false;
}
+static void destroy_mp_common(enum vmpressure_level level) {
+ struct epoll_event epev;
+ int fd = mpevfd[level];
+
+ if (fd < 0) {
+ return;
+ }
+
+ if (epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &epev)) {
+ // Log an error and keep going
+ ALOGE("epoll_ctl for level %s failed; errno=%d", level_name[level], errno);
+ }
+ maxevents--;
+ close(fd);
+ mpevfd[level] = -1;
+}
+
static void kernel_event_handler(int data __unused, uint32_t events __unused,
struct polling_params *poll_params __unused) {
poll_kernel(kpoll_fd);
}
+static bool init_monitors() {
+ /* Try to use psi monitor first if kernel has it */
+ use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
+ init_psi_monitors();
+ /* Fall back to vmpressure */
+ if (!use_psi_monitors &&
+ (!init_mp_common(VMPRESS_LEVEL_LOW) ||
+ !init_mp_common(VMPRESS_LEVEL_MEDIUM) ||
+ !init_mp_common(VMPRESS_LEVEL_CRITICAL))) {
+ ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
+ return false;
+ }
+ if (use_psi_monitors) {
+ ALOGI("Using psi monitors for memory pressure detection");
+ } else {
+ ALOGI("Using vmpressure for memory pressure detection");
+ }
+ return true;
+}
+
+static void destroy_monitors() {
+ if (use_psi_monitors) {
+ destroy_mp_psi(VMPRESS_LEVEL_CRITICAL);
+ destroy_mp_psi(VMPRESS_LEVEL_MEDIUM);
+ destroy_mp_psi(VMPRESS_LEVEL_LOW);
+ } else {
+ destroy_mp_common(VMPRESS_LEVEL_CRITICAL);
+ destroy_mp_common(VMPRESS_LEVEL_MEDIUM);
+ destroy_mp_common(VMPRESS_LEVEL_LOW);
+ }
+}
+
static int init(void) {
static struct event_handler_info kernel_poll_hinfo = { 0, kernel_event_handler };
struct reread_data file_data = {
@@ -2887,22 +2972,9 @@
}
}
} else {
- /* Try to use psi monitor first if kernel has it */
- use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
- init_psi_monitors();
- /* Fall back to vmpressure */
- if (!use_psi_monitors &&
- (!init_mp_common(VMPRESS_LEVEL_LOW) ||
- !init_mp_common(VMPRESS_LEVEL_MEDIUM) ||
- !init_mp_common(VMPRESS_LEVEL_CRITICAL))) {
- ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
+ if (!init_monitors()) {
return -1;
}
- if (use_psi_monitors) {
- ALOGI("Using psi monitors for memory pressure detection");
- } else {
- ALOGI("Using vmpressure for memory pressure detection");
- }
/* let the others know it does support reporting kills */
property_set("sys.lmk.reportkills", "1");
}
@@ -2993,7 +3065,7 @@
poll_params.paused_handler = NULL;
while (1) {
- struct epoll_event events[maxevents];
+ struct epoll_event events[MAX_EPOLL_EVENTS];
int nevents;
int i;
@@ -3085,11 +3157,41 @@
}
}
-int main(int argc __unused, char **argv __unused) {
- struct sched_param param = {
- .sched_priority = 1,
- };
+int issue_reinit() {
+ LMKD_CTRL_PACKET packet;
+ size_t size;
+ int sock;
+ sock = lmkd_connect();
+ if (sock < 0) {
+ ALOGE("failed to connect to lmkd: %s", strerror(errno));
+ return -1;
+ }
+
+ enum update_props_result res = lmkd_update_props(sock);
+ switch (res) {
+ case UPDATE_PROPS_SUCCESS:
+ ALOGI("lmkd updated properties successfully");
+ break;
+ case UPDATE_PROPS_SEND_ERR:
+ ALOGE("failed to send lmkd request: %s", strerror(errno));
+ break;
+ case UPDATE_PROPS_RECV_ERR:
+ ALOGE("failed to receive lmkd reply: %s", strerror(errno));
+ break;
+ case UPDATE_PROPS_FORMAT_ERR:
+ ALOGE("lmkd reply is invalid");
+ break;
+ case UPDATE_PROPS_FAIL:
+ ALOGE("lmkd failed to update its properties");
+ break;
+ }
+
+ close(sock);
+ return res == UPDATE_PROPS_SUCCESS ? 0 : -1;
+}
+
+static void update_props() {
/* By default disable low level vmpressure events */
level_oomadj[VMPRESS_LEVEL_LOW] =
property_get_int32("ro.lmk.low", OOM_SCORE_ADJ_MAX + 1);
@@ -3126,6 +3228,17 @@
thrashing_limit_decay_pct = clamp(0, 100, property_get_int32("ro.lmk.thrashing_limit_decay",
low_ram_device ? DEF_THRASHING_DECAY_LOWRAM : DEF_THRASHING_DECAY));
swap_util_max = clamp(0, 100, property_get_int32("ro.lmk.swap_util_max", 100));
+}
+
+int main(int argc, char **argv) {
+ if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {
+ if (property_set(LMKD_REINIT_PROP, "0")) {
+ ALOGE("Failed to reset " LMKD_REINIT_PROP " property");
+ }
+ return issue_reinit();
+ }
+
+ update_props();
ctx = create_android_logger(KILLINFO_LOG_TAG);
@@ -3148,6 +3261,9 @@
}
/* CAP_NICE required */
+ struct sched_param param = {
+ .sched_priority = 1,
+ };
if (sched_setscheduler(0, SCHED_FIFO, ¶m)) {
ALOGW("set SCHED_FIFO failed %s", strerror(errno));
}