Merge topic 'lmkd' into lmp-dev
* changes:
lmkd: kill a process if zoneinfo can't be read
lmkd: kill multiple tasks
lmkd: use open instead of fopen
lmkd: make lmkd SCHED_FIFO
lmkd: mlock all memory
lmkd: remove libcutils from LOCAL_SHARED_LIBRARIES
diff --git a/lmkd/Android.mk b/lmkd/Android.mk
index 043a085..39081d6 100644
--- a/lmkd/Android.mk
+++ b/lmkd/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES := lmkd.c
-LOCAL_SHARED_LIBRARIES := libcutils liblog libm libc libprocessgroup
+LOCAL_SHARED_LIBRARIES := liblog libm libc libprocessgroup
LOCAL_CFLAGS := -Werror
LOCAL_MODULE := lmkd
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 1aeb182..a534a24 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -19,13 +19,13 @@
#include <arpa/inet.h>
#include <errno.h>
#include <signal.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/cdefs.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
+#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
@@ -128,6 +128,26 @@
/* PAGE_SIZE / 1024 */
static long page_k;
+static ssize_t read_all(int fd, char *buf, size_t max_len)
+{
+ ssize_t ret = 0;
+
+ while (max_len > 0) {
+ ssize_t r = read(fd, buf, max_len);
+ if (r == 0) {
+ break;
+ }
+ if (r == -1) {
+ return -1;
+ }
+ ret += r;
+ buf += r;
+ max_len -= r;
+ }
+
+ return ret;
+}
+
static int lowmem_oom_adj_to_oom_score_adj(int oom_adj)
{
if (oom_adj == OOM_ADJUST_MAX)
@@ -421,17 +441,13 @@
static int zoneinfo_parse_protection(char *cp) {
int max = 0;
int zoneval;
+ char *save_ptr;
- if (*cp++ != '(')
- return 0;
-
- do {
+ for (cp = strtok_r(cp, "(), ", &save_ptr); cp; cp = strtok_r(NULL, "), ", &save_ptr)) {
zoneval = strtol(cp, &cp, 0);
- if ((*cp != ',') && (*cp != ')'))
- return 0;
if (zoneval > max)
max = zoneval;
- } while ((cp = strtok(NULL, " ")));
+ }
return max;
}
@@ -439,12 +455,13 @@
static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) {
char *cp = line;
char *ap;
+ char *save_ptr;
- cp = strtok(line, " ");
+ cp = strtok_r(line, " ", &save_ptr);
if (!cp)
return;
- ap = strtok(NULL, " ");
+ ap = strtok_r(NULL, " ", &save_ptr);
if (!ap)
return;
@@ -461,56 +478,74 @@
}
static int zoneinfo_parse(struct sysmeminfo *mip) {
- FILE *f;
- char line[LINE_MAX];
+ int fd;
+ ssize_t size;
+ char buf[PAGE_SIZE];
+ char *save_ptr;
+ char *line;
memset(mip, 0, sizeof(struct sysmeminfo));
- f = fopen(ZONEINFO_PATH, "r");
- if (!f) {
+
+ fd = open(ZONEINFO_PATH, O_RDONLY);
+ if (fd == -1) {
ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno);
return -1;
}
- while (fgets(line, LINE_MAX, f))
+ size = read_all(fd, buf, sizeof(buf) - 1);
+ if (size < 0) {
+ ALOGE("%s read: errno=%d", ZONEINFO_PATH, errno);
+ close(fd);
+ return -1;
+ }
+ ALOG_ASSERT((size_t)size < sizeof(buf) - 1, "/proc/zoneinfo too large");
+ buf[size] = 0;
+
+ for (line = strtok_r(buf, "\n", &save_ptr); line; line = strtok_r(NULL, "\n", &save_ptr))
zoneinfo_parse_line(line, mip);
- fclose(f);
+ close(fd);
return 0;
}
static int proc_get_size(int pid) {
char path[PATH_MAX];
char line[LINE_MAX];
- FILE *f;
+ int fd;
int rss = 0;
int total;
+ ssize_t ret;
snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
- f = fopen(path, "r");
- if (!f)
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
return -1;
- if (!fgets(line, LINE_MAX, f)) {
- fclose(f);
+
+ ret = read_all(fd, line, sizeof(line) - 1);
+ if (ret < 0) {
+ close(fd);
return -1;
}
sscanf(line, "%d %d ", &total, &rss);
- fclose(f);
+ close(fd);
return rss;
}
static char *proc_get_name(int pid) {
char path[PATH_MAX];
static char line[LINE_MAX];
- FILE *f;
+ int fd;
char *cp;
+ ssize_t ret;
snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
- f = fopen(path, "r");
- if (!f)
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
return NULL;
- if (!fgets(line, LINE_MAX, f)) {
- fclose(f);
+ ret = read_all(fd, line, sizeof(line) - 1);
+ close(fd);
+ if (ret < 0) {
return NULL;
}
@@ -525,29 +560,57 @@
return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
}
-static void mp_event(uint32_t events __unused) {
+/* Kill one process specified by procp. Returns the size of the process killed */
+static int kill_one_process(struct proc *procp, int other_free, int other_file,
+ int minfree, int min_score_adj, bool first)
+{
+ int pid = procp->pid;
+ uid_t uid = procp->uid;
+ char *taskname;
+ int tasksize;
+ int r;
+
+ taskname = proc_get_name(pid);
+ if (!taskname) {
+ pid_remove(pid);
+ return -1;
+ }
+
+ tasksize = proc_get_size(pid);
+ if (tasksize <= 0) {
+ pid_remove(pid);
+ return -1;
+ }
+
+ ALOGI("Killing '%s' (%d), uid %d, adj %d\n"
+ " to free %ldkB because cache %s%ldkB is below limit %ldkB for oom_adj %d\n"
+ " Free memory is %s%ldkB %s reserved",
+ taskname, pid, uid, procp->oomadj, tasksize * page_k,
+ first ? "" : "~", other_file * page_k, minfree * page_k, min_score_adj,
+ first ? "" : "~", other_free * page_k, other_free >= 0 ? "above" : "below");
+ r = kill(pid, SIGKILL);
+ killProcessGroup(uid, pid, SIGKILL);
+ pid_remove(pid);
+
+ if (r) {
+ ALOGE("kill(%d): errno=%d", procp->pid, errno);
+ return -1;
+ } else {
+ return tasksize;
+ }
+}
+
+/*
+ * Find a process to kill based on the current (possibly estimated) free memory
+ * and cached memory sizes. Returns the size of the killed processes.
+ */
+static int find_and_kill_process(int other_free, int other_file, bool first)
+{
int i;
- int ret;
- unsigned long long evcount;
- struct sysmeminfo mi;
- int other_free;
- int other_file;
- int minfree = 0;
+ int r;
int min_score_adj = OOM_ADJUST_MAX + 1;
-
- ret = read(mpevfd, &evcount, sizeof(evcount));
- if (ret < 0)
- ALOGE("Error reading memory pressure event fd; errno=%d",
- errno);
-
- if (time(NULL) - kill_lasttime < KILL_TIMEOUT)
- return;
-
- if (zoneinfo_parse(&mi) < 0)
- return;
-
- other_free = mi.nr_free_pages - mi.totalreserve_pages;
- other_file = mi.nr_file_pages - mi.nr_shmem;
+ int minfree = 0;
+ int killed_size = 0;
for (i = 0; i < lowmem_targets_size; i++) {
minfree = lowmem_minfree[i];
@@ -558,52 +621,61 @@
}
if (min_score_adj == OOM_ADJUST_MAX + 1)
- return;
+ return 0;
for (i = OOM_ADJUST_MAX; i >= min_score_adj; i--) {
struct proc *procp;
- retry:
+retry:
procp = proc_adj_lru(i);
if (procp) {
- int pid = procp->pid;
- uid_t uid = procp->uid;
- char *taskname;
- int tasksize;
- int r;
-
- taskname = proc_get_name(pid);
- if (!taskname) {
- pid_remove(pid);
- goto retry;
- }
-
- tasksize = proc_get_size(pid);
- if (tasksize < 0) {
- pid_remove(pid);
- goto retry;
- }
-
- ALOGI("Killing '%s' (%d), uid %d, adj %d\n"
- " to free %ldkB because cache %ldkB is below limit %ldkB for oom_adj %d\n"
- " Free memory is %ldkB %s reserved",
- taskname, pid, uid, procp->oomadj, tasksize * page_k,
- other_file * page_k, minfree * page_k, min_score_adj,
- other_free * page_k, other_free >= 0 ? "above" : "below");
- r = kill(pid, SIGKILL);
- killProcessGroup(uid, pid, SIGKILL);
- pid_remove(pid);
-
- if (r) {
- ALOGE("kill(%d): errno=%d", procp->pid, errno);
+ killed_size = kill_one_process(procp, other_free, other_file, minfree, min_score_adj, first);
+ if (killed_size < 0) {
goto retry;
} else {
- time(&kill_lasttime);
- break;
+ return killed_size;
}
}
}
+
+ return 0;
+}
+
+static void mp_event(uint32_t events __unused) {
+ int i;
+ int ret;
+ unsigned long long evcount;
+ struct sysmeminfo mi;
+ int other_free;
+ int other_file;
+ int killed_size;
+ bool first = true;
+
+ ret = read(mpevfd, &evcount, sizeof(evcount));
+ if (ret < 0)
+ ALOGE("Error reading memory pressure event fd; errno=%d",
+ errno);
+
+ if (time(NULL) - kill_lasttime < KILL_TIMEOUT)
+ return;
+
+ while (zoneinfo_parse(&mi) < 0) {
+ // Failed to read /proc/zoneinfo, assume ENOMEM and kill something
+ find_and_kill_process(0, 0, true);
+ }
+
+ other_free = mi.nr_free_pages - mi.totalreserve_pages;
+ other_file = mi.nr_file_pages - mi.nr_shmem;
+
+ do {
+ killed_size = find_and_kill_process(other_free, other_file, first);
+ if (killed_size > 0) {
+ first = false;
+ other_free += killed_size;
+ other_file += killed_size;
+ }
+ } while (killed_size > 0);
}
static int init_mp(char *levelstr, void *event_handler)
@@ -747,6 +819,12 @@
}
int main(int argc __unused, char **argv __unused) {
+ struct sched_param param = {
+ .sched_priority = 1,
+ };
+
+ mlockall(MCL_FUTURE);
+ sched_setscheduler(0, SCHED_FIFO, ¶m);
if (!init())
mainloop();