lmkd: Allow killing multiple processes to downgrade memory pressure
Record free memory at low vmpressure levels and whenever pressure increases beyond low free up enough memory to downgrade memory pressure to low. This is done by freeing enough memory to get to the max free memory levels seen during low vmpressure. The kill logic for Go devices is not changed as these devices are designed to operate under high memory pressure. Bug: 63631020 Test: alloc-stress Change-Id: Ic8396eee08013b1c709072a13525601d5c8bf1f1 Signed-off-by: Suren Baghdasaryan <surenb@google.com>
This commit is contained in:
parent
e82e15c242
commit
65f54a2665
1 changed files with 114 additions and 23 deletions
137
lmkd/lmkd.c
137
lmkd/lmkd.c
|
@ -29,6 +29,7 @@
|
|||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cutils/properties.h>
|
||||
|
@ -101,6 +102,16 @@ static const char *level_name[] = {
|
|||
"critical"
|
||||
};
|
||||
|
||||
struct mem_size {
|
||||
int free_mem;
|
||||
int free_swap;
|
||||
};
|
||||
|
||||
struct {
|
||||
int min_free; /* recorded but not used yet */
|
||||
int max_free;
|
||||
} low_pressure_mem = { -1, -1 };
|
||||
|
||||
static int level_oomadj[VMPRESS_LEVEL_COUNT];
|
||||
static int mpevfd[VMPRESS_LEVEL_COUNT] = { -1, -1, -1 };
|
||||
static bool debug_process_killing;
|
||||
|
@ -559,6 +570,18 @@ static int zoneinfo_parse(struct sysmeminfo *mip) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int get_free_memory(struct mem_size *ms) {
|
||||
struct sysinfo si;
|
||||
|
||||
if (sysinfo(&si) < 0)
|
||||
return -1;
|
||||
|
||||
ms->free_mem = (int)(si.freeram * si.mem_unit / PAGE_SIZE);
|
||||
ms->free_swap = (int)(si.freeswap * si.mem_unit / PAGE_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_get_size(int pid) {
|
||||
char path[PATH_MAX];
|
||||
char line[LINE_MAX];
|
||||
|
@ -676,34 +699,40 @@ static int kill_one_process(struct proc* procp, int min_score_adj,
|
|||
}
|
||||
|
||||
/*
|
||||
* Find a process to kill based on the current (possibly estimated) free memory
|
||||
* and cached memory sizes. Returns the size of the killed processes.
|
||||
* Find processes to kill to free required number of pages.
|
||||
* If pages_to_free is set to 0 only one process will be killed.
|
||||
* Returns the size of the killed processes.
|
||||
*/
|
||||
static int find_and_kill_process(enum vmpressure_level level) {
|
||||
static int find_and_kill_processes(enum vmpressure_level level,
|
||||
int pages_to_free) {
|
||||
int i;
|
||||
int killed_size = 0;
|
||||
int killed_size;
|
||||
int pages_freed = 0;
|
||||
int min_score_adj = level_oomadj[level];
|
||||
|
||||
for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
|
||||
struct proc *procp;
|
||||
|
||||
retry:
|
||||
if (kill_heaviest_task)
|
||||
procp = proc_get_heaviest(i);
|
||||
else
|
||||
procp = proc_adj_lru(i);
|
||||
while (true) {
|
||||
if (is_go_device)
|
||||
procp = proc_adj_lru(i);
|
||||
else
|
||||
procp = proc_get_heaviest(i);
|
||||
|
||||
if (!procp)
|
||||
break;
|
||||
|
||||
if (procp) {
|
||||
killed_size = kill_one_process(procp, min_score_adj, level);
|
||||
if (killed_size < 0) {
|
||||
goto retry;
|
||||
} else {
|
||||
return killed_size;
|
||||
if (killed_size >= 0) {
|
||||
pages_freed += killed_size;
|
||||
if (pages_freed >= pages_to_free) {
|
||||
return pages_freed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return pages_freed;
|
||||
}
|
||||
|
||||
static int64_t get_memory_usage(const char* path) {
|
||||
|
@ -730,6 +759,32 @@ static int64_t get_memory_usage(const char* path) {
|
|||
return mem_usage;
|
||||
}
|
||||
|
||||
void record_low_pressure_levels(struct mem_size *free_mem) {
|
||||
if (low_pressure_mem.min_free == -1 ||
|
||||
low_pressure_mem.min_free > free_mem->free_mem) {
|
||||
if (debug_process_killing) {
|
||||
ALOGI("Low pressure min memory update from %d to %d",
|
||||
low_pressure_mem.min_free, free_mem->free_mem);
|
||||
}
|
||||
low_pressure_mem.min_free = free_mem->free_mem;
|
||||
}
|
||||
/*
|
||||
* Free memory at low vmpressure events occasionally gets spikes,
|
||||
* possibly a stale low vmpressure event with memory already
|
||||
* freed up (no memory pressure should have been reported).
|
||||
* Ignore large jumps in max_free that would mess up our stats.
|
||||
*/
|
||||
if (low_pressure_mem.max_free == -1 ||
|
||||
(low_pressure_mem.max_free < free_mem->free_mem &&
|
||||
free_mem->free_mem - low_pressure_mem.max_free < low_pressure_mem.max_free * 0.1)) {
|
||||
if (debug_process_killing) {
|
||||
ALOGI("Low pressure max memory update from %d to %d",
|
||||
low_pressure_mem.max_free, free_mem->free_mem);
|
||||
}
|
||||
low_pressure_mem.max_free = free_mem->free_mem;
|
||||
}
|
||||
}
|
||||
|
||||
enum vmpressure_level upgrade_level(enum vmpressure_level level) {
|
||||
return (enum vmpressure_level)((level < VMPRESS_LEVEL_CRITICAL) ?
|
||||
level + 1 : level);
|
||||
|
@ -746,6 +801,7 @@ static void mp_event_common(enum vmpressure_level level) {
|
|||
int64_t mem_usage, memsw_usage;
|
||||
int64_t mem_pressure;
|
||||
enum vmpressure_level lvl;
|
||||
struct mem_size free_mem;
|
||||
|
||||
/*
|
||||
* Check all event counters from low to critical
|
||||
|
@ -760,6 +816,20 @@ static void mp_event_common(enum vmpressure_level level) {
|
|||
}
|
||||
}
|
||||
|
||||
if (get_free_memory(&free_mem) == 0) {
|
||||
if (level == VMPRESS_LEVEL_LOW) {
|
||||
record_low_pressure_levels(&free_mem);
|
||||
}
|
||||
} else {
|
||||
ALOGE("Failed to get free memory!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (level_oomadj[level] > OOM_SCORE_ADJ_MAX) {
|
||||
/* Do not monitor this pressure level */
|
||||
return;
|
||||
}
|
||||
|
||||
mem_usage = get_memory_usage(MEMCG_MEMORY_USAGE);
|
||||
memsw_usage = get_memory_usage(MEMCG_MEMORYSW_USAGE);
|
||||
if (memsw_usage < 0 || mem_usage < 0) {
|
||||
|
@ -796,9 +866,35 @@ static void mp_event_common(enum vmpressure_level level) {
|
|||
}
|
||||
|
||||
do_kill:
|
||||
if (find_and_kill_process(level) == 0) {
|
||||
if (debug_process_killing) {
|
||||
ALOGI("Nothing to kill");
|
||||
if (is_go_device) {
|
||||
/* For Go devices kill only one task */
|
||||
if (find_and_kill_processes(level, 0) == 0) {
|
||||
if (debug_process_killing) {
|
||||
ALOGI("Nothing to kill");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* If pressure level is less than critical and enough free swap then ignore */
|
||||
if (level < VMPRESS_LEVEL_CRITICAL && free_mem.free_swap > low_pressure_mem.max_free) {
|
||||
if (debug_process_killing) {
|
||||
ALOGI("Ignoring pressure since %d swap pages are available ", free_mem.free_swap);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Free up enough memory to downgrate the memory pressure to low level */
|
||||
if (free_mem.free_mem < low_pressure_mem.max_free) {
|
||||
int pages_to_free = low_pressure_mem.max_free - free_mem.free_mem;
|
||||
if (debug_process_killing) {
|
||||
ALOGI("Trying to free %d pages", pages_to_free);
|
||||
}
|
||||
int pages_freed = find_and_kill_processes(level, pages_to_free);
|
||||
if (pages_freed < pages_to_free) {
|
||||
if (debug_process_killing) {
|
||||
ALOGI("Unable to free enough memory (pages freed=%d)",
|
||||
pages_freed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -824,11 +920,6 @@ static bool init_mp_common(void *event_handler, enum vmpressure_level level) {
|
|||
int ret;
|
||||
const char *levelstr = level_name[level];
|
||||
|
||||
if (level_oomadj[level] > OOM_SCORE_ADJ_MAX) {
|
||||
ALOGI("%s pressure events are disabled", levelstr);
|
||||
return true;
|
||||
}
|
||||
|
||||
mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
|
||||
if (mpfd < 0) {
|
||||
ALOGI("No kernel memory.pressure_level support (errno=%d)", errno);
|
||||
|
|
Loading…
Reference in a new issue