blob: e489e814b300feb91f2767c0c80d0cb0f400ff2a [file] [log] [blame]
Todd Poynor3948f802013-07-09 19:35:14 -07001/*
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
19#include <errno.h>
20#include <signal.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <time.h>
25#include <unistd.h>
26#include <arpa/inet.h>
27#include <sys/epoll.h>
28#include <sys/eventfd.h>
29#include <sys/socket.h>
30#include <sys/types.h>
31#include <cutils/log.h>
32#include <cutils/sockets.h>
33
34#define MEMCG_SYSFS_PATH "/dev/memcg/"
35#define MEMPRESSURE_WATCH_LEVEL "medium"
36#define ZONEINFO_PATH "/proc/zoneinfo"
37#define LINE_MAX 128
38
39#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
40#define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj"
41
42#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
43
44enum lmk_cmd {
45 LMK_TARGET,
46 LMK_PROCPRIO,
47 LMK_PROCREMOVE,
48};
49
50#define MAX_TARGETS 6
51/*
52 * longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio
53 * values
54 */
55#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1))
56
57/* default to old in-kernel interface if no memory pressure events */
58static int use_inkernel_interface = 1;
59
60/* memory pressure level medium event */
61static int mpevfd;
62
63/* control socket listen and data */
64static int ctrl_lfd;
65static int ctrl_dfd = -1;
66static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */
67
68/* 1 memory pressure level, 1 ctrl listen socket, 1 ctrl data socket */
69#define MAX_EPOLL_EVENTS 3
70static int epollfd;
71static int maxevents;
72
73#define OOM_DISABLE (-17)
74/* inclusive */
75#define OOM_ADJUST_MIN (-16)
76#define OOM_ADJUST_MAX 15
77
Todd Poynor16b60992013-09-16 19:26:47 -070078/* kernel OOM score values */
79#define OOM_SCORE_ADJ_MIN (-1000)
80#define OOM_SCORE_ADJ_MAX 1000
81
Todd Poynor3948f802013-07-09 19:35:14 -070082static int lowmem_adj[MAX_TARGETS];
83static int lowmem_minfree[MAX_TARGETS];
84static int lowmem_targets_size;
85
86struct sysmeminfo {
87 int nr_free_pages;
88 int nr_file_pages;
89 int nr_shmem;
90 int totalreserve_pages;
91};
92
93struct adjslot_list {
94 struct adjslot_list *next;
95 struct adjslot_list *prev;
96};
97
98struct proc {
99 struct adjslot_list asl;
100 int pid;
101 int oomadj;
102 struct proc *pidhash_next;
103};
104
105#define PIDHASH_SZ 1024
106static struct proc *pidhash[PIDHASH_SZ];
107#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
108
109#define ADJTOSLOT(adj) (adj + -OOM_ADJUST_MIN)
110static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_ADJUST_MAX) + 1];
111
112/*
113 * Wait 1-2 seconds for the death report of a killed process prior to
114 * considering killing more processes.
115 */
116#define KILL_TIMEOUT 2
117/* Time of last process kill we initiated, stop me before I kill again */
118static time_t kill_lasttime;
119
120/* PAGE_SIZE / 1024 */
121static long page_k;
122
Todd Poynor16b60992013-09-16 19:26:47 -0700123static int lowmem_oom_adj_to_oom_score_adj(int oom_adj)
124{
125 if (oom_adj == OOM_ADJUST_MAX)
126 return OOM_SCORE_ADJ_MAX;
127 else
128 return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
129}
130
Todd Poynor3948f802013-07-09 19:35:14 -0700131static struct proc *pid_lookup(int pid) {
132 struct proc *procp;
133
134 for (procp = pidhash[pid_hashfn(pid)]; procp && procp->pid != pid;
135 procp = procp->pidhash_next)
136 ;
137
138 return procp;
139}
140
141static void adjslot_insert(struct adjslot_list *head, struct adjslot_list *new)
142{
143 struct adjslot_list *next = head->next;
144 new->prev = head;
145 new->next = next;
146 next->prev = new;
147 head->next = new;
148}
149
150static void adjslot_remove(struct adjslot_list *old)
151{
152 struct adjslot_list *prev = old->prev;
153 struct adjslot_list *next = old->next;
154 next->prev = prev;
155 prev->next = next;
156}
157
158static struct adjslot_list *adjslot_tail(struct adjslot_list *head) {
159 struct adjslot_list *asl = head->prev;
160
161 return asl == head ? NULL : asl;
162}
163
164static void proc_slot(struct proc *procp) {
165 int adjslot = ADJTOSLOT(procp->oomadj);
166
167 adjslot_insert(&procadjslot_list[adjslot], &procp->asl);
168}
169
170static void proc_unslot(struct proc *procp) {
171 adjslot_remove(&procp->asl);
172}
173
174static void proc_insert(struct proc *procp) {
175 int hval = pid_hashfn(procp->pid);
176
177 procp->pidhash_next = pidhash[hval];
178 pidhash[hval] = procp;
179 proc_slot(procp);
180}
181
182static int pid_remove(int pid) {
183 int hval = pid_hashfn(pid);
184 struct proc *procp;
185 struct proc *prevp;
186
187 for (procp = pidhash[hval], prevp = NULL; procp && procp->pid != pid;
188 procp = procp->pidhash_next)
189 prevp = procp;
190
191 if (!procp)
192 return -1;
193
194 if (!prevp)
195 pidhash[hval] = procp->pidhash_next;
196 else
197 prevp->pidhash_next = procp->pidhash_next;
198
199 proc_unslot(procp);
200 free(procp);
201 return 0;
202}
203
204static void writefilestring(char *path, char *s) {
205 int fd = open(path, O_WRONLY);
206 int len = strlen(s);
207 int ret;
208
209 if (fd < 0) {
210 ALOGE("Error opening %s; errno=%d", path, errno);
211 return;
212 }
213
214 ret = write(fd, s, len);
215 if (ret < 0) {
216 ALOGE("Error writing %s; errno=%d", path, errno);
217 } else if (ret < len) {
218 ALOGE("Short write on %s; length=%d", path, ret);
219 }
220
221 close(fd);
222}
223
224static void cmd_procprio(int pid, int oomadj) {
225 struct proc *procp;
226 char path[80];
227 char val[20];
228
229 if (oomadj < OOM_DISABLE || oomadj > OOM_ADJUST_MAX) {
230 ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
231 return;
232 }
233
Todd Poynor16b60992013-09-16 19:26:47 -0700234 snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
235 snprintf(val, sizeof(val), "%d", lowmem_oom_adj_to_oom_score_adj(oomadj));
Todd Poynor3948f802013-07-09 19:35:14 -0700236 writefilestring(path, val);
237
238 if (use_inkernel_interface)
239 return;
240
241 procp = pid_lookup(pid);
242 if (!procp) {
243 procp = malloc(sizeof(struct proc));
244 if (!procp) {
245 // Oh, the irony. May need to rebuild our state.
246 return;
247 }
248
249 procp->pid = pid;
250 procp->oomadj = oomadj;
251 proc_insert(procp);
252 } else {
253 proc_unslot(procp);
254 procp->oomadj = oomadj;
255 proc_slot(procp);
256 }
257}
258
259static void cmd_procremove(int pid) {
260 struct proc *procp;
261
262 if (use_inkernel_interface)
263 return;
264
265 pid_remove(pid);
266 kill_lasttime = 0;
267}
268
269static void cmd_target(int ntargets, int *params) {
270 int i;
271
272 if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
273 return;
274
275 for (i = 0; i < ntargets; i++) {
276 lowmem_minfree[i] = ntohl(*params++);
277 lowmem_adj[i] = ntohl(*params++);
278 }
279
280 lowmem_targets_size = ntargets;
281
282 if (use_inkernel_interface) {
283 char minfreestr[128];
284 char killpriostr[128];
285
286 minfreestr[0] = '\0';
287 killpriostr[0] = '\0';
288
289 for (i = 0; i < lowmem_targets_size; i++) {
290 char val[40];
291
292 if (i) {
293 strlcat(minfreestr, ",", sizeof(minfreestr));
294 strlcat(killpriostr, ",", sizeof(killpriostr));
295 }
296
297 snprintf(val, sizeof(val), "%d", lowmem_minfree[i]);
298 strlcat(minfreestr, val, sizeof(minfreestr));
299 snprintf(val, sizeof(val), "%d", lowmem_adj[i]);
300 strlcat(killpriostr, val, sizeof(killpriostr));
301 }
302
303 writefilestring(INKERNEL_MINFREE_PATH, minfreestr);
304 writefilestring(INKERNEL_ADJ_PATH, killpriostr);
305 }
306}
307
308static void ctrl_data_close(void) {
309 ALOGI("Closing Activity Manager data connection");
310 close(ctrl_dfd);
311 ctrl_dfd = -1;
312 maxevents--;
313}
314
315static int ctrl_data_read(char *buf, size_t bufsz) {
316 int ret = 0;
317
318 ret = read(ctrl_dfd, buf, bufsz);
319
320 if (ret == -1) {
321 ALOGE("control data socket read failed; errno=%d", errno);
322 } else if (ret == 0) {
323 ALOGE("Got EOF on control data socket");
324 ret = -1;
325 }
326
327 return ret;
328}
329
330static void ctrl_command_handler(void) {
331 int ibuf[CTRL_PACKET_MAX / sizeof(int)];
332 int len;
333 int cmd = -1;
334 int nargs;
335 int targets;
336
337 len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);
338 if (len <= 0)
339 return;
340
341 nargs = len / sizeof(int) - 1;
342 if (nargs < 0)
343 goto wronglen;
344
345 cmd = ntohl(ibuf[0]);
346
347 switch(cmd) {
348 case LMK_TARGET:
349 targets = nargs / 2;
350 if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
351 goto wronglen;
352 cmd_target(targets, &ibuf[1]);
353 break;
354 case LMK_PROCPRIO:
355 if (nargs != 2)
356 goto wronglen;
357 cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]));
358 break;
359 case LMK_PROCREMOVE:
360 if (nargs != 1)
361 goto wronglen;
362 cmd_procremove(ntohl(ibuf[1]));
363 break;
364 default:
365 ALOGE("Received unknown command code %d", cmd);
366 return;
367 }
368
369 return;
370
371wronglen:
372 ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
373}
374
375static void ctrl_data_handler(uint32_t events) {
376 if (events & EPOLLHUP) {
377 ALOGI("ActivityManager disconnected");
378 if (!ctrl_dfd_reopened)
379 ctrl_data_close();
380 } else if (events & EPOLLIN) {
381 ctrl_command_handler();
382 }
383}
384
385static void ctrl_connect_handler(uint32_t events) {
386 struct sockaddr addr;
387 socklen_t alen;
388 struct epoll_event epev;
389
390 if (ctrl_dfd >= 0) {
391 ctrl_data_close();
392 ctrl_dfd_reopened = 1;
393 }
394
395 alen = sizeof(addr);
396 ctrl_dfd = accept(ctrl_lfd, &addr, &alen);
397
398 if (ctrl_dfd < 0) {
399 ALOGE("lmkd control socket accept failed; errno=%d", errno);
400 return;
401 }
402
403 ALOGI("ActivityManager connected");
404 maxevents++;
405 epev.events = EPOLLIN;
406 epev.data.ptr = (void *)ctrl_data_handler;
407 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_dfd, &epev) == -1) {
408 ALOGE("epoll_ctl for data connection socket failed; errno=%d", errno);
409 ctrl_data_close();
410 return;
411 }
412}
413
414static int zoneinfo_parse_protection(char *cp) {
415 int max = 0;
416 int zoneval;
417
418 if (*cp++ != '(')
419 return 0;
420
421 do {
422 zoneval = strtol(cp, &cp, 0);
423 if ((*cp != ',') && (*cp != ')'))
424 return 0;
425 if (zoneval > max)
426 max = zoneval;
427 } while (cp = strtok(NULL, " "));
428
429 return max;
430}
431
432static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) {
433 char *cp = line;
434 char *ap;
435
436 cp = strtok(line, " ");
437 if (!cp)
438 return;
439
440 ap = strtok(NULL, " ");
441 if (!ap)
442 return;
443
444 if (!strcmp(cp, "nr_free_pages"))
445 mip->nr_free_pages += strtol(ap, NULL, 0);
446 else if (!strcmp(cp, "nr_file_pages"))
447 mip->nr_file_pages += strtol(ap, NULL, 0);
448 else if (!strcmp(cp, "nr_shmem"))
449 mip->nr_shmem += strtol(ap, NULL, 0);
450 else if (!strcmp(cp, "high"))
451 mip->totalreserve_pages += strtol(ap, NULL, 0);
452 else if (!strcmp(cp, "protection:"))
453 mip->totalreserve_pages += zoneinfo_parse_protection(ap);
454}
455
456static int zoneinfo_parse(struct sysmeminfo *mip) {
457 FILE *f;
458 char *cp;
459 char line[LINE_MAX];
460
461 memset(mip, 0, sizeof(struct sysmeminfo));
462 f = fopen(ZONEINFO_PATH, "r");
463 if (!f) {
464 ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno);
465 return -1;
466 }
467
468 while (fgets(line, LINE_MAX, f))
469 zoneinfo_parse_line(line, mip);
470
471 fclose(f);
472 return 0;
473}
474
475static int proc_get_size(int pid) {
476 char path[PATH_MAX];
477 char line[LINE_MAX];
478 FILE *f;
479 int rss = 0;
480 int total;
481
482 snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
483 f = fopen(path, "r");
484 if (!f)
485 return -1;
486 if (!fgets(line, LINE_MAX, f)) {
487 fclose(f);
488 return -1;
489 }
490
491 sscanf(line, "%d %d ", &total, &rss);
492 fclose(f);
493 return rss;
494}
495
496static char *proc_get_name(int pid) {
497 char path[PATH_MAX];
498 static char line[LINE_MAX];
499 FILE *f;
500 char *cp;
501
502 snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
503 f = fopen(path, "r");
504 if (!f)
505 return NULL;
506 if (!fgets(line, LINE_MAX, f)) {
507 fclose(f);
508 return NULL;
509 }
510
511 cp = strchr(line, ' ');
512 if (cp)
513 *cp = '\0';
514
515 return line;
516}
517
518static struct proc *proc_adj_lru(int oomadj) {
519 return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
520}
521
522static void mp_event(uint32_t events) {
523 int i;
524 int ret;
525 unsigned long long evcount;
526 struct sysmeminfo mi;
527 int other_free;
528 int other_file;
529 int minfree = 0;
530 int min_score_adj = OOM_ADJUST_MAX + 1;
531
532 ret = read(mpevfd, &evcount, sizeof(evcount));
533 if (ret < 0)
534 ALOGE("Error reading memory pressure event fd; errno=%d",
535 errno);
536
537 if (time(NULL) - kill_lasttime < KILL_TIMEOUT)
538 return;
539
540 if (zoneinfo_parse(&mi) < 0)
541 return;
542
543 other_free = mi.nr_free_pages - mi.totalreserve_pages;
544 other_file = mi.nr_file_pages - mi.nr_shmem;
545
546 for (i = 0; i < lowmem_targets_size; i++) {
547 minfree = lowmem_minfree[i];
548 if (other_free < minfree && other_file < minfree) {
549 min_score_adj = lowmem_adj[i];
550 break;
551 }
552 }
553
554 if (min_score_adj == OOM_ADJUST_MAX + 1)
555 return;
556
557 for (i = OOM_ADJUST_MAX; i >= min_score_adj; i--) {
558 struct proc *procp;
559
560 retry:
561 procp = proc_adj_lru(i);
562
563 if (procp) {
564 int pid = procp->pid;
565 char *taskname;
566 int tasksize;
567 int r;
568
569 taskname = proc_get_name(pid);
570 if (!taskname) {
571 pid_remove(pid);
572 goto retry;
573 }
574
575 tasksize = proc_get_size(pid);
576 if (tasksize < 0) {
577 pid_remove(pid);
578 goto retry;
579 }
580
581 ALOGI("Killing '%s' (%d), adj %d\n"
582 " to free %ldkB because cache %ldkB is below limit %ldkB for oom_adj %d\n"
583 " Free memory is %ldkB %s reserved",
584 taskname, pid, procp->oomadj, tasksize * page_k,
585 other_file * page_k, minfree * page_k, min_score_adj,
586 other_free * page_k, other_free >= 0 ? "above" : "below");
587 r = kill(pid, SIGKILL);
588 pid_remove(pid);
589
590 if (r) {
591 ALOGE("kill(%d): errno=%d", procp->pid, errno);
592 goto retry;
593 } else {
594 time(&kill_lasttime);
595 break;
596 }
597 }
598 }
599}
600
601static int init_mp(char *levelstr, void *event_handler)
602{
603 int mpfd;
604 int evfd;
605 int evctlfd;
606 char buf[256];
607 struct epoll_event epev;
608 int ret;
609
610 mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY);
611 if (mpfd < 0) {
612 ALOGI("No kernel memory.pressure_level support (errno=%d)", errno);
613 goto err_open_mpfd;
614 }
615
616 evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY);
617 if (evctlfd < 0) {
618 ALOGI("No kernel memory cgroup event control (errno=%d)", errno);
619 goto err_open_evctlfd;
620 }
621
622 evfd = eventfd(0, EFD_NONBLOCK);
623 if (evfd < 0) {
624 ALOGE("eventfd failed for level %s; errno=%d", levelstr, errno);
625 goto err_eventfd;
626 }
627
628 ret = snprintf(buf, sizeof(buf), "%d %d %s", evfd, mpfd, levelstr);
629 if (ret >= (ssize_t)sizeof(buf)) {
630 ALOGE("cgroup.event_control line overflow for level %s", levelstr);
631 goto err;
632 }
633
634 ret = write(evctlfd, buf, strlen(buf) + 1);
635 if (ret == -1) {
636 ALOGE("cgroup.event_control write failed for level %s; errno=%d",
637 levelstr, errno);
638 goto err;
639 }
640
641 epev.events = EPOLLIN;
642 epev.data.ptr = event_handler;
643 ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev);
644 if (ret == -1) {
645 ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno);
646 goto err;
647 }
648 maxevents++;
649 mpevfd = evfd;
650 return 0;
651
652err:
653 close(evfd);
654err_eventfd:
655 close(evctlfd);
656err_open_evctlfd:
657 close(mpfd);
658err_open_mpfd:
659 return -1;
660}
661
662static int init(void) {
663 struct epoll_event epev;
664 int i;
665 int ret;
666
667 page_k = sysconf(_SC_PAGESIZE);
668 if (page_k == -1)
669 page_k = PAGE_SIZE;
670 page_k /= 1024;
671
672 epollfd = epoll_create(MAX_EPOLL_EVENTS);
673 if (epollfd == -1) {
674 ALOGE("epoll_create failed (errno=%d)", errno);
675 return -1;
676 }
677
678 ctrl_lfd = android_get_control_socket("lmkd");
679 if (ctrl_lfd < 0) {
680 ALOGE("get lmkd control socket failed");
681 return -1;
682 }
683
684 ret = listen(ctrl_lfd, 1);
685 if (ret < 0) {
686 ALOGE("lmkd control socket listen failed (errno=%d)", errno);
687 return -1;
688 }
689
690 epev.events = EPOLLIN;
691 epev.data.ptr = (void *)ctrl_connect_handler;
692 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) {
693 ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
694 return -1;
695 }
696 maxevents++;
697
698 use_inkernel_interface = !access(INKERNEL_MINFREE_PATH, W_OK);
699
700 if (use_inkernel_interface) {
701 ALOGI("Using in-kernel low memory killer interface");
702 } else {
703 ret = init_mp(MEMPRESSURE_WATCH_LEVEL, (void *)&mp_event);
704 if (ret)
705 ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
706 }
707
708 for (i = 0; i <= ADJTOSLOT(OOM_ADJUST_MAX); i++) {
709 procadjslot_list[i].next = &procadjslot_list[i];
710 procadjslot_list[i].prev = &procadjslot_list[i];
711 }
712
713 return 0;
714}
715
716static void mainloop(void) {
717 while (1) {
718 struct epoll_event events[maxevents];
719 int nevents;
720 int i;
721
722 ctrl_dfd_reopened = 0;
723 nevents = epoll_wait(epollfd, events, maxevents, -1);
724
725 if (nevents == -1) {
726 if (errno == EINTR)
727 continue;
728 ALOGE("epoll_wait failed (errno=%d)", errno);
729 continue;
730 }
731
732 for (i = 0; i < nevents; ++i) {
733 if (events[i].events & EPOLLERR)
734 ALOGD("EPOLLERR on event #%d", i);
735 if (events[i].data.ptr)
736 (*(void (*)(uint32_t))events[i].data.ptr)(events[i].events);
737 }
738 }
739}
740
741int main(int argc, char **argv) {
742 if (!init())
743 mainloop();
744
745 ALOGI("exiting");
746 return 0;
747}