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>
Merged-In: Ic60331f3368f5a7fdfe09ad7d47c7ccf0a497685
Change-Id: Ic60331f3368f5a7fdfe09ad7d47c7ccf0a497685
diff --git a/Android.bp b/Android.bp
index ef53b6b..43d9e76 100644
--- a/Android.bp
+++ b/Android.bp
@@ -24,6 +24,7 @@
static_libs: [
"libstatslogc",
"libstatslog_lmkd",
+ "liblmkd_utils",
],
local_include_dirs: ["include"],
cflags: ["-Werror", "-DLMKD_TRACE_KILLS"],
diff --git a/include/liblmkd_utils.h b/include/liblmkd_utils.h
index 92e4d41..a84db84 100644
--- a/include/liblmkd_utils.h
+++ b/include/liblmkd_utils.h
@@ -47,6 +47,20 @@
*/
int lmkd_unregister_proc(int sock, struct lmk_procremove *params);
+enum update_props_result {
+ UPDATE_PROPS_SUCCESS,
+ UPDATE_PROPS_FAIL,
+ UPDATE_PROPS_SEND_ERR,
+ UPDATE_PROPS_RECV_ERR,
+ UPDATE_PROPS_FORMAT_ERR,
+};
+
+/*
+ * Updates lmkd properties.
+ * In the case of ERR_SEND or ERR_RECV errno is set appropriately.
+ */
+enum update_props_result lmkd_update_props(int sock);
+
/*
* Creates memcg directory for given process.
* On success returns 0.
diff --git a/include/lmkd.h b/include/lmkd.h
index c22cb78..ad5dc75 100644
--- a/include/lmkd.h
+++ b/include/lmkd.h
@@ -34,6 +34,7 @@
LMK_GETKILLCNT, /* Get number of kills */
LMK_SUBSCRIBE, /* Subscribe for asynchronous events */
LMK_PROCKILL, /* Unsolicited msg to subscribed clients on proc kills */
+ LMK_UPDATE_PROPS, /* Reinit properties */
};
/*
@@ -244,6 +245,39 @@
return 3 * sizeof(int);
}
+/*
+ * Prepare LMK_UPDATE_PROPS packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+static inline size_t lmkd_pack_set_update_props(LMKD_CTRL_PACKET packet) {
+ packet[0] = htonl(LMK_UPDATE_PROPS);
+ return sizeof(int);
+}
+
+/*
+ * Prepare LMK_UPDATE_PROPS reply packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+static inline size_t lmkd_pack_set_update_props_repl(LMKD_CTRL_PACKET packet, int result) {
+ packet[0] = htonl(LMK_UPDATE_PROPS);
+ packet[1] = htonl(result);
+ return 2 * sizeof(int);
+}
+
+/* LMK_PROCPRIO reply payload */
+struct lmk_update_props_reply {
+ int result;
+};
+
+/*
+ * For LMK_UPDATE_PROPS reply payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+static inline void lmkd_pack_get_update_props_repl(LMKD_CTRL_PACKET packet,
+ struct lmk_update_props_reply* params) {
+ params->result = ntohl(packet[1]);
+}
+
__END_DECLS
#endif /* _LMKD_H_ */
diff --git a/liblmkd_utils.cpp b/liblmkd_utils.cpp
index 280c149..45e867e 100644
--- a/liblmkd_utils.cpp
+++ b/liblmkd_utils.cpp
@@ -53,6 +53,30 @@
return (ret < 0) ? -1 : 0;
}
+enum update_props_result lmkd_update_props(int sock) {
+ LMKD_CTRL_PACKET packet;
+ size_t size;
+
+ size = lmkd_pack_set_update_props(packet);
+ if (TEMP_FAILURE_RETRY(write(sock, packet, size)) < 0) {
+ return UPDATE_PROPS_SEND_ERR;
+ }
+
+ size = TEMP_FAILURE_RETRY(read(sock, packet, CTRL_PACKET_MAX_SIZE));
+ if (size < 0) {
+ return UPDATE_PROPS_RECV_ERR;
+ }
+
+ if (size != 2 * sizeof(int) || lmkd_pack_get_cmd(packet) != LMK_UPDATE_PROPS) {
+ return UPDATE_PROPS_FORMAT_ERR;
+ }
+
+ struct lmk_update_props_reply params;
+ lmkd_pack_get_update_props_repl(packet, ¶ms);
+
+ return params.result == 0 ? UPDATE_PROPS_SUCCESS : UPDATE_PROPS_FAIL;
+}
+
int create_memcg(uid_t uid, pid_t pid) {
char buf[256];
int tasks_file;
diff --git a/lmkd.cpp b/lmkd.cpp
index 23bb5c3..351b41a 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);
@@ -2688,10 +2719,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;
}
@@ -2794,11 +2830,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 = {
@@ -2866,22 +2951,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");
}
@@ -2965,7 +3037,7 @@
poll_params.update = POLLING_DO_NOT_CHANGE;
while (1) {
- struct epoll_event events[maxevents];
+ struct epoll_event events[MAX_EPOLL_EVENTS];
int nevents;
int i;
@@ -3042,11 +3114,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);
@@ -3082,6 +3184,17 @@
low_ram_device ? DEF_THRASHING_LOWRAM : DEF_THRASHING));
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));
+}
+
+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);
@@ -3104,6 +3217,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));
}
diff --git a/lmkd.rc b/lmkd.rc
index 982a188..17c6560 100644
--- a/lmkd.rc
+++ b/lmkd.rc
@@ -6,3 +6,6 @@
critical
socket lmkd seqpacket+passcred 0660 system system
writepid /dev/cpuset/system-background/tasks
+
+on property:lmkd.reinit=1
+ exec_background /system/bin/lmkd --reinit