Loading lmkd/README.md +5 −0 Original line number Original line Diff line number Diff line Loading @@ -29,6 +29,11 @@ properties: ro.config.low_ram: choose between low-memory vs high-performance ro.config.low_ram: choose between low-memory vs high-performance device. Default = false. 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 ro.lmk.low: min oom_adj score for processes eligible to be killed at low vmpressure level. Default = 1001 killed at low vmpressure level. Default = 1001 (disabled) (disabled) Loading lmkd/lmkd.c +80 −25 Original line number Original line Diff line number Diff line Loading @@ -109,6 +109,7 @@ static int64_t downgrade_pressure; static bool low_ram_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; static bool use_minfree_levels; /* data required to handle events */ /* data required to handle events */ struct event_handler_info { struct event_handler_info { Loading Loading @@ -972,11 +973,10 @@ static int kill_one_process(struct proc* procp, int min_score_adj, * Returns the size of the killed processes. * Returns the size of the killed processes. */ */ static int find_and_kill_processes(enum vmpressure_level level, 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 i; int killed_size; int killed_size; int pages_freed = 0; int pages_freed = 0; int min_score_adj = level_oomadj[level]; for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) { for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) { struct proc *procp; struct proc *procp; Loading Loading @@ -1071,9 +1071,14 @@ static void mp_event_common(int data, uint32_t events __unused) { int64_t mem_pressure; int64_t mem_pressure; enum vmpressure_level lvl; enum vmpressure_level lvl; union meminfo mi; union meminfo mi; union zoneinfo zi; 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; 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 = { static struct reread_data mem_usage_file_data = { .filename = MEMCG_MEMORY_USAGE, .filename = MEMCG_MEMORY_USAGE, .fd = -1, .fd = -1, Loading Loading @@ -1114,11 +1119,40 @@ static void mp_event_common(int data, uint32_t events __unused) { skip_count = 0; skip_count = 0; } } if (meminfo_parse(&mi) < 0) { if (meminfo_parse(&mi) < 0 || zoneinfo_parse(&zi) < 0) { ALOGE("Failed to get free memory!"); ALOGE("Failed to get free memory!"); return; 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) { if (level == VMPRESS_LEVEL_LOW) { record_low_pressure_levels(&mi); record_low_pressure_levels(&mi); } } Loading Loading @@ -1167,12 +1201,15 @@ static void mp_event_common(int data, uint32_t events __unused) { do_kill: do_kill: if (low_ram_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, level_oomadj[level], 0) == 0) { if (debug_process_killing) { if (debug_process_killing) { ALOGI("Nothing to kill"); ALOGI("Nothing to kill"); } } } } } else { } else { int pages_freed; if (!use_minfree_levels) { /* If pressure level is less than critical and enough free swap then ignore */ /* If pressure level is less than critical and enough free swap then ignore */ if (level < VMPRESS_LEVEL_CRITICAL && if (level < VMPRESS_LEVEL_CRITICAL && mi.field.free_swap > low_pressure_mem.max_nr_free_pages) { mi.field.free_swap > low_pressure_mem.max_nr_free_pages) { Loading @@ -1183,26 +1220,42 @@ do_kill: } } return; return; } } /* Free up enough memory to downgrate the memory pressure to low level */ /* 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) { if (mi.field.nr_free_pages < low_pressure_mem.max_nr_free_pages) { int pages_to_free = low_pressure_mem.max_nr_free_pages - pages_to_free = low_pressure_mem.max_nr_free_pages - mi.field.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"); } } if (debug_process_killing) { if (debug_process_killing) { ALOGI("Trying to free %d pages", pages_to_free); ALOGI("Trying to free %d pages", pages_to_free); } } int pages_freed = find_and_kill_processes(level, pages_to_free); pages_freed = find_and_kill_processes(level, min_score_adj, pages_to_free); if (pages_freed < pages_to_free) { if (pages_freed < pages_to_free) { if (debug_process_killing) { if (debug_process_killing) { ALOGI("Unable to free enough memory (pages freed=%d)", ALOGI("Unable to free enough memory (pages freed=%d)", pages_freed); pages_freed); } } } else { } else { gettimeofday(&last_report_tm, NULL); gettimeofday(&last_report_tm, NULL); } } } } } } } static bool init_mp_common(enum vmpressure_level level) { static bool init_mp_common(enum vmpressure_level level) { int mpfd; int mpfd; Loading Loading @@ -1409,6 +1462,8 @@ int main(int argc __unused, char **argv __unused) { low_ram_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); use_minfree_levels = property_get_bool("ro.lmk.use_minfree_levels", false); if (!init()) { if (!init()) { if (!use_inkernel_interface) { if (!use_inkernel_interface) { Loading Loading
lmkd/README.md +5 −0 Original line number Original line Diff line number Diff line Loading @@ -29,6 +29,11 @@ properties: ro.config.low_ram: choose between low-memory vs high-performance ro.config.low_ram: choose between low-memory vs high-performance device. Default = false. 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 ro.lmk.low: min oom_adj score for processes eligible to be killed at low vmpressure level. Default = 1001 killed at low vmpressure level. Default = 1001 (disabled) (disabled) Loading
lmkd/lmkd.c +80 −25 Original line number Original line Diff line number Diff line Loading @@ -109,6 +109,7 @@ static int64_t downgrade_pressure; static bool low_ram_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; static bool use_minfree_levels; /* data required to handle events */ /* data required to handle events */ struct event_handler_info { struct event_handler_info { Loading Loading @@ -972,11 +973,10 @@ static int kill_one_process(struct proc* procp, int min_score_adj, * Returns the size of the killed processes. * Returns the size of the killed processes. */ */ static int find_and_kill_processes(enum vmpressure_level level, 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 i; int killed_size; int killed_size; int pages_freed = 0; int pages_freed = 0; int min_score_adj = level_oomadj[level]; for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) { for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) { struct proc *procp; struct proc *procp; Loading Loading @@ -1071,9 +1071,14 @@ static void mp_event_common(int data, uint32_t events __unused) { int64_t mem_pressure; int64_t mem_pressure; enum vmpressure_level lvl; enum vmpressure_level lvl; union meminfo mi; union meminfo mi; union zoneinfo zi; 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; 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 = { static struct reread_data mem_usage_file_data = { .filename = MEMCG_MEMORY_USAGE, .filename = MEMCG_MEMORY_USAGE, .fd = -1, .fd = -1, Loading Loading @@ -1114,11 +1119,40 @@ static void mp_event_common(int data, uint32_t events __unused) { skip_count = 0; skip_count = 0; } } if (meminfo_parse(&mi) < 0) { if (meminfo_parse(&mi) < 0 || zoneinfo_parse(&zi) < 0) { ALOGE("Failed to get free memory!"); ALOGE("Failed to get free memory!"); return; 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) { if (level == VMPRESS_LEVEL_LOW) { record_low_pressure_levels(&mi); record_low_pressure_levels(&mi); } } Loading Loading @@ -1167,12 +1201,15 @@ static void mp_event_common(int data, uint32_t events __unused) { do_kill: do_kill: if (low_ram_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, level_oomadj[level], 0) == 0) { if (debug_process_killing) { if (debug_process_killing) { ALOGI("Nothing to kill"); ALOGI("Nothing to kill"); } } } } } else { } else { int pages_freed; if (!use_minfree_levels) { /* If pressure level is less than critical and enough free swap then ignore */ /* If pressure level is less than critical and enough free swap then ignore */ if (level < VMPRESS_LEVEL_CRITICAL && if (level < VMPRESS_LEVEL_CRITICAL && mi.field.free_swap > low_pressure_mem.max_nr_free_pages) { mi.field.free_swap > low_pressure_mem.max_nr_free_pages) { Loading @@ -1183,26 +1220,42 @@ do_kill: } } return; return; } } /* Free up enough memory to downgrate the memory pressure to low level */ /* 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) { if (mi.field.nr_free_pages < low_pressure_mem.max_nr_free_pages) { int pages_to_free = low_pressure_mem.max_nr_free_pages - pages_to_free = low_pressure_mem.max_nr_free_pages - mi.field.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"); } } if (debug_process_killing) { if (debug_process_killing) { ALOGI("Trying to free %d pages", pages_to_free); ALOGI("Trying to free %d pages", pages_to_free); } } int pages_freed = find_and_kill_processes(level, pages_to_free); pages_freed = find_and_kill_processes(level, min_score_adj, pages_to_free); if (pages_freed < pages_to_free) { if (pages_freed < pages_to_free) { if (debug_process_killing) { if (debug_process_killing) { ALOGI("Unable to free enough memory (pages freed=%d)", ALOGI("Unable to free enough memory (pages freed=%d)", pages_freed); pages_freed); } } } else { } else { gettimeofday(&last_report_tm, NULL); gettimeofday(&last_report_tm, NULL); } } } } } } } static bool init_mp_common(enum vmpressure_level level) { static bool init_mp_common(enum vmpressure_level level) { int mpfd; int mpfd; Loading Loading @@ -1409,6 +1462,8 @@ int main(int argc __unused, char **argv __unused) { low_ram_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); use_minfree_levels = property_get_bool("ro.lmk.use_minfree_levels", false); if (!init()) { if (!init()) { if (!use_inkernel_interface) { if (!use_inkernel_interface) { Loading