diff --git a/lmkd/README.md b/lmkd/README.md index ba2e83d06..656a6ea0a 100644 --- a/lmkd/README.md +++ b/lmkd/README.md @@ -29,6 +29,11 @@ properties: ro.config.low_ram: choose between low-memory vs high-performance device. Default = false. + ro.lmk.use_minfree_levels: use free memory and file cache thresholds for + making decisions when to kill. This mode works + the same way kernel lowmemorykiller driver used + to work. Default = false + ro.lmk.low: min oom_adj score for processes eligible to be killed at low vmpressure level. Default = 1001 (disabled) diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c index 2bf201cee..ec55f908b 100644 --- a/lmkd/lmkd.c +++ b/lmkd/lmkd.c @@ -111,6 +111,7 @@ static int64_t downgrade_pressure; static bool low_ram_device; static bool kill_heaviest_task; static unsigned long kill_timeout_ms; +static bool use_minfree_levels; /* data required to handle events */ struct event_handler_info { @@ -1019,11 +1020,10 @@ static int kill_one_process(struct proc* procp, int min_score_adj, * Returns the size of the killed processes. */ static int find_and_kill_processes(enum vmpressure_level level, - int pages_to_free) { + int min_score_adj, int pages_to_free) { int i; int killed_size; int pages_freed = 0; - int min_score_adj = level_oomadj[level]; #ifdef LMKD_LOG_STATS if (enable_stats_log) { @@ -1137,9 +1137,14 @@ static void mp_event_common(int data, uint32_t events __unused) { int64_t mem_pressure; enum vmpressure_level lvl; union meminfo mi; + union zoneinfo zi; static struct timeval last_report_tm; static unsigned long skip_count = 0; enum vmpressure_level level = (enum vmpressure_level)data; + long other_free = 0, other_file = 0; + int min_score_adj; + int pages_to_free = 0; + int minfree = 0; static struct reread_data mem_usage_file_data = { .filename = MEMCG_MEMORY_USAGE, .fd = -1, @@ -1180,11 +1185,40 @@ static void mp_event_common(int data, uint32_t events __unused) { skip_count = 0; } - if (meminfo_parse(&mi) < 0) { + if (meminfo_parse(&mi) < 0 || zoneinfo_parse(&zi) < 0) { ALOGE("Failed to get free memory!"); return; } + if (use_minfree_levels) { + int i; + + other_free = mi.field.nr_free_pages - zi.field.totalreserve_pages; + if (mi.field.nr_file_pages > (mi.field.shmem + mi.field.unevictable + mi.field.swap_cached)) { + other_file = (mi.field.nr_file_pages - mi.field.shmem - + mi.field.unevictable - mi.field.swap_cached); + } else { + other_file = 0; + } + + min_score_adj = OOM_SCORE_ADJ_MAX + 1; + for (i = 0; i < lowmem_targets_size; i++) { + minfree = lowmem_minfree[i]; + if (other_free < minfree && other_file < minfree) { + min_score_adj = lowmem_adj[i]; + break; + } + } + + if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) + return; + + /* Free up enough pages to push over the highest minfree level */ + pages_to_free = lowmem_minfree[lowmem_targets_size - 1] - + ((other_free < other_file) ? other_free : other_file); + goto do_kill; + } + if (level == VMPRESS_LEVEL_LOW) { record_low_pressure_levels(&mi); } @@ -1233,39 +1267,58 @@ static void mp_event_common(int data, uint32_t events __unused) { do_kill: if (low_ram_device) { /* For Go devices kill only one task */ - if (find_and_kill_processes(level, 0) == 0) { + if (find_and_kill_processes(level, level_oomadj[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 && - mi.field.free_swap > low_pressure_mem.max_nr_free_pages) { - if (debug_process_killing) { - ALOGI("Ignoring pressure since %" PRId64 - " swap pages are available ", - mi.field.free_swap); + int pages_freed; + + if (!use_minfree_levels) { + /* If pressure level is less than critical and enough free swap then ignore */ + if (level < VMPRESS_LEVEL_CRITICAL && + mi.field.free_swap > low_pressure_mem.max_nr_free_pages) { + if (debug_process_killing) { + ALOGI("Ignoring pressure since %" PRId64 + " swap pages are available ", + mi.field.free_swap); + } + return; + } + /* Free up enough memory to downgrate the memory pressure to low level */ + if (mi.field.nr_free_pages < low_pressure_mem.max_nr_free_pages) { + pages_to_free = low_pressure_mem.max_nr_free_pages - + mi.field.nr_free_pages; + } else { + if (debug_process_killing) { + ALOGI("Ignoring pressure since more memory is " + "available (%" PRId64 ") than watermark (%" PRId64 ")", + mi.field.nr_free_pages, low_pressure_mem.max_nr_free_pages); + } + return; + } + min_score_adj = level_oomadj[level]; + } else { + if (debug_process_killing) { + ALOGI("Killing because cache %ldkB is below " + "limit %ldkB for oom_adj %d\n" + " Free memory is %ldkB %s reserved", + other_file * page_k, minfree * page_k, min_score_adj, + other_free * page_k, other_free >= 0 ? "above" : "below"); } - return; } - /* Free up enough memory to downgrate the memory pressure to low level */ - if (mi.field.nr_free_pages < low_pressure_mem.max_nr_free_pages) { - int pages_to_free = low_pressure_mem.max_nr_free_pages - - mi.field.nr_free_pages; + if (debug_process_killing) { + ALOGI("Trying to free %d pages", pages_to_free); + } + pages_freed = find_and_kill_processes(level, min_score_adj, pages_to_free); + if (pages_freed < pages_to_free) { 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); - } - } else { - gettimeofday(&last_report_tm, NULL); + ALOGI("Unable to free enough memory (pages freed=%d)", pages_freed); } + } else { + gettimeofday(&last_report_tm, NULL); } } } @@ -1475,6 +1528,8 @@ int main(int argc __unused, char **argv __unused) { low_ram_device = property_get_bool("ro.config.low_ram", false); kill_timeout_ms = (unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 0); + use_minfree_levels = + property_get_bool("ro.lmk.use_minfree_levels", false); #ifdef LMKD_LOG_STATS statslog_init(&log_ctx, &enable_stats_log);