diff --git a/Android.bp b/Android.bp index ef53b6b..43d9e76 100644 --- a/Android.bp +++ b/Android.bp @@ -24,6 +24,7 @@ cc_binary { 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_register_proc(int sock, struct lmk_procprio *params); */ 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 @@ enum lmk_cmd { 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 @@ static inline size_t lmkd_pack_set_prockills(LMKD_CTRL_PACKET packet, pid_t pid, 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 @@ int lmkd_unregister_proc(int sock, struct lmk_procremove *params) { 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 3b8a6fa..d022f34 100644 --- a/lmkd.cpp +++ b/lmkd.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -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 @@ static uint32_t killcnt_total = 0; /* 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 @@ static void ctrl_command_handler(int dsock_idx) { 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 @@ static void ctrl_command_handler(int dsock_idx) { /* 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 @@ static void stop_wait_for_proc_kill(bool finished) { 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 bool init_mp_psi(enum vmpressure_level level, bool use_new_strategy) { 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 @@ err_open_mpfd: 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 @@ static int init(void) { } } } 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 @@ static void mainloop(void) { 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 @@ static void mainloop(void) { } } -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 @@ int main(int argc __unused, char **argv __unused) { 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 @@ int main(int argc __unused, char **argv __unused) { } /* 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 @@ service lmkd /system/bin/lmkd 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