| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2013 The Android Open Source Project | 
 | 3 |  * | 
 | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #define LOG_TAG "lowmemorykiller" | 
 | 18 |  | 
| Mark Salyzyn | e6ed68b | 2014-04-30 13:36:35 -0700 | [diff] [blame] | 19 | #include <arpa/inet.h> | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 20 | #include <errno.h> | 
| Robert Benea | c47f299 | 2017-08-21 15:18:31 -0700 | [diff] [blame] | 21 | #include <inttypes.h> | 
| Mark Salyzyn | cfd5b08 | 2016-10-17 14:28:00 -0700 | [diff] [blame] | 22 | #include <sched.h> | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 23 | #include <signal.h> | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 24 | #include <stdlib.h> | 
 | 25 | #include <string.h> | 
| Mark Salyzyn | e6ed68b | 2014-04-30 13:36:35 -0700 | [diff] [blame] | 26 | #include <sys/cdefs.h> | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 27 | #include <sys/epoll.h> | 
 | 28 | #include <sys/eventfd.h> | 
| Colin Cross | b28ff91 | 2014-07-11 17:15:44 -0700 | [diff] [blame] | 29 | #include <sys/mman.h> | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 30 | #include <sys/socket.h> | 
 | 31 | #include <sys/types.h> | 
| Robert Benea | c47f299 | 2017-08-21 15:18:31 -0700 | [diff] [blame] | 32 | #include <time.h> | 
| Mark Salyzyn | e6ed68b | 2014-04-30 13:36:35 -0700 | [diff] [blame] | 33 | #include <unistd.h> | 
 | 34 |  | 
| Robert Benea | 58891d5 | 2017-07-31 17:15:20 -0700 | [diff] [blame] | 35 | #include <cutils/properties.h> | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 36 | #include <cutils/sockets.h> | 
| Mark Salyzyn | 30f991f | 2017-01-10 13:19:54 -0800 | [diff] [blame] | 37 | #include <log/log.h> | 
| Colin Cross | fef9522 | 2014-06-11 14:53:41 -0700 | [diff] [blame] | 38 | #include <processgroup/processgroup.h> | 
| Mark Salyzyn | e6ed68b | 2014-04-30 13:36:35 -0700 | [diff] [blame] | 39 |  | 
 | 40 | #ifndef __unused | 
 | 41 | #define __unused __attribute__((__unused__)) | 
 | 42 | #endif | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 43 |  | 
 | 44 | #define MEMCG_SYSFS_PATH "/dev/memcg/" | 
| Robert Benea | c47f299 | 2017-08-21 15:18:31 -0700 | [diff] [blame] | 45 | #define MEMCG_MEMORY_USAGE "/dev/memcg/memory.usage_in_bytes" | 
 | 46 | #define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes" | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 47 | #define MEMPRESSURE_WATCH_MEDIUM_LEVEL "medium" | 
 | 48 | #define MEMPRESSURE_WATCH_CRITICAL_LEVEL "critical" | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 49 | #define ZONEINFO_PATH "/proc/zoneinfo" | 
 | 50 | #define LINE_MAX 128 | 
 | 51 |  | 
 | 52 | #define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree" | 
 | 53 | #define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj" | 
 | 54 |  | 
 | 55 | #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(*(x))) | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 56 | #define EIGHT_MEGA (1 << 23) | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 57 |  | 
 | 58 | enum lmk_cmd { | 
 | 59 |     LMK_TARGET, | 
 | 60 |     LMK_PROCPRIO, | 
 | 61 |     LMK_PROCREMOVE, | 
 | 62 | }; | 
 | 63 |  | 
 | 64 | #define MAX_TARGETS 6 | 
 | 65 | /* | 
 | 66 |  * longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio | 
 | 67 |  * values | 
 | 68 |  */ | 
 | 69 | #define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1)) | 
 | 70 |  | 
 | 71 | /* default to old in-kernel interface if no memory pressure events */ | 
 | 72 | static int use_inkernel_interface = 1; | 
| Robert Benea | 164baeb | 2017-09-11 16:53:28 -0700 | [diff] [blame] | 73 | static bool has_inkernel_module; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 74 |  | 
 | 75 | /* memory pressure level medium event */ | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 76 | static int mpevfd[2]; | 
 | 77 | #define CRITICAL_INDEX 1 | 
 | 78 | #define MEDIUM_INDEX 0 | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 79 |  | 
| Robert Benea | 58891d5 | 2017-07-31 17:15:20 -0700 | [diff] [blame] | 80 | static int medium_oomadj; | 
 | 81 | static int critical_oomadj; | 
| Robert Benea | c47f299 | 2017-08-21 15:18:31 -0700 | [diff] [blame] | 82 | static bool debug_process_killing; | 
 | 83 | static bool enable_pressure_upgrade; | 
 | 84 | static int64_t upgrade_pressure; | 
| Robert Benea | 6e8e710 | 2017-09-13 15:20:30 -0700 | [diff] [blame] | 85 | static int64_t downgrade_pressure; | 
| Robert Benea | 164baeb | 2017-09-11 16:53:28 -0700 | [diff] [blame] | 86 | static bool is_go_device; | 
| Robert Benea | 58891d5 | 2017-07-31 17:15:20 -0700 | [diff] [blame] | 87 |  | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 88 | /* control socket listen and data */ | 
 | 89 | static int ctrl_lfd; | 
 | 90 | static int ctrl_dfd = -1; | 
 | 91 | static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */ | 
 | 92 |  | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 93 | /* 2 memory pressure levels, 1 ctrl listen socket, 1 ctrl data socket */ | 
 | 94 | #define MAX_EPOLL_EVENTS 4 | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 95 | static int epollfd; | 
 | 96 | static int maxevents; | 
 | 97 |  | 
| Chong Zhang | 0a4acdf | 2015-10-14 16:19:53 -0700 | [diff] [blame] | 98 | /* OOM score values used by both kernel and framework */ | 
| Todd Poynor | 16b6099 | 2013-09-16 19:26:47 -0700 | [diff] [blame] | 99 | #define OOM_SCORE_ADJ_MIN       (-1000) | 
 | 100 | #define OOM_SCORE_ADJ_MAX       1000 | 
 | 101 |  | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 102 | static int lowmem_adj[MAX_TARGETS]; | 
 | 103 | static int lowmem_minfree[MAX_TARGETS]; | 
 | 104 | static int lowmem_targets_size; | 
 | 105 |  | 
 | 106 | struct sysmeminfo { | 
 | 107 |     int nr_free_pages; | 
 | 108 |     int nr_file_pages; | 
 | 109 |     int nr_shmem; | 
 | 110 |     int totalreserve_pages; | 
 | 111 | }; | 
 | 112 |  | 
 | 113 | struct adjslot_list { | 
 | 114 |     struct adjslot_list *next; | 
 | 115 |     struct adjslot_list *prev; | 
 | 116 | }; | 
 | 117 |  | 
 | 118 | struct proc { | 
 | 119 |     struct adjslot_list asl; | 
 | 120 |     int pid; | 
| Colin Cross | fbb78c6 | 2014-06-13 14:52:43 -0700 | [diff] [blame] | 121 |     uid_t uid; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 122 |     int oomadj; | 
 | 123 |     struct proc *pidhash_next; | 
 | 124 | }; | 
 | 125 |  | 
 | 126 | #define PIDHASH_SZ 1024 | 
 | 127 | static struct proc *pidhash[PIDHASH_SZ]; | 
 | 128 | #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) | 
 | 129 |  | 
| Chih-Hung Hsieh | daa13ea | 2016-05-19 16:02:22 -0700 | [diff] [blame] | 130 | #define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN) | 
| Chong Zhang | 0a4acdf | 2015-10-14 16:19:53 -0700 | [diff] [blame] | 131 | static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1]; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 132 |  | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 133 | /* PAGE_SIZE / 1024 */ | 
 | 134 | static long page_k; | 
 | 135 |  | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 136 | static ssize_t read_all(int fd, char *buf, size_t max_len) | 
 | 137 | { | 
 | 138 |     ssize_t ret = 0; | 
 | 139 |  | 
 | 140 |     while (max_len > 0) { | 
 | 141 |         ssize_t r = read(fd, buf, max_len); | 
 | 142 |         if (r == 0) { | 
 | 143 |             break; | 
 | 144 |         } | 
 | 145 |         if (r == -1) { | 
 | 146 |             return -1; | 
 | 147 |         } | 
 | 148 |         ret += r; | 
 | 149 |         buf += r; | 
 | 150 |         max_len -= r; | 
 | 151 |     } | 
 | 152 |  | 
 | 153 |     return ret; | 
 | 154 | } | 
 | 155 |  | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 156 | static struct proc *pid_lookup(int pid) { | 
 | 157 |     struct proc *procp; | 
 | 158 |  | 
 | 159 |     for (procp = pidhash[pid_hashfn(pid)]; procp && procp->pid != pid; | 
 | 160 |          procp = procp->pidhash_next) | 
 | 161 |             ; | 
 | 162 |  | 
 | 163 |     return procp; | 
 | 164 | } | 
 | 165 |  | 
 | 166 | static void adjslot_insert(struct adjslot_list *head, struct adjslot_list *new) | 
 | 167 | { | 
 | 168 |     struct adjslot_list *next = head->next; | 
 | 169 |     new->prev = head; | 
 | 170 |     new->next = next; | 
 | 171 |     next->prev = new; | 
 | 172 |     head->next = new; | 
 | 173 | } | 
 | 174 |  | 
 | 175 | static void adjslot_remove(struct adjslot_list *old) | 
 | 176 | { | 
 | 177 |     struct adjslot_list *prev = old->prev; | 
 | 178 |     struct adjslot_list *next = old->next; | 
 | 179 |     next->prev = prev; | 
 | 180 |     prev->next = next; | 
 | 181 | } | 
 | 182 |  | 
 | 183 | static struct adjslot_list *adjslot_tail(struct adjslot_list *head) { | 
 | 184 |     struct adjslot_list *asl = head->prev; | 
 | 185 |  | 
 | 186 |     return asl == head ? NULL : asl; | 
 | 187 | } | 
 | 188 |  | 
 | 189 | static void proc_slot(struct proc *procp) { | 
 | 190 |     int adjslot = ADJTOSLOT(procp->oomadj); | 
 | 191 |  | 
 | 192 |     adjslot_insert(&procadjslot_list[adjslot], &procp->asl); | 
 | 193 | } | 
 | 194 |  | 
 | 195 | static void proc_unslot(struct proc *procp) { | 
 | 196 |     adjslot_remove(&procp->asl); | 
 | 197 | } | 
 | 198 |  | 
 | 199 | static void proc_insert(struct proc *procp) { | 
 | 200 |     int hval = pid_hashfn(procp->pid); | 
 | 201 |  | 
 | 202 |     procp->pidhash_next = pidhash[hval]; | 
 | 203 |     pidhash[hval] = procp; | 
 | 204 |     proc_slot(procp); | 
 | 205 | } | 
 | 206 |  | 
 | 207 | static int pid_remove(int pid) { | 
 | 208 |     int hval = pid_hashfn(pid); | 
 | 209 |     struct proc *procp; | 
 | 210 |     struct proc *prevp; | 
 | 211 |  | 
 | 212 |     for (procp = pidhash[hval], prevp = NULL; procp && procp->pid != pid; | 
 | 213 |          procp = procp->pidhash_next) | 
 | 214 |             prevp = procp; | 
 | 215 |  | 
 | 216 |     if (!procp) | 
 | 217 |         return -1; | 
 | 218 |  | 
 | 219 |     if (!prevp) | 
 | 220 |         pidhash[hval] = procp->pidhash_next; | 
 | 221 |     else | 
 | 222 |         prevp->pidhash_next = procp->pidhash_next; | 
 | 223 |  | 
 | 224 |     proc_unslot(procp); | 
 | 225 |     free(procp); | 
 | 226 |     return 0; | 
 | 227 | } | 
 | 228 |  | 
 | 229 | static void writefilestring(char *path, char *s) { | 
| Nick Kralevich | c68c886 | 2015-12-18 20:52:37 -0800 | [diff] [blame] | 230 |     int fd = open(path, O_WRONLY | O_CLOEXEC); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 231 |     int len = strlen(s); | 
 | 232 |     int ret; | 
 | 233 |  | 
 | 234 |     if (fd < 0) { | 
 | 235 |         ALOGE("Error opening %s; errno=%d", path, errno); | 
 | 236 |         return; | 
 | 237 |     } | 
 | 238 |  | 
 | 239 |     ret = write(fd, s, len); | 
 | 240 |     if (ret < 0) { | 
 | 241 |         ALOGE("Error writing %s; errno=%d", path, errno); | 
 | 242 |     } else if (ret < len) { | 
 | 243 |         ALOGE("Short write on %s; length=%d", path, ret); | 
 | 244 |     } | 
 | 245 |  | 
 | 246 |     close(fd); | 
 | 247 | } | 
 | 248 |  | 
| Colin Cross | fbb78c6 | 2014-06-13 14:52:43 -0700 | [diff] [blame] | 249 | static void cmd_procprio(int pid, int uid, int oomadj) { | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 250 |     struct proc *procp; | 
 | 251 |     char path[80]; | 
 | 252 |     char val[20]; | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 253 |     int soft_limit_mult; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 254 |  | 
| Chong Zhang | 0a4acdf | 2015-10-14 16:19:53 -0700 | [diff] [blame] | 255 |     if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) { | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 256 |         ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj); | 
 | 257 |         return; | 
 | 258 |     } | 
 | 259 |  | 
| Todd Poynor | 16b6099 | 2013-09-16 19:26:47 -0700 | [diff] [blame] | 260 |     snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid); | 
| Chong Zhang | 0a4acdf | 2015-10-14 16:19:53 -0700 | [diff] [blame] | 261 |     snprintf(val, sizeof(val), "%d", oomadj); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 262 |     writefilestring(path, val); | 
 | 263 |  | 
 | 264 |     if (use_inkernel_interface) | 
 | 265 |         return; | 
 | 266 |  | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 267 |     if (oomadj >= 900) { | 
 | 268 |         soft_limit_mult = 0; | 
 | 269 |     } else if (oomadj >= 800) { | 
 | 270 |         soft_limit_mult = 0; | 
 | 271 |     } else if (oomadj >= 700) { | 
 | 272 |         soft_limit_mult = 0; | 
 | 273 |     } else if (oomadj >= 600) { | 
| Robert Benea | caeaa65 | 2017-08-11 16:03:20 -0700 | [diff] [blame] | 274 |         // Launcher should be perceptible, don't kill it. | 
 | 275 |         oomadj = 200; | 
 | 276 |         soft_limit_mult = 1; | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 277 |     } else if (oomadj >= 500) { | 
 | 278 |         soft_limit_mult = 0; | 
 | 279 |     } else if (oomadj >= 400) { | 
 | 280 |         soft_limit_mult = 0; | 
 | 281 |     } else if (oomadj >= 300) { | 
 | 282 |         soft_limit_mult = 1; | 
 | 283 |     } else if (oomadj >= 200) { | 
 | 284 |         soft_limit_mult = 2; | 
 | 285 |     } else if (oomadj >= 100) { | 
 | 286 |         soft_limit_mult = 10; | 
 | 287 |     } else if (oomadj >=   0) { | 
 | 288 |         soft_limit_mult = 20; | 
 | 289 |     } else { | 
 | 290 |         // Persistent processes will have a large | 
 | 291 |         // soft limit 512MB. | 
 | 292 |         soft_limit_mult = 64; | 
 | 293 |     } | 
 | 294 |  | 
 | 295 |     snprintf(path, sizeof(path), "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes", uid, pid); | 
 | 296 |     snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA); | 
 | 297 |     writefilestring(path, val); | 
 | 298 |  | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 299 |     procp = pid_lookup(pid); | 
 | 300 |     if (!procp) { | 
 | 301 |             procp = malloc(sizeof(struct proc)); | 
 | 302 |             if (!procp) { | 
 | 303 |                 // Oh, the irony.  May need to rebuild our state. | 
 | 304 |                 return; | 
 | 305 |             } | 
 | 306 |  | 
 | 307 |             procp->pid = pid; | 
| Colin Cross | fbb78c6 | 2014-06-13 14:52:43 -0700 | [diff] [blame] | 308 |             procp->uid = uid; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 309 |             procp->oomadj = oomadj; | 
 | 310 |             proc_insert(procp); | 
 | 311 |     } else { | 
 | 312 |         proc_unslot(procp); | 
 | 313 |         procp->oomadj = oomadj; | 
 | 314 |         proc_slot(procp); | 
 | 315 |     } | 
 | 316 | } | 
 | 317 |  | 
 | 318 | static void cmd_procremove(int pid) { | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 319 |     if (use_inkernel_interface) | 
 | 320 |         return; | 
 | 321 |  | 
 | 322 |     pid_remove(pid); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 323 | } | 
 | 324 |  | 
 | 325 | static void cmd_target(int ntargets, int *params) { | 
 | 326 |     int i; | 
 | 327 |  | 
 | 328 |     if (ntargets > (int)ARRAY_SIZE(lowmem_adj)) | 
 | 329 |         return; | 
 | 330 |  | 
 | 331 |     for (i = 0; i < ntargets; i++) { | 
 | 332 |         lowmem_minfree[i] = ntohl(*params++); | 
 | 333 |         lowmem_adj[i] = ntohl(*params++); | 
 | 334 |     } | 
 | 335 |  | 
 | 336 |     lowmem_targets_size = ntargets; | 
 | 337 |  | 
| Robert Benea | 164baeb | 2017-09-11 16:53:28 -0700 | [diff] [blame] | 338 |     if (has_inkernel_module) { | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 339 |         char minfreestr[128]; | 
 | 340 |         char killpriostr[128]; | 
 | 341 |  | 
 | 342 |         minfreestr[0] = '\0'; | 
 | 343 |         killpriostr[0] = '\0'; | 
 | 344 |  | 
 | 345 |         for (i = 0; i < lowmem_targets_size; i++) { | 
 | 346 |             char val[40]; | 
 | 347 |  | 
 | 348 |             if (i) { | 
 | 349 |                 strlcat(minfreestr, ",", sizeof(minfreestr)); | 
 | 350 |                 strlcat(killpriostr, ",", sizeof(killpriostr)); | 
 | 351 |             } | 
 | 352 |  | 
| Robert Benea | 164baeb | 2017-09-11 16:53:28 -0700 | [diff] [blame] | 353 |             snprintf(val, sizeof(val), "%d", use_inkernel_interface ? lowmem_minfree[i] : 0); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 354 |             strlcat(minfreestr, val, sizeof(minfreestr)); | 
| Robert Benea | 164baeb | 2017-09-11 16:53:28 -0700 | [diff] [blame] | 355 |             snprintf(val, sizeof(val), "%d", use_inkernel_interface ? lowmem_adj[i] : 0); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 356 |             strlcat(killpriostr, val, sizeof(killpriostr)); | 
 | 357 |         } | 
 | 358 |  | 
 | 359 |         writefilestring(INKERNEL_MINFREE_PATH, minfreestr); | 
 | 360 |         writefilestring(INKERNEL_ADJ_PATH, killpriostr); | 
 | 361 |     } | 
 | 362 | } | 
 | 363 |  | 
 | 364 | static void ctrl_data_close(void) { | 
 | 365 |     ALOGI("Closing Activity Manager data connection"); | 
 | 366 |     close(ctrl_dfd); | 
 | 367 |     ctrl_dfd = -1; | 
 | 368 |     maxevents--; | 
 | 369 | } | 
 | 370 |  | 
 | 371 | static int ctrl_data_read(char *buf, size_t bufsz) { | 
 | 372 |     int ret = 0; | 
 | 373 |  | 
 | 374 |     ret = read(ctrl_dfd, buf, bufsz); | 
 | 375 |  | 
 | 376 |     if (ret == -1) { | 
 | 377 |         ALOGE("control data socket read failed; errno=%d", errno); | 
 | 378 |     } else if (ret == 0) { | 
 | 379 |         ALOGE("Got EOF on control data socket"); | 
 | 380 |         ret = -1; | 
 | 381 |     } | 
 | 382 |  | 
 | 383 |     return ret; | 
 | 384 | } | 
 | 385 |  | 
 | 386 | static void ctrl_command_handler(void) { | 
 | 387 |     int ibuf[CTRL_PACKET_MAX / sizeof(int)]; | 
 | 388 |     int len; | 
 | 389 |     int cmd = -1; | 
 | 390 |     int nargs; | 
 | 391 |     int targets; | 
 | 392 |  | 
 | 393 |     len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX); | 
 | 394 |     if (len <= 0) | 
 | 395 |         return; | 
 | 396 |  | 
 | 397 |     nargs = len / sizeof(int) - 1; | 
 | 398 |     if (nargs < 0) | 
 | 399 |         goto wronglen; | 
 | 400 |  | 
 | 401 |     cmd = ntohl(ibuf[0]); | 
 | 402 |  | 
 | 403 |     switch(cmd) { | 
 | 404 |     case LMK_TARGET: | 
 | 405 |         targets = nargs / 2; | 
 | 406 |         if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj)) | 
 | 407 |             goto wronglen; | 
 | 408 |         cmd_target(targets, &ibuf[1]); | 
 | 409 |         break; | 
 | 410 |     case LMK_PROCPRIO: | 
| Colin Cross | fbb78c6 | 2014-06-13 14:52:43 -0700 | [diff] [blame] | 411 |         if (nargs != 3) | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 412 |             goto wronglen; | 
| Colin Cross | fbb78c6 | 2014-06-13 14:52:43 -0700 | [diff] [blame] | 413 |         cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3])); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 414 |         break; | 
 | 415 |     case LMK_PROCREMOVE: | 
 | 416 |         if (nargs != 1) | 
 | 417 |             goto wronglen; | 
 | 418 |         cmd_procremove(ntohl(ibuf[1])); | 
 | 419 |         break; | 
 | 420 |     default: | 
 | 421 |         ALOGE("Received unknown command code %d", cmd); | 
 | 422 |         return; | 
 | 423 |     } | 
 | 424 |  | 
 | 425 |     return; | 
 | 426 |  | 
 | 427 | wronglen: | 
 | 428 |     ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len); | 
 | 429 | } | 
 | 430 |  | 
 | 431 | static void ctrl_data_handler(uint32_t events) { | 
 | 432 |     if (events & EPOLLHUP) { | 
 | 433 |         ALOGI("ActivityManager disconnected"); | 
 | 434 |         if (!ctrl_dfd_reopened) | 
 | 435 |             ctrl_data_close(); | 
 | 436 |     } else if (events & EPOLLIN) { | 
 | 437 |         ctrl_command_handler(); | 
 | 438 |     } | 
 | 439 | } | 
 | 440 |  | 
| Mark Salyzyn | e6ed68b | 2014-04-30 13:36:35 -0700 | [diff] [blame] | 441 | static void ctrl_connect_handler(uint32_t events __unused) { | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 442 |     struct epoll_event epev; | 
 | 443 |  | 
 | 444 |     if (ctrl_dfd >= 0) { | 
 | 445 |         ctrl_data_close(); | 
 | 446 |         ctrl_dfd_reopened = 1; | 
 | 447 |     } | 
 | 448 |  | 
| Elliott Hughes | 3dcfa3f | 2016-08-23 12:50:00 -0700 | [diff] [blame] | 449 |     ctrl_dfd = accept(ctrl_lfd, NULL, NULL); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 450 |  | 
 | 451 |     if (ctrl_dfd < 0) { | 
 | 452 |         ALOGE("lmkd control socket accept failed; errno=%d", errno); | 
 | 453 |         return; | 
 | 454 |     } | 
 | 455 |  | 
 | 456 |     ALOGI("ActivityManager connected"); | 
 | 457 |     maxevents++; | 
 | 458 |     epev.events = EPOLLIN; | 
 | 459 |     epev.data.ptr = (void *)ctrl_data_handler; | 
 | 460 |     if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_dfd, &epev) == -1) { | 
 | 461 |         ALOGE("epoll_ctl for data connection socket failed; errno=%d", errno); | 
 | 462 |         ctrl_data_close(); | 
 | 463 |         return; | 
 | 464 |     } | 
 | 465 | } | 
 | 466 |  | 
 | 467 | static int zoneinfo_parse_protection(char *cp) { | 
 | 468 |     int max = 0; | 
 | 469 |     int zoneval; | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 470 |     char *save_ptr; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 471 |  | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 472 |     for (cp = strtok_r(cp, "(), ", &save_ptr); cp; cp = strtok_r(NULL, "), ", &save_ptr)) { | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 473 |         zoneval = strtol(cp, &cp, 0); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 474 |         if (zoneval > max) | 
 | 475 |             max = zoneval; | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 476 |     } | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 477 |  | 
 | 478 |     return max; | 
 | 479 | } | 
 | 480 |  | 
 | 481 | static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) { | 
 | 482 |     char *cp = line; | 
 | 483 |     char *ap; | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 484 |     char *save_ptr; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 485 |  | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 486 |     cp = strtok_r(line, " ", &save_ptr); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 487 |     if (!cp) | 
 | 488 |         return; | 
 | 489 |  | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 490 |     ap = strtok_r(NULL, " ", &save_ptr); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 491 |     if (!ap) | 
 | 492 |         return; | 
 | 493 |  | 
 | 494 |     if (!strcmp(cp, "nr_free_pages")) | 
 | 495 |         mip->nr_free_pages += strtol(ap, NULL, 0); | 
 | 496 |     else if (!strcmp(cp, "nr_file_pages")) | 
 | 497 |         mip->nr_file_pages += strtol(ap, NULL, 0); | 
 | 498 |     else if (!strcmp(cp, "nr_shmem")) | 
 | 499 |         mip->nr_shmem += strtol(ap, NULL, 0); | 
 | 500 |     else if (!strcmp(cp, "high")) | 
 | 501 |         mip->totalreserve_pages += strtol(ap, NULL, 0); | 
 | 502 |     else if (!strcmp(cp, "protection:")) | 
 | 503 |         mip->totalreserve_pages += zoneinfo_parse_protection(ap); | 
 | 504 | } | 
 | 505 |  | 
 | 506 | static int zoneinfo_parse(struct sysmeminfo *mip) { | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 507 |     int fd; | 
 | 508 |     ssize_t size; | 
 | 509 |     char buf[PAGE_SIZE]; | 
 | 510 |     char *save_ptr; | 
 | 511 |     char *line; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 512 |  | 
 | 513 |     memset(mip, 0, sizeof(struct sysmeminfo)); | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 514 |  | 
| Nick Kralevich | c68c886 | 2015-12-18 20:52:37 -0800 | [diff] [blame] | 515 |     fd = open(ZONEINFO_PATH, O_RDONLY | O_CLOEXEC); | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 516 |     if (fd == -1) { | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 517 |         ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno); | 
 | 518 |         return -1; | 
 | 519 |     } | 
 | 520 |  | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 521 |     size = read_all(fd, buf, sizeof(buf) - 1); | 
 | 522 |     if (size < 0) { | 
 | 523 |         ALOGE("%s read: errno=%d", ZONEINFO_PATH, errno); | 
 | 524 |         close(fd); | 
 | 525 |         return -1; | 
 | 526 |     } | 
 | 527 |     ALOG_ASSERT((size_t)size < sizeof(buf) - 1, "/proc/zoneinfo too large"); | 
 | 528 |     buf[size] = 0; | 
 | 529 |  | 
 | 530 |     for (line = strtok_r(buf, "\n", &save_ptr); line; line = strtok_r(NULL, "\n", &save_ptr)) | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 531 |             zoneinfo_parse_line(line, mip); | 
 | 532 |  | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 533 |     close(fd); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 534 |     return 0; | 
 | 535 | } | 
 | 536 |  | 
 | 537 | static int proc_get_size(int pid) { | 
 | 538 |     char path[PATH_MAX]; | 
 | 539 |     char line[LINE_MAX]; | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 540 |     int fd; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 541 |     int rss = 0; | 
 | 542 |     int total; | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 543 |     ssize_t ret; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 544 |  | 
 | 545 |     snprintf(path, PATH_MAX, "/proc/%d/statm", pid); | 
| Nick Kralevich | c68c886 | 2015-12-18 20:52:37 -0800 | [diff] [blame] | 546 |     fd = open(path, O_RDONLY | O_CLOEXEC); | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 547 |     if (fd == -1) | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 548 |         return -1; | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 549 |  | 
 | 550 |     ret = read_all(fd, line, sizeof(line) - 1); | 
 | 551 |     if (ret < 0) { | 
 | 552 |         close(fd); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 553 |         return -1; | 
 | 554 |     } | 
 | 555 |  | 
 | 556 |     sscanf(line, "%d %d ", &total, &rss); | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 557 |     close(fd); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 558 |     return rss; | 
 | 559 | } | 
 | 560 |  | 
 | 561 | static char *proc_get_name(int pid) { | 
 | 562 |     char path[PATH_MAX]; | 
 | 563 |     static char line[LINE_MAX]; | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 564 |     int fd; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 565 |     char *cp; | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 566 |     ssize_t ret; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 567 |  | 
 | 568 |     snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid); | 
| Nick Kralevich | c68c886 | 2015-12-18 20:52:37 -0800 | [diff] [blame] | 569 |     fd = open(path, O_RDONLY | O_CLOEXEC); | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 570 |     if (fd == -1) | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 571 |         return NULL; | 
| Colin Cross | ce85d95 | 2014-07-11 17:53:27 -0700 | [diff] [blame] | 572 |     ret = read_all(fd, line, sizeof(line) - 1); | 
 | 573 |     close(fd); | 
 | 574 |     if (ret < 0) { | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 575 |         return NULL; | 
 | 576 |     } | 
 | 577 |  | 
 | 578 |     cp = strchr(line, ' '); | 
 | 579 |     if (cp) | 
 | 580 |         *cp = '\0'; | 
 | 581 |  | 
 | 582 |     return line; | 
 | 583 | } | 
 | 584 |  | 
 | 585 | static struct proc *proc_adj_lru(int oomadj) { | 
 | 586 |     return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]); | 
 | 587 | } | 
 | 588 |  | 
| Colin Cross | 16b0946 | 2014-07-14 12:39:56 -0700 | [diff] [blame] | 589 | /* Kill one process specified by procp.  Returns the size of the process killed */ | 
| Robert Benea | caeaa65 | 2017-08-11 16:03:20 -0700 | [diff] [blame] | 590 | static int kill_one_process(struct proc* procp, int min_score_adj, bool is_critical) { | 
| Colin Cross | 16b0946 | 2014-07-14 12:39:56 -0700 | [diff] [blame] | 591 |     int pid = procp->pid; | 
 | 592 |     uid_t uid = procp->uid; | 
 | 593 |     char *taskname; | 
 | 594 |     int tasksize; | 
 | 595 |     int r; | 
 | 596 |  | 
 | 597 |     taskname = proc_get_name(pid); | 
 | 598 |     if (!taskname) { | 
 | 599 |         pid_remove(pid); | 
 | 600 |         return -1; | 
 | 601 |     } | 
 | 602 |  | 
 | 603 |     tasksize = proc_get_size(pid); | 
 | 604 |     if (tasksize <= 0) { | 
 | 605 |         pid_remove(pid); | 
 | 606 |         return -1; | 
 | 607 |     } | 
 | 608 |  | 
| Robert Benea | caeaa65 | 2017-08-11 16:03:20 -0700 | [diff] [blame] | 609 |     ALOGI( | 
 | 610 |         "Killing '%s' (%d), uid %d, adj %d\n" | 
 | 611 |         "   to free %ldkB because system is under %s memory pressure oom_adj %d\n", | 
 | 612 |         taskname, pid, uid, procp->oomadj, tasksize * page_k, is_critical ? "critical" : "medium", | 
 | 613 |         min_score_adj); | 
| Colin Cross | 16b0946 | 2014-07-14 12:39:56 -0700 | [diff] [blame] | 614 |     r = kill(pid, SIGKILL); | 
| Colin Cross | 16b0946 | 2014-07-14 12:39:56 -0700 | [diff] [blame] | 615 |     pid_remove(pid); | 
 | 616 |  | 
 | 617 |     if (r) { | 
 | 618 |         ALOGE("kill(%d): errno=%d", procp->pid, errno); | 
 | 619 |         return -1; | 
 | 620 |     } else { | 
 | 621 |         return tasksize; | 
 | 622 |     } | 
 | 623 | } | 
 | 624 |  | 
 | 625 | /* | 
 | 626 |  * Find a process to kill based on the current (possibly estimated) free memory | 
 | 627 |  * and cached memory sizes.  Returns the size of the killed processes. | 
 | 628 |  */ | 
| Robert Benea | 19e2670 | 2017-09-17 18:31:35 -0700 | [diff] [blame] | 629 | static int find_and_kill_process(bool is_critical) { | 
| Colin Cross | 16b0946 | 2014-07-14 12:39:56 -0700 | [diff] [blame] | 630 |     int i; | 
| Colin Cross | 16b0946 | 2014-07-14 12:39:56 -0700 | [diff] [blame] | 631 |     int killed_size = 0; | 
| Robert Benea | 19e2670 | 2017-09-17 18:31:35 -0700 | [diff] [blame] | 632 |     int min_score_adj = is_critical ? critical_oomadj : medium_oomadj; | 
| Colin Cross | 16b0946 | 2014-07-14 12:39:56 -0700 | [diff] [blame] | 633 |  | 
| Chong Zhang | 0a4acdf | 2015-10-14 16:19:53 -0700 | [diff] [blame] | 634 |     for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) { | 
| Colin Cross | 16b0946 | 2014-07-14 12:39:56 -0700 | [diff] [blame] | 635 |         struct proc *procp; | 
 | 636 |  | 
 | 637 | retry: | 
 | 638 |         procp = proc_adj_lru(i); | 
 | 639 |  | 
 | 640 |         if (procp) { | 
| Robert Benea | caeaa65 | 2017-08-11 16:03:20 -0700 | [diff] [blame] | 641 |             killed_size = kill_one_process(procp, min_score_adj, is_critical); | 
| Colin Cross | 16b0946 | 2014-07-14 12:39:56 -0700 | [diff] [blame] | 642 |             if (killed_size < 0) { | 
 | 643 |                 goto retry; | 
 | 644 |             } else { | 
 | 645 |                 return killed_size; | 
 | 646 |             } | 
 | 647 |         } | 
 | 648 |     } | 
 | 649 |  | 
 | 650 |     return 0; | 
 | 651 | } | 
 | 652 |  | 
| Robert Benea | c47f299 | 2017-08-21 15:18:31 -0700 | [diff] [blame] | 653 | static int64_t get_memory_usage(const char* path) { | 
 | 654 |     int ret; | 
 | 655 |     int64_t mem_usage; | 
 | 656 |     char buf[32]; | 
 | 657 |     int fd = open(path, O_RDONLY | O_CLOEXEC); | 
 | 658 |     if (fd == -1) { | 
 | 659 |         ALOGE("%s open: errno=%d", path, errno); | 
 | 660 |         return -1; | 
 | 661 |     } | 
 | 662 |  | 
 | 663 |     ret = read_all(fd, buf, sizeof(buf) - 1); | 
 | 664 |     close(fd); | 
 | 665 |     if (ret < 0) { | 
 | 666 |         ALOGE("%s error: errno=%d", path, errno); | 
 | 667 |         return -1; | 
 | 668 |     } | 
 | 669 |     sscanf(buf, "%" SCNd64, &mem_usage); | 
 | 670 |     if (mem_usage == 0) { | 
 | 671 |         ALOGE("No memory!"); | 
 | 672 |         return -1; | 
 | 673 |     } | 
 | 674 |     return mem_usage; | 
 | 675 | } | 
 | 676 |  | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 677 | static void mp_event_common(bool is_critical) { | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 678 |     int ret; | 
 | 679 |     unsigned long long evcount; | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 680 |     int index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX; | 
| Robert Benea | c47f299 | 2017-08-21 15:18:31 -0700 | [diff] [blame] | 681 |     int64_t mem_usage, memsw_usage; | 
| Robert Benea | 6e8e710 | 2017-09-13 15:20:30 -0700 | [diff] [blame] | 682 |     int64_t mem_pressure; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 683 |  | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 684 |     ret = read(mpevfd[index], &evcount, sizeof(evcount)); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 685 |     if (ret < 0) | 
 | 686 |         ALOGE("Error reading memory pressure event fd; errno=%d", | 
 | 687 |               errno); | 
 | 688 |  | 
| Robert Benea | 6e8e710 | 2017-09-13 15:20:30 -0700 | [diff] [blame] | 689 |     mem_usage = get_memory_usage(MEMCG_MEMORY_USAGE); | 
 | 690 |     memsw_usage = get_memory_usage(MEMCG_MEMORYSW_USAGE); | 
 | 691 |     if (memsw_usage < 0 || mem_usage < 0) { | 
| Robert Benea | 19e2670 | 2017-09-17 18:31:35 -0700 | [diff] [blame] | 692 |         find_and_kill_process(is_critical); | 
| Robert Benea | 6e8e710 | 2017-09-13 15:20:30 -0700 | [diff] [blame] | 693 |         return; | 
 | 694 |     } | 
| Robert Benea | c47f299 | 2017-08-21 15:18:31 -0700 | [diff] [blame] | 695 |  | 
| Robert Benea | 6e8e710 | 2017-09-13 15:20:30 -0700 | [diff] [blame] | 696 |     // Calculate percent for swappinness. | 
 | 697 |     mem_pressure = (mem_usage * 100) / memsw_usage; | 
 | 698 |  | 
 | 699 |     if (enable_pressure_upgrade && !is_critical) { | 
 | 700 |         // We are swapping too much. | 
 | 701 |         if (mem_pressure < upgrade_pressure) { | 
| Robert Benea | c47f299 | 2017-08-21 15:18:31 -0700 | [diff] [blame] | 702 |             ALOGI("Event upgraded to critical."); | 
| Robert Benea | c47f299 | 2017-08-21 15:18:31 -0700 | [diff] [blame] | 703 |             is_critical = true; | 
 | 704 |         } | 
 | 705 |     } | 
 | 706 |  | 
| Robert Benea | 6e8e710 | 2017-09-13 15:20:30 -0700 | [diff] [blame] | 707 |     // If the pressure is larger than downgrade_pressure lmk will not | 
 | 708 |     // kill any process, since enough memory is available. | 
 | 709 |     if (mem_pressure > downgrade_pressure) { | 
 | 710 |         if (debug_process_killing) { | 
 | 711 |             ALOGI("Ignore %s memory pressure", is_critical ? "critical" : "medium"); | 
 | 712 |         } | 
 | 713 |         return; | 
 | 714 |     } else if (is_critical && mem_pressure > upgrade_pressure) { | 
 | 715 |         if (debug_process_killing) { | 
 | 716 |             ALOGI("Downgrade critical memory pressure"); | 
 | 717 |         } | 
 | 718 |         // Downgrade event to medium, since enough memory available. | 
 | 719 |         is_critical = false; | 
 | 720 |     } | 
 | 721 |  | 
| Robert Benea | 19e2670 | 2017-09-17 18:31:35 -0700 | [diff] [blame] | 722 |     if (find_and_kill_process(is_critical) == 0) { | 
| Robert Benea | caeaa65 | 2017-08-11 16:03:20 -0700 | [diff] [blame] | 723 |         if (debug_process_killing) { | 
 | 724 |             ALOGI("Nothing to kill"); | 
 | 725 |         } | 
| Colin Cross | f8857cc | 2014-07-11 17:16:56 -0700 | [diff] [blame] | 726 |     } | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 727 | } | 
 | 728 |  | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 729 | static void mp_event(uint32_t events __unused) { | 
 | 730 |     mp_event_common(false); | 
 | 731 | } | 
 | 732 |  | 
 | 733 | static void mp_event_critical(uint32_t events __unused) { | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 734 |     mp_event_common(true); | 
 | 735 | } | 
 | 736 |  | 
 | 737 | static int init_mp_common(char *levelstr, void *event_handler, bool is_critical) | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 738 | { | 
 | 739 |     int mpfd; | 
 | 740 |     int evfd; | 
 | 741 |     int evctlfd; | 
 | 742 |     char buf[256]; | 
 | 743 |     struct epoll_event epev; | 
 | 744 |     int ret; | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 745 |     int mpevfd_index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 746 |  | 
| Nick Kralevich | c68c886 | 2015-12-18 20:52:37 -0800 | [diff] [blame] | 747 |     mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 748 |     if (mpfd < 0) { | 
 | 749 |         ALOGI("No kernel memory.pressure_level support (errno=%d)", errno); | 
 | 750 |         goto err_open_mpfd; | 
 | 751 |     } | 
 | 752 |  | 
| Nick Kralevich | c68c886 | 2015-12-18 20:52:37 -0800 | [diff] [blame] | 753 |     evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY | O_CLOEXEC); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 754 |     if (evctlfd < 0) { | 
 | 755 |         ALOGI("No kernel memory cgroup event control (errno=%d)", errno); | 
 | 756 |         goto err_open_evctlfd; | 
 | 757 |     } | 
 | 758 |  | 
| Nick Kralevich | c68c886 | 2015-12-18 20:52:37 -0800 | [diff] [blame] | 759 |     evfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 760 |     if (evfd < 0) { | 
 | 761 |         ALOGE("eventfd failed for level %s; errno=%d", levelstr, errno); | 
 | 762 |         goto err_eventfd; | 
 | 763 |     } | 
 | 764 |  | 
 | 765 |     ret = snprintf(buf, sizeof(buf), "%d %d %s", evfd, mpfd, levelstr); | 
 | 766 |     if (ret >= (ssize_t)sizeof(buf)) { | 
 | 767 |         ALOGE("cgroup.event_control line overflow for level %s", levelstr); | 
 | 768 |         goto err; | 
 | 769 |     } | 
 | 770 |  | 
 | 771 |     ret = write(evctlfd, buf, strlen(buf) + 1); | 
 | 772 |     if (ret == -1) { | 
 | 773 |         ALOGE("cgroup.event_control write failed for level %s; errno=%d", | 
 | 774 |               levelstr, errno); | 
 | 775 |         goto err; | 
 | 776 |     } | 
 | 777 |  | 
 | 778 |     epev.events = EPOLLIN; | 
 | 779 |     epev.data.ptr = event_handler; | 
 | 780 |     ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev); | 
 | 781 |     if (ret == -1) { | 
 | 782 |         ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno); | 
 | 783 |         goto err; | 
 | 784 |     } | 
 | 785 |     maxevents++; | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 786 |     mpevfd[mpevfd_index] = evfd; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 787 |     return 0; | 
 | 788 |  | 
 | 789 | err: | 
 | 790 |     close(evfd); | 
 | 791 | err_eventfd: | 
 | 792 |     close(evctlfd); | 
 | 793 | err_open_evctlfd: | 
 | 794 |     close(mpfd); | 
 | 795 | err_open_mpfd: | 
 | 796 |     return -1; | 
 | 797 | } | 
 | 798 |  | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 799 | static int init_mp_medium() | 
 | 800 | { | 
 | 801 |     return init_mp_common(MEMPRESSURE_WATCH_MEDIUM_LEVEL, (void *)&mp_event, false); | 
 | 802 | } | 
 | 803 |  | 
 | 804 | static int init_mp_critical() | 
 | 805 | { | 
 | 806 |     return init_mp_common(MEMPRESSURE_WATCH_CRITICAL_LEVEL, (void *)&mp_event_critical, true); | 
 | 807 | } | 
 | 808 |  | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 809 | static int init(void) { | 
 | 810 |     struct epoll_event epev; | 
 | 811 |     int i; | 
 | 812 |     int ret; | 
 | 813 |  | 
 | 814 |     page_k = sysconf(_SC_PAGESIZE); | 
 | 815 |     if (page_k == -1) | 
 | 816 |         page_k = PAGE_SIZE; | 
 | 817 |     page_k /= 1024; | 
 | 818 |  | 
 | 819 |     epollfd = epoll_create(MAX_EPOLL_EVENTS); | 
 | 820 |     if (epollfd == -1) { | 
 | 821 |         ALOGE("epoll_create failed (errno=%d)", errno); | 
 | 822 |         return -1; | 
 | 823 |     } | 
 | 824 |  | 
 | 825 |     ctrl_lfd = android_get_control_socket("lmkd"); | 
 | 826 |     if (ctrl_lfd < 0) { | 
 | 827 |         ALOGE("get lmkd control socket failed"); | 
 | 828 |         return -1; | 
 | 829 |     } | 
 | 830 |  | 
 | 831 |     ret = listen(ctrl_lfd, 1); | 
 | 832 |     if (ret < 0) { | 
 | 833 |         ALOGE("lmkd control socket listen failed (errno=%d)", errno); | 
 | 834 |         return -1; | 
 | 835 |     } | 
 | 836 |  | 
 | 837 |     epev.events = EPOLLIN; | 
 | 838 |     epev.data.ptr = (void *)ctrl_connect_handler; | 
 | 839 |     if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) { | 
 | 840 |         ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno); | 
 | 841 |         return -1; | 
 | 842 |     } | 
 | 843 |     maxevents++; | 
 | 844 |  | 
| Robert Benea | 164baeb | 2017-09-11 16:53:28 -0700 | [diff] [blame] | 845 |     has_inkernel_module = !access(INKERNEL_MINFREE_PATH, W_OK); | 
 | 846 |     use_inkernel_interface = has_inkernel_module && !is_go_device; | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 847 |  | 
 | 848 |     if (use_inkernel_interface) { | 
 | 849 |         ALOGI("Using in-kernel low memory killer interface"); | 
 | 850 |     } else { | 
| Robert Benea | 673e276 | 2017-06-01 16:32:31 -0700 | [diff] [blame] | 851 |         ret = init_mp_medium(); | 
 | 852 |         ret |= init_mp_critical(); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 853 |         if (ret) | 
 | 854 |             ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer"); | 
 | 855 |     } | 
 | 856 |  | 
| Chong Zhang | 0a4acdf | 2015-10-14 16:19:53 -0700 | [diff] [blame] | 857 |     for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) { | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 858 |         procadjslot_list[i].next = &procadjslot_list[i]; | 
 | 859 |         procadjslot_list[i].prev = &procadjslot_list[i]; | 
 | 860 |     } | 
 | 861 |  | 
 | 862 |     return 0; | 
 | 863 | } | 
 | 864 |  | 
 | 865 | static void mainloop(void) { | 
 | 866 |     while (1) { | 
 | 867 |         struct epoll_event events[maxevents]; | 
 | 868 |         int nevents; | 
 | 869 |         int i; | 
 | 870 |  | 
 | 871 |         ctrl_dfd_reopened = 0; | 
 | 872 |         nevents = epoll_wait(epollfd, events, maxevents, -1); | 
 | 873 |  | 
 | 874 |         if (nevents == -1) { | 
 | 875 |             if (errno == EINTR) | 
 | 876 |                 continue; | 
 | 877 |             ALOGE("epoll_wait failed (errno=%d)", errno); | 
 | 878 |             continue; | 
 | 879 |         } | 
 | 880 |  | 
 | 881 |         for (i = 0; i < nevents; ++i) { | 
 | 882 |             if (events[i].events & EPOLLERR) | 
 | 883 |                 ALOGD("EPOLLERR on event #%d", i); | 
 | 884 |             if (events[i].data.ptr) | 
 | 885 |                 (*(void (*)(uint32_t))events[i].data.ptr)(events[i].events); | 
 | 886 |         } | 
 | 887 |     } | 
 | 888 | } | 
 | 889 |  | 
| Mark Salyzyn | e6ed68b | 2014-04-30 13:36:35 -0700 | [diff] [blame] | 890 | int main(int argc __unused, char **argv __unused) { | 
| Colin Cross | 1a0d9be | 2014-07-14 14:31:15 -0700 | [diff] [blame] | 891 |     struct sched_param param = { | 
 | 892 |             .sched_priority = 1, | 
 | 893 |     }; | 
 | 894 |  | 
| Robert Benea | 58891d5 | 2017-07-31 17:15:20 -0700 | [diff] [blame] | 895 |     medium_oomadj = property_get_int32("ro.lmk.medium", 800); | 
 | 896 |     critical_oomadj = property_get_int32("ro.lmk.critical", 0); | 
| Robert Benea | caeaa65 | 2017-08-11 16:03:20 -0700 | [diff] [blame] | 897 |     debug_process_killing = property_get_bool("ro.lmk.debug", false); | 
| Robert Benea | c47f299 | 2017-08-21 15:18:31 -0700 | [diff] [blame] | 898 |     enable_pressure_upgrade = property_get_bool("ro.lmk.critical_upgrade", false); | 
 | 899 |     upgrade_pressure = (int64_t)property_get_int32("ro.lmk.upgrade_pressure", 50); | 
| Robert Benea | 6e8e710 | 2017-09-13 15:20:30 -0700 | [diff] [blame] | 900 |     downgrade_pressure = (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 60); | 
| Robert Benea | 164baeb | 2017-09-11 16:53:28 -0700 | [diff] [blame] | 901 |     is_go_device = property_get_bool("ro.config.low_ram", false); | 
| Robert Benea | 58891d5 | 2017-07-31 17:15:20 -0700 | [diff] [blame] | 902 |  | 
| Colin Cross | b28ff91 | 2014-07-11 17:15:44 -0700 | [diff] [blame] | 903 |     mlockall(MCL_FUTURE); | 
| Colin Cross | 1a0d9be | 2014-07-14 14:31:15 -0700 | [diff] [blame] | 904 |     sched_setscheduler(0, SCHED_FIFO, ¶m); | 
| Todd Poynor | 3948f80 | 2013-07-09 19:35:14 -0700 | [diff] [blame] | 905 |     if (!init()) | 
 | 906 |         mainloop(); | 
 | 907 |  | 
 | 908 |     ALOGI("exiting"); | 
 | 909 |     return 0; | 
 | 910 | } |