Merge "lmkd: Allow killing multiple processes to downgrade memory pressure"
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 1b8fed3..ece4dff 100644
--- a/lmkd/lmkd.c
+++ b/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 @@
     "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 @@
     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 @@
 }
 
 /*
- * 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) {
+            if (!procp)
+                break;
+
             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 @@
     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 @@
     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 @@
         }
     }
 
+    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 @@
     }
 
 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 @@
     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);