summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'lmkd/lmkd.c')
-rw-r--r--lmkd/lmkd.c120
1 files changed, 68 insertions, 52 deletions
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 8a6168cd3..c0953158d 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -40,7 +40,8 @@
40#endif 40#endif
41 41
42#define MEMCG_SYSFS_PATH "/dev/memcg/" 42#define MEMCG_SYSFS_PATH "/dev/memcg/"
43#define MEMPRESSURE_WATCH_LEVEL "low" 43#define MEMPRESSURE_WATCH_MEDIUM_LEVEL "medium"
44#define MEMPRESSURE_WATCH_CRITICAL_LEVEL "critical"
44#define ZONEINFO_PATH "/proc/zoneinfo" 45#define ZONEINFO_PATH "/proc/zoneinfo"
45#define LINE_MAX 128 46#define LINE_MAX 128
46 47
@@ -48,6 +49,7 @@
48#define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj" 49#define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj"
49 50
50#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) 51#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
52#define EIGHT_MEGA (1 << 23)
51 53
52enum lmk_cmd { 54enum lmk_cmd {
53 LMK_TARGET, 55 LMK_TARGET,
@@ -66,15 +68,17 @@ enum lmk_cmd {
66static int use_inkernel_interface = 1; 68static int use_inkernel_interface = 1;
67 69
68/* memory pressure level medium event */ 70/* memory pressure level medium event */
69static int mpevfd; 71static int mpevfd[2];
72#define CRITICAL_INDEX 1
73#define MEDIUM_INDEX 0
70 74
71/* control socket listen and data */ 75/* control socket listen and data */
72static int ctrl_lfd; 76static int ctrl_lfd;
73static int ctrl_dfd = -1; 77static int ctrl_dfd = -1;
74static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */ 78static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */
75 79
76/* 1 memory pressure level, 1 ctrl listen socket, 1 ctrl data socket */ 80/* 2 memory pressure levels, 1 ctrl listen socket, 1 ctrl data socket */
77#define MAX_EPOLL_EVENTS 3 81#define MAX_EPOLL_EVENTS 4
78static int epollfd; 82static int epollfd;
79static int maxevents; 83static int maxevents;
80 84
@@ -113,14 +117,6 @@ static struct proc *pidhash[PIDHASH_SZ];
113#define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN) 117#define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN)
114static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1]; 118static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1];
115 119
116/*
117 * Wait 1-2 seconds for the death report of a killed process prior to
118 * considering killing more processes.
119 */
120#define KILL_TIMEOUT 2
121/* Time of last process kill we initiated, stop me before I kill again */
122static time_t kill_lasttime;
123
124/* PAGE_SIZE / 1024 */ 120/* PAGE_SIZE / 1024 */
125static long page_k; 121static long page_k;
126 122
@@ -241,6 +237,7 @@ static void cmd_procprio(int pid, int uid, int oomadj) {
241 struct proc *procp; 237 struct proc *procp;
242 char path[80]; 238 char path[80];
243 char val[20]; 239 char val[20];
240 int soft_limit_mult;
244 241
245 if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) { 242 if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) {
246 ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj); 243 ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
@@ -254,6 +251,36 @@ static void cmd_procprio(int pid, int uid, int oomadj) {
254 if (use_inkernel_interface) 251 if (use_inkernel_interface)
255 return; 252 return;
256 253
254 if (oomadj >= 900) {
255 soft_limit_mult = 0;
256 } else if (oomadj >= 800) {
257 soft_limit_mult = 0;
258 } else if (oomadj >= 700) {
259 soft_limit_mult = 0;
260 } else if (oomadj >= 600) {
261 soft_limit_mult = 0;
262 } else if (oomadj >= 500) {
263 soft_limit_mult = 0;
264 } else if (oomadj >= 400) {
265 soft_limit_mult = 0;
266 } else if (oomadj >= 300) {
267 soft_limit_mult = 1;
268 } else if (oomadj >= 200) {
269 soft_limit_mult = 2;
270 } else if (oomadj >= 100) {
271 soft_limit_mult = 10;
272 } else if (oomadj >= 0) {
273 soft_limit_mult = 20;
274 } else {
275 // Persistent processes will have a large
276 // soft limit 512MB.
277 soft_limit_mult = 64;
278 }
279
280 snprintf(path, sizeof(path), "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes", uid, pid);
281 snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
282 writefilestring(path, val);
283
257 procp = pid_lookup(pid); 284 procp = pid_lookup(pid);
258 if (!procp) { 285 if (!procp) {
259 procp = malloc(sizeof(struct proc)); 286 procp = malloc(sizeof(struct proc));
@@ -278,7 +305,6 @@ static void cmd_procremove(int pid) {
278 return; 305 return;
279 306
280 pid_remove(pid); 307 pid_remove(pid);
281 kill_lasttime = 0;
282} 308}
283 309
284static void cmd_target(int ntargets, int *params) { 310static void cmd_target(int ntargets, int *params) {
@@ -574,7 +600,6 @@ static int kill_one_process(struct proc *procp, int other_free, int other_file,
574 first ? "" : "~", other_file * page_k, minfree * page_k, min_score_adj, 600 first ? "" : "~", other_file * page_k, minfree * page_k, min_score_adj,
575 first ? "" : "~", other_free * page_k, other_free >= 0 ? "above" : "below"); 601 first ? "" : "~", other_free * page_k, other_free >= 0 ? "above" : "below");
576 r = kill(pid, SIGKILL); 602 r = kill(pid, SIGKILL);
577 killProcessGroup(uid, pid, SIGKILL);
578 pid_remove(pid); 603 pid_remove(pid);
579 604
580 if (r) { 605 if (r) {
@@ -589,24 +614,12 @@ static int kill_one_process(struct proc *procp, int other_free, int other_file,
589 * Find a process to kill based on the current (possibly estimated) free memory 614 * Find a process to kill based on the current (possibly estimated) free memory
590 * and cached memory sizes. Returns the size of the killed processes. 615 * and cached memory sizes. Returns the size of the killed processes.
591 */ 616 */
592static int find_and_kill_process(int other_free, int other_file, bool first) 617static int find_and_kill_process(int other_free, int other_file, bool first, int min_score_adj)
593{ 618{
594 int i; 619 int i;
595 int min_score_adj = OOM_SCORE_ADJ_MAX + 1;
596 int minfree = 0; 620 int minfree = 0;
597 int killed_size = 0; 621 int killed_size = 0;
598 622
599 for (i = 0; i < lowmem_targets_size; i++) {
600 minfree = lowmem_minfree[i];
601 if (other_free < minfree && other_file < minfree) {
602 min_score_adj = lowmem_adj[i];
603 break;
604 }
605 }
606
607 if (min_score_adj == OOM_SCORE_ADJ_MAX + 1)
608 return 0;
609
610 for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) { 623 for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
611 struct proc *procp; 624 struct proc *procp;
612 625
@@ -626,42 +639,33 @@ retry:
626 return 0; 639 return 0;
627} 640}
628 641
629static void mp_event(uint32_t events __unused) { 642static void mp_event_common(bool is_critical) {
630 int ret; 643 int ret;
631 unsigned long long evcount; 644 unsigned long long evcount;
632 struct sysmeminfo mi;
633 int other_free;
634 int other_file;
635 int killed_size;
636 bool first = true; 645 bool first = true;
646 int min_adj_score = is_critical ? 0 : 800;
647 int index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX;
637 648
638 ret = read(mpevfd, &evcount, sizeof(evcount)); 649 ret = read(mpevfd[index], &evcount, sizeof(evcount));
639 if (ret < 0) 650 if (ret < 0)
640 ALOGE("Error reading memory pressure event fd; errno=%d", 651 ALOGE("Error reading memory pressure event fd; errno=%d",
641 errno); 652 errno);
642 653
643 if (time(NULL) - kill_lasttime < KILL_TIMEOUT) 654 if (find_and_kill_process(0, 0, first, min_adj_score) == 0) {
644 return; 655 ALOGI("Nothing to kill");
645
646 while (zoneinfo_parse(&mi) < 0) {
647 // Failed to read /proc/zoneinfo, assume ENOMEM and kill something
648 find_and_kill_process(0, 0, true);
649 } 656 }
657}
650 658
651 other_free = mi.nr_free_pages - mi.totalreserve_pages; 659static void mp_event(uint32_t events __unused) {
652 other_file = mi.nr_file_pages - mi.nr_shmem; 660 mp_event_common(false);
661}
653 662
654 do { 663static void mp_event_critical(uint32_t events __unused) {
655 killed_size = find_and_kill_process(other_free, other_file, first); 664 ALOGI("Memory pressure critical");
656 if (killed_size > 0) { 665 mp_event_common(true);
657 first = false;
658 other_free += killed_size;
659 other_file += killed_size;
660 }
661 } while (killed_size > 0);
662} 666}
663 667
664static int init_mp(char *levelstr, void *event_handler) 668static int init_mp_common(char *levelstr, void *event_handler, bool is_critical)
665{ 669{
666 int mpfd; 670 int mpfd;
667 int evfd; 671 int evfd;
@@ -669,6 +673,7 @@ static int init_mp(char *levelstr, void *event_handler)
669 char buf[256]; 673 char buf[256];
670 struct epoll_event epev; 674 struct epoll_event epev;
671 int ret; 675 int ret;
676 int mpevfd_index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX;
672 677
673 mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC); 678 mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
674 if (mpfd < 0) { 679 if (mpfd < 0) {
@@ -709,7 +714,7 @@ static int init_mp(char *levelstr, void *event_handler)
709 goto err; 714 goto err;
710 } 715 }
711 maxevents++; 716 maxevents++;
712 mpevfd = evfd; 717 mpevfd[mpevfd_index] = evfd;
713 return 0; 718 return 0;
714 719
715err: 720err:
@@ -722,6 +727,16 @@ err_open_mpfd:
722 return -1; 727 return -1;
723} 728}
724 729
730static int init_mp_medium()
731{
732 return init_mp_common(MEMPRESSURE_WATCH_MEDIUM_LEVEL, (void *)&mp_event, false);
733}
734
735static int init_mp_critical()
736{
737 return init_mp_common(MEMPRESSURE_WATCH_CRITICAL_LEVEL, (void *)&mp_event_critical, true);
738}
739
725static int init(void) { 740static int init(void) {
726 struct epoll_event epev; 741 struct epoll_event epev;
727 int i; 742 int i;
@@ -763,7 +778,8 @@ static int init(void) {
763 if (use_inkernel_interface) { 778 if (use_inkernel_interface) {
764 ALOGI("Using in-kernel low memory killer interface"); 779 ALOGI("Using in-kernel low memory killer interface");
765 } else { 780 } else {
766 ret = init_mp(MEMPRESSURE_WATCH_LEVEL, (void *)&mp_event); 781 ret = init_mp_medium();
782 ret |= init_mp_critical();
767 if (ret) 783 if (ret)
768 ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer"); 784 ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
769 } 785 }