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, &params);
+
+    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, &param)) {
                 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