Merge "lmkd: Introduce support for legacy kill algorithm that uses minfree levels"
diff --git a/lmkd/README.md b/lmkd/README.md
index ba2e83d..656a6ea 100644
--- a/lmkd/README.md
+++ b/lmkd/README.md
@@ -29,6 +29,11 @@
   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 5eb3fbe..151e1dc 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -109,6 +109,7 @@
 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 {
@@ -972,11 +973,10 @@
  * 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];
 
     for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
         struct proc *procp;
@@ -1071,9 +1071,14 @@
     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,
@@ -1114,11 +1119,40 @@
         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);
     }
@@ -1167,39 +1201,58 @@
 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;
             }
-            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");
+            }
         }
 
-        /* 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);
+                ALOGI("Unable to free enough memory (pages freed=%d)", pages_freed);
             }
-            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);
-            }
+        } else {
+            gettimeofday(&last_report_tm, NULL);
         }
     }
 }
@@ -1409,6 +1462,8 @@
     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);
 
     if (!init()) {
         if (!use_inkernel_interface) {