diff --git a/lmkd/Android.bp b/lmkd/Android.bp index 76d308a3c..d172755bd 100644 --- a/lmkd/Android.bp +++ b/lmkd/Android.bp @@ -6,6 +6,7 @@ cc_binary { "liblog", "libcutils", ], + local_include_dirs: ["include"], cflags: ["-Werror"], init_rc: ["lmkd.rc"], @@ -18,3 +19,17 @@ cc_binary { }, }, } + +cc_library_static { + name: "liblmkd_utils", + srcs: ["liblmkd_utils.c"], + shared_libs: [ + "libcutils", + ], + export_include_dirs: ["include"], + cppflags: [ + "-g", + "-Wall", + "-Werror", + ] +} diff --git a/lmkd/include/liblmkd_utils.h b/lmkd/include/liblmkd_utils.h new file mode 100644 index 000000000..72e3f4a2b --- /dev/null +++ b/lmkd/include/liblmkd_utils.h @@ -0,0 +1,54 @@ +/* + * Copyright 2018 Google, Inc + * + * 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. + */ + +#ifndef _LIBLMKD_UTILS_H_ +#define _LIBLMKD_UTILS_H_ + +#include +#include + +#include + +__BEGIN_DECLS + +/* + * Connects to lmkd process and returns socket handle. + * On success returns socket handle. + * On error, -1 is returned, and errno is set appropriately. + */ +int lmkd_connect(); + +/* + * Registers a process with lmkd and sets its oomadj score. + * On success returns 0. + * On error, -1 is returned. + * In the case of error errno is set appropriately. + */ +int lmkd_register_proc(int sock, struct lmk_procprio *params); + +/* + * Creates memcg directory for given process. + * On success returns 0. + * -1 is returned if path creation failed. + * -2 is returned if tasks file open operation failed. + * -3 is returned if tasks file write operation failed. + * In the case of error errno is set appropriately. + */ +int create_memcg(uid_t uid, pid_t pid); + +__END_DECLS + +#endif /* _LIBLMKD_UTILS_H_ */ diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h new file mode 100644 index 000000000..fe6364d82 --- /dev/null +++ b/lmkd/include/lmkd.h @@ -0,0 +1,147 @@ +/* + * Copyright 2018 Google, Inc + * + * 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. + */ + +#ifndef _LMKD_H_ +#define _LMKD_H_ + +#include +#include +#include + +__BEGIN_DECLS + +/* + * Supported LMKD commands + */ +enum lmk_cmd { + LMK_TARGET = 0, /* Associate minfree with oom_adj_score */ + LMK_PROCPRIO, /* Register a process and set its oom_adj_score */ + LMK_PROCREMOVE, /* Unregister a process */ +}; + +/* + * Max number of targets in LMK_TARGET command. + */ +#define MAX_TARGETS 6 + +/* + * Max packet length in bytes. + * Longest packet is LMK_TARGET followed by MAX_TARGETS + * of minfree and oom_adj_score values + */ +#define CTRL_PACKET_MAX_SIZE (sizeof(int) * (MAX_TARGETS * 2 + 1)) + +/* LMKD packet - first int is lmk_cmd followed by payload */ +typedef int LMKD_CTRL_PACKET[CTRL_PACKET_MAX_SIZE / sizeof(int)]; + +/* Get LMKD packet command */ +inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) { + return (enum lmk_cmd)ntohl(pack[0]); +} + +/* LMK_TARGET packet payload */ +struct lmk_target { + int minfree; + int oom_adj_score; +}; + +/* + * For LMK_TARGET packet get target_idx-th payload. + * Warning: no checks performed, caller should ensure valid parameters. + */ +inline void lmkd_pack_get_target(LMKD_CTRL_PACKET packet, + int target_idx, struct lmk_target *target) { + target->minfree = ntohl(packet[target_idx * 2 + 1]); + target->oom_adj_score = ntohl(packet[target_idx * 2 + 2]); +} + +/* + * Prepare LMK_TARGET packet and return packet size in bytes. + * Warning: no checks performed, caller should ensure valid parameters. + */ +inline size_t lmkd_pack_set_target(LMKD_CTRL_PACKET packet, + struct lmk_target *targets, + size_t target_cnt) { + int idx = 0; + packet[idx++] = htonl(LMK_TARGET); + while (target_cnt) { + packet[idx++] = htonl(targets->minfree); + packet[idx++] = htonl(targets->oom_adj_score); + targets++; + target_cnt--; + } + return idx * sizeof(int); +} + +/* LMK_PROCPRIO packet payload */ +struct lmk_procprio { + pid_t pid; + uid_t uid; + int oomadj; +}; + +/* + * For LMK_PROCPRIO packet get its payload. + * Warning: no checks performed, caller should ensure valid parameters. + */ +inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet, + struct lmk_procprio *params) { + params->pid = (pid_t)ntohl(packet[1]); + params->uid = (uid_t)ntohl(packet[2]); + params->oomadj = ntohl(packet[3]); +} + +/* + * Prepare LMK_PROCPRIO packet and return packet size in bytes. + * Warning: no checks performed, caller should ensure valid parameters. + */ +inline size_t lmkd_pack_set_procprio(LMKD_CTRL_PACKET packet, + struct lmk_procprio *params) { + packet[0] = htonl(LMK_PROCPRIO); + packet[1] = htonl(params->pid); + packet[2] = htonl(params->uid); + packet[3] = htonl(params->oomadj); + return 4 * sizeof(int); +} + +/* LMK_PROCREMOVE packet payload */ +struct lmk_procremove { + pid_t pid; +}; + +/* + * For LMK_PROCREMOVE packet get its payload. + * Warning: no checks performed, caller should ensure valid parameters. + */ +inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet, + struct lmk_procremove *params) { + params->pid = (pid_t)ntohl(packet[1]); +} + +/* + * Prepare LMK_PROCREMOVE packet and return packet size in bytes. + * Warning: no checks performed, caller should ensure valid parameters. + */ +inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet, + struct lmk_procprio *params) { + packet[0] = htonl(LMK_PROCREMOVE); + packet[1] = htonl(params->pid); + return 2 * sizeof(int); +} + +__END_DECLS + +#endif /* _LMKD_H_ */ diff --git a/lmkd/liblmkd_utils.c b/lmkd/liblmkd_utils.c new file mode 100644 index 000000000..fa3b7a920 --- /dev/null +++ b/lmkd/liblmkd_utils.c @@ -0,0 +1,76 @@ +/* + * Copyright 2018 Google, Inc + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +int lmkd_connect() { + return socket_local_client("lmkd", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_SEQPACKET); +} + +int lmkd_register_proc(int sock, struct lmk_procprio *params) { + LMKD_CTRL_PACKET packet; + size_t size; + int ret; + + size = lmkd_pack_set_procprio(packet, params); + ret = TEMP_FAILURE_RETRY(write(sock, packet, size)); + + return (ret < 0) ? -1 : 0; +} + +int create_memcg(uid_t uid, pid_t pid) { + char buf[256]; + int tasks_file; + int written; + + snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u", uid); + if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 && + errno != EEXIST) { + return -1; + } + + snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u", uid, pid); + if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 && + errno != EEXIST) { + return -1; + } + + snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u/tasks", uid, pid); + tasks_file = open(buf, O_WRONLY); + if (tasks_file < 0) { + return -2; + } + written = snprintf(buf, sizeof(buf), "%u", pid); + if (__predict_false(written >= (int)sizeof(buf))) { + written = sizeof(buf) - 1; + } + written = TEMP_FAILURE_RETRY(write(tasks_file, buf, written)); + close(tasks_file); + + return (written < 0) ? -3 : 0; +} + diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c index 2a7fedb44..45fa863b2 100644 --- a/lmkd/lmkd.c +++ b/lmkd/lmkd.c @@ -16,7 +16,6 @@ #define LOG_TAG "lowmemorykiller" -#include #include #include #include @@ -34,6 +33,7 @@ #include #include +#include #include /* @@ -71,19 +71,6 @@ #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) #define EIGHT_MEGA (1 << 23) -enum lmk_cmd { - LMK_TARGET, - LMK_PROCPRIO, - LMK_PROCREMOVE, -}; - -#define MAX_TARGETS 6 -/* - * longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio - * values - */ -#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1)) - /* default to old in-kernel interface if no memory pressure events */ static int use_inkernel_interface = 1; static bool has_inkernel_module; @@ -300,45 +287,49 @@ static void writefilestring(const char *path, char *s) { close(fd); } -static void cmd_procprio(int pid, int uid, int oomadj) { +static void cmd_procprio(LMKD_CTRL_PACKET packet) { struct proc *procp; char path[80]; char val[20]; int soft_limit_mult; + struct lmk_procprio params; - if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) { - ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj); + lmkd_pack_get_procprio(packet, ¶ms); + + if (params.oomadj < OOM_SCORE_ADJ_MIN || + params.oomadj > OOM_SCORE_ADJ_MAX) { + ALOGE("Invalid PROCPRIO oomadj argument %d", params.oomadj); return; } - snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid); - snprintf(val, sizeof(val), "%d", oomadj); + snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", params.pid); + snprintf(val, sizeof(val), "%d", params.oomadj); writefilestring(path, val); if (use_inkernel_interface) return; - if (oomadj >= 900) { + if (params.oomadj >= 900) { soft_limit_mult = 0; - } else if (oomadj >= 800) { + } else if (params.oomadj >= 800) { soft_limit_mult = 0; - } else if (oomadj >= 700) { + } else if (params.oomadj >= 700) { soft_limit_mult = 0; - } else if (oomadj >= 600) { + } else if (params.oomadj >= 600) { // Launcher should be perceptible, don't kill it. - oomadj = 200; + params.oomadj = 200; soft_limit_mult = 1; - } else if (oomadj >= 500) { + } else if (params.oomadj >= 500) { soft_limit_mult = 0; - } else if (oomadj >= 400) { + } else if (params.oomadj >= 400) { soft_limit_mult = 0; - } else if (oomadj >= 300) { + } else if (params.oomadj >= 300) { soft_limit_mult = 1; - } else if (oomadj >= 200) { + } else if (params.oomadj >= 200) { soft_limit_mult = 2; - } else if (oomadj >= 100) { + } else if (params.oomadj >= 100) { soft_limit_mult = 10; - } else if (oomadj >= 0) { + } else if (params.oomadj >= 0) { soft_limit_mult = 20; } else { // Persistent processes will have a large @@ -346,11 +337,13 @@ static void cmd_procprio(int pid, int uid, int oomadj) { soft_limit_mult = 64; } - snprintf(path, sizeof(path), "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes", uid, pid); + snprintf(path, sizeof(path), + "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes", + params.uid, params.pid); snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA); writefilestring(path, val); - procp = pid_lookup(pid); + procp = pid_lookup(params.pid); if (!procp) { procp = malloc(sizeof(struct proc)); if (!procp) { @@ -358,33 +351,38 @@ static void cmd_procprio(int pid, int uid, int oomadj) { return; } - procp->pid = pid; - procp->uid = uid; - procp->oomadj = oomadj; + procp->pid = params.pid; + procp->uid = params.uid; + procp->oomadj = params.oomadj; proc_insert(procp); } else { proc_unslot(procp); - procp->oomadj = oomadj; + procp->oomadj = params.oomadj; proc_slot(procp); } } -static void cmd_procremove(int pid) { +static void cmd_procremove(LMKD_CTRL_PACKET packet) { + struct lmk_procremove params; + if (use_inkernel_interface) return; - pid_remove(pid); + lmkd_pack_get_procremove(packet, ¶ms); + pid_remove(params.pid); } -static void cmd_target(int ntargets, int *params) { +static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) { int i; + struct lmk_target target; if (ntargets > (int)ARRAY_SIZE(lowmem_adj)) return; for (i = 0; i < ntargets; i++) { - lowmem_minfree[i] = ntohl(*params++); - lowmem_adj[i] = ntohl(*params++); + lmkd_pack_get_target(packet, i, &target); + lowmem_minfree[i] = target.minfree; + lowmem_adj[i] = target.oom_adj_score; } lowmem_targets_size = ntargets; @@ -445,38 +443,42 @@ static int ctrl_data_read(int dsock_idx, char *buf, size_t bufsz) { } static void ctrl_command_handler(int dsock_idx) { - int ibuf[CTRL_PACKET_MAX / sizeof(int)]; + LMKD_CTRL_PACKET packet; int len; - int cmd = -1; + enum lmk_cmd cmd; int nargs; int targets; - len = ctrl_data_read(dsock_idx, (char *)ibuf, CTRL_PACKET_MAX); + len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE); if (len <= 0) return; + if (len < (int)sizeof(int)) { + ALOGE("Wrong control socket read length len=%d", len); + return; + } + + cmd = lmkd_pack_get_cmd(packet); nargs = len / sizeof(int) - 1; if (nargs < 0) goto wronglen; - cmd = ntohl(ibuf[0]); - switch(cmd) { case LMK_TARGET: targets = nargs / 2; if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj)) goto wronglen; - cmd_target(targets, &ibuf[1]); + cmd_target(targets, packet); break; case LMK_PROCPRIO: if (nargs != 3) goto wronglen; - cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3])); + cmd_procprio(packet); break; case LMK_PROCREMOVE: if (nargs != 1) goto wronglen; - cmd_procremove(ntohl(ibuf[1])); + cmd_procremove(packet); break; default: ALOGE("Received unknown command code %d", cmd);