Snap for 4725392 from 7cb8aade2072d16180083e41ca34f72747089357 to pi-release
Change-Id: I41683ad62b26109d9aa5eee925934092f6347d34
This commit is contained in:
commit
bea361295e
|
|
@ -0,0 +1,60 @@
|
||||||
|
Android Low Memory Killer Daemon
|
||||||
|
================================
|
||||||
|
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
Android Low Memory Killer Daemon (lmkd) is a process monitoring memory
|
||||||
|
state of a running Android system and reacting to high memory pressure
|
||||||
|
by killing the least essential process(es) to keep system performing
|
||||||
|
at acceptable levels.
|
||||||
|
|
||||||
|
|
||||||
|
Background
|
||||||
|
----------
|
||||||
|
|
||||||
|
Historically on Android systems memory monitoring and killing of
|
||||||
|
non-essential processes was handled by a kernel lowmemorykiller driver.
|
||||||
|
Since Linux Kernel 4.12 the lowmemorykiller driver has been removed and
|
||||||
|
instead userspace lmkd daemon performs these tasks.
|
||||||
|
|
||||||
|
|
||||||
|
Android Properties
|
||||||
|
------------------
|
||||||
|
|
||||||
|
lmkd can be configured on a particular system using the following Android
|
||||||
|
properties:
|
||||||
|
|
||||||
|
ro.config.low_ram: choose between low-memory vs high-performance
|
||||||
|
device. Default = false.
|
||||||
|
|
||||||
|
ro.lmk.low: min oom_adj score for processes eligible to be
|
||||||
|
killed at low vmpressure level. Default = 1001
|
||||||
|
(disabled)
|
||||||
|
|
||||||
|
ro.lmk.medium: min oom_adj score for processes eligible to be
|
||||||
|
killed at medium vmpressure level. Default = 800
|
||||||
|
(non-essential processes)
|
||||||
|
|
||||||
|
ro.lmk.critical: min oom_adj score for processes eligible to be
|
||||||
|
killed at critical vmpressure level. Default = 0
|
||||||
|
(all processes)
|
||||||
|
|
||||||
|
ro.lmk.critical_upgrade: enables upgrade to critical level. Default = false
|
||||||
|
|
||||||
|
ro.lmk.upgrade_pressure: max mem_pressure at which level will be upgraded
|
||||||
|
because system is swapping too much. Default = 100
|
||||||
|
(disabled)
|
||||||
|
|
||||||
|
ro.lmk.downgrade_pressure: min mem_pressure at which vmpressure event will
|
||||||
|
be ignored because enough free memory is still
|
||||||
|
available. Default = 100 (disabled)
|
||||||
|
|
||||||
|
ro.lmk.kill_heaviest_task: kill heaviest eligible task (best decision) vs.
|
||||||
|
any eligible task (fast decision). Default = false
|
||||||
|
|
||||||
|
ro.lmk.kill_timeout_ms: duration in ms after a kill when no additional
|
||||||
|
kill will be done, Default = 0 (disabled)
|
||||||
|
|
||||||
|
ro.lmk.debug: enable lmkd debug logs, Default = false
|
||||||
102
lmkd.c
102
lmkd.c
|
|
@ -112,7 +112,7 @@ static bool debug_process_killing;
|
||||||
static bool enable_pressure_upgrade;
|
static bool enable_pressure_upgrade;
|
||||||
static int64_t upgrade_pressure;
|
static int64_t upgrade_pressure;
|
||||||
static int64_t downgrade_pressure;
|
static int64_t downgrade_pressure;
|
||||||
static bool is_go_device;
|
static bool low_ram_device;
|
||||||
static bool kill_heaviest_task;
|
static bool kill_heaviest_task;
|
||||||
static unsigned long kill_timeout_ms;
|
static unsigned long kill_timeout_ms;
|
||||||
|
|
||||||
|
|
@ -171,6 +171,11 @@ struct proc {
|
||||||
struct proc *pidhash_next;
|
struct proc *pidhash_next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct reread_data {
|
||||||
|
const char* const filename;
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef LMKD_LOG_STATS
|
#ifdef LMKD_LOG_STATS
|
||||||
static bool enable_stats_log;
|
static bool enable_stats_log;
|
||||||
static android_log_context log_ctx;
|
static android_log_context log_ctx;
|
||||||
|
|
@ -186,12 +191,27 @@ static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1];
|
||||||
/* PAGE_SIZE / 1024 */
|
/* PAGE_SIZE / 1024 */
|
||||||
static long page_k;
|
static long page_k;
|
||||||
|
|
||||||
|
static bool parse_int64(const char* str, int64_t* ret) {
|
||||||
|
char* endptr;
|
||||||
|
long long val = strtoll(str, &endptr, 10);
|
||||||
|
if (str == endptr || val > INT64_MAX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*ret = (int64_t)val;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read file content from the beginning up to max_len bytes or EOF
|
||||||
|
* whichever happens first.
|
||||||
|
*/
|
||||||
static ssize_t read_all(int fd, char *buf, size_t max_len)
|
static ssize_t read_all(int fd, char *buf, size_t max_len)
|
||||||
{
|
{
|
||||||
ssize_t ret = 0;
|
ssize_t ret = 0;
|
||||||
|
off_t offset = 0;
|
||||||
|
|
||||||
while (max_len > 0) {
|
while (max_len > 0) {
|
||||||
ssize_t r = read(fd, buf, max_len);
|
ssize_t r = TEMP_FAILURE_RETRY(pread(fd, buf, max_len, offset));
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -200,12 +220,44 @@ static ssize_t read_all(int fd, char *buf, size_t max_len)
|
||||||
}
|
}
|
||||||
ret += r;
|
ret += r;
|
||||||
buf += r;
|
buf += r;
|
||||||
|
offset += r;
|
||||||
max_len -= r;
|
max_len -= r;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read a new or already opened file from the beginning.
|
||||||
|
* If the file has not been opened yet data->fd should be set to -1.
|
||||||
|
* To be used with files which are read often and possibly during high
|
||||||
|
* memory pressure to minimize file opening which by itself requires kernel
|
||||||
|
* memory allocation and might result in a stall on memory stressed system.
|
||||||
|
*/
|
||||||
|
static int reread_file(struct reread_data *data, char *buf, size_t buf_size) {
|
||||||
|
ssize_t size;
|
||||||
|
|
||||||
|
if (data->fd == -1) {
|
||||||
|
data->fd = open(data->filename, O_RDONLY | O_CLOEXEC);
|
||||||
|
if (data->fd == -1) {
|
||||||
|
ALOGE("%s open: %s", data->filename, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size = read_all(data->fd, buf, buf_size - 1);
|
||||||
|
if (size < 0) {
|
||||||
|
ALOGE("%s read: %s", data->filename, strerror(errno));
|
||||||
|
close(data->fd);
|
||||||
|
data->fd = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ALOG_ASSERT((size_t)size < buf_size - 1, data->filename " too large");
|
||||||
|
buf[size] = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct proc *pid_lookup(int pid) {
|
static struct proc *pid_lookup(int pid) {
|
||||||
struct proc *procp;
|
struct proc *procp;
|
||||||
|
|
||||||
|
|
@ -442,7 +494,7 @@ static void ctrl_data_close(int dsock_idx) {
|
||||||
static int ctrl_data_read(int dsock_idx, char *buf, size_t bufsz) {
|
static int ctrl_data_read(int dsock_idx, char *buf, size_t bufsz) {
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
ret = read(data_sock[dsock_idx].sock, buf, bufsz);
|
ret = TEMP_FAILURE_RETRY(read(data_sock[dsock_idx].sock, buf, bufsz));
|
||||||
|
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
ALOGE("control data socket read failed; errno=%d", errno);
|
ALOGE("control data socket read failed; errno=%d", errno);
|
||||||
|
|
@ -771,10 +823,8 @@ static int find_and_kill_processes(enum vmpressure_level level,
|
||||||
struct proc *procp;
|
struct proc *procp;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (is_go_device)
|
procp = kill_heaviest_task ?
|
||||||
procp = proc_adj_lru(i);
|
proc_get_heaviest(i) : proc_adj_lru(i);
|
||||||
else
|
|
||||||
procp = proc_get_heaviest(i);
|
|
||||||
|
|
||||||
if (!procp)
|
if (!procp)
|
||||||
break;
|
break;
|
||||||
|
|
@ -805,23 +855,19 @@ static int find_and_kill_processes(enum vmpressure_level level,
|
||||||
return pages_freed;
|
return pages_freed;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t get_memory_usage(const char* path) {
|
static int64_t get_memory_usage(struct reread_data *file_data) {
|
||||||
int ret;
|
int ret;
|
||||||
int64_t mem_usage;
|
int64_t mem_usage;
|
||||||
char buf[32];
|
char buf[32];
|
||||||
int fd = open(path, O_RDONLY | O_CLOEXEC);
|
|
||||||
if (fd == -1) {
|
if (reread_file(file_data, buf, sizeof(buf)) < 0) {
|
||||||
ALOGE("%s open: errno=%d", path, errno);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = read_all(fd, buf, sizeof(buf) - 1);
|
if (!parse_int64(buf, &mem_usage)) {
|
||||||
close(fd);
|
ALOGE("%s parse error", file_data->filename);
|
||||||
if (ret < 0) {
|
|
||||||
ALOGE("%s error: errno=%d", path, errno);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
sscanf(buf, "%" SCNd64, &mem_usage);
|
|
||||||
if (mem_usage == 0) {
|
if (mem_usage == 0) {
|
||||||
ALOGE("No memory!");
|
ALOGE("No memory!");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -881,6 +927,14 @@ static void mp_event_common(int data, uint32_t events __unused) {
|
||||||
static struct timeval last_report_tm;
|
static struct timeval last_report_tm;
|
||||||
static unsigned long skip_count = 0;
|
static unsigned long skip_count = 0;
|
||||||
enum vmpressure_level level = (enum vmpressure_level)data;
|
enum vmpressure_level level = (enum vmpressure_level)data;
|
||||||
|
static struct reread_data mem_usage_file_data = {
|
||||||
|
.filename = MEMCG_MEMORY_USAGE,
|
||||||
|
.fd = -1,
|
||||||
|
};
|
||||||
|
static struct reread_data memsw_usage_file_data = {
|
||||||
|
.filename = MEMCG_MEMORYSW_USAGE,
|
||||||
|
.fd = -1,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check all event counters from low to critical
|
* Check all event counters from low to critical
|
||||||
|
|
@ -889,7 +943,8 @@ static void mp_event_common(int data, uint32_t events __unused) {
|
||||||
*/
|
*/
|
||||||
for (lvl = VMPRESS_LEVEL_LOW; lvl < VMPRESS_LEVEL_COUNT; lvl++) {
|
for (lvl = VMPRESS_LEVEL_LOW; lvl < VMPRESS_LEVEL_COUNT; lvl++) {
|
||||||
if (mpevfd[lvl] != -1 &&
|
if (mpevfd[lvl] != -1 &&
|
||||||
read(mpevfd[lvl], &evcount, sizeof(evcount)) > 0 &&
|
TEMP_FAILURE_RETRY(read(mpevfd[lvl],
|
||||||
|
&evcount, sizeof(evcount))) > 0 &&
|
||||||
evcount > 0 && lvl > level) {
|
evcount > 0 && lvl > level) {
|
||||||
level = lvl;
|
level = lvl;
|
||||||
}
|
}
|
||||||
|
|
@ -926,9 +981,10 @@ static void mp_event_common(int data, uint32_t events __unused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mem_usage = get_memory_usage(MEMCG_MEMORY_USAGE);
|
if ((mem_usage = get_memory_usage(&mem_usage_file_data)) < 0) {
|
||||||
memsw_usage = get_memory_usage(MEMCG_MEMORYSW_USAGE);
|
goto do_kill;
|
||||||
if (memsw_usage < 0 || mem_usage < 0) {
|
}
|
||||||
|
if ((memsw_usage = get_memory_usage(&memsw_usage_file_data)) < 0) {
|
||||||
goto do_kill;
|
goto do_kill;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -962,7 +1018,7 @@ static void mp_event_common(int data, uint32_t events __unused) {
|
||||||
}
|
}
|
||||||
|
|
||||||
do_kill:
|
do_kill:
|
||||||
if (is_go_device) {
|
if (low_ram_device) {
|
||||||
/* For Go devices kill only one task */
|
/* For Go devices kill only one task */
|
||||||
if (find_and_kill_processes(level, 0) == 0) {
|
if (find_and_kill_processes(level, 0) == 0) {
|
||||||
if (debug_process_killing) {
|
if (debug_process_killing) {
|
||||||
|
|
@ -1198,8 +1254,8 @@ int main(int argc __unused, char **argv __unused) {
|
||||||
downgrade_pressure =
|
downgrade_pressure =
|
||||||
(int64_t)property_get_int32("ro.lmk.downgrade_pressure", 100);
|
(int64_t)property_get_int32("ro.lmk.downgrade_pressure", 100);
|
||||||
kill_heaviest_task =
|
kill_heaviest_task =
|
||||||
property_get_bool("ro.lmk.kill_heaviest_task", true);
|
property_get_bool("ro.lmk.kill_heaviest_task", false);
|
||||||
is_go_device = property_get_bool("ro.config.low_ram", false);
|
low_ram_device = property_get_bool("ro.config.low_ram", false);
|
||||||
kill_timeout_ms =
|
kill_timeout_ms =
|
||||||
(unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 0);
|
(unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 0);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue