summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lmkd/Android.bp15
-rw-r--r--lmkd/include/liblmkd_utils.h54
-rw-r--r--lmkd/include/lmkd.h147
-rw-r--r--lmkd/liblmkd_utils.c76
-rw-r--r--lmkd/lmkd.c100
5 files changed, 343 insertions, 49 deletions
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 76d308a3c..d172755bd 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -6,6 +6,7 @@ cc_binary {
6 "liblog", 6 "liblog",
7 "libcutils", 7 "libcutils",
8 ], 8 ],
9 local_include_dirs: ["include"],
9 cflags: ["-Werror"], 10 cflags: ["-Werror"],
10 11
11 init_rc: ["lmkd.rc"], 12 init_rc: ["lmkd.rc"],
@@ -18,3 +19,17 @@ cc_binary {
18 }, 19 },
19 }, 20 },
20} 21}
22
23cc_library_static {
24 name: "liblmkd_utils",
25 srcs: ["liblmkd_utils.c"],
26 shared_libs: [
27 "libcutils",
28 ],
29 export_include_dirs: ["include"],
30 cppflags: [
31 "-g",
32 "-Wall",
33 "-Werror",
34 ]
35}
diff --git a/lmkd/include/liblmkd_utils.h b/lmkd/include/liblmkd_utils.h
new file mode 100644
index 000000000..72e3f4a2b
--- /dev/null
+++ b/lmkd/include/liblmkd_utils.h
@@ -0,0 +1,54 @@
1/*
2 * Copyright 2018 Google, Inc
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#ifndef _LIBLMKD_UTILS_H_
18#define _LIBLMKD_UTILS_H_
19
20#include <sys/cdefs.h>
21#include <sys/types.h>
22
23#include <lmkd.h>
24
25__BEGIN_DECLS
26
27/*
28 * Connects to lmkd process and returns socket handle.
29 * On success returns socket handle.
30 * On error, -1 is returned, and errno is set appropriately.
31 */
32int lmkd_connect();
33
34/*
35 * Registers a process with lmkd and sets its oomadj score.
36 * On success returns 0.
37 * On error, -1 is returned.
38 * In the case of error errno is set appropriately.
39 */
40int lmkd_register_proc(int sock, struct lmk_procprio *params);
41
42/*
43 * Creates memcg directory for given process.
44 * On success returns 0.
45 * -1 is returned if path creation failed.
46 * -2 is returned if tasks file open operation failed.
47 * -3 is returned if tasks file write operation failed.
48 * In the case of error errno is set appropriately.
49 */
50int create_memcg(uid_t uid, pid_t pid);
51
52__END_DECLS
53
54#endif /* _LIBLMKD_UTILS_H_ */
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
new file mode 100644
index 000000000..fe6364d82
--- /dev/null
+++ b/lmkd/include/lmkd.h
@@ -0,0 +1,147 @@
1/*
2 * Copyright 2018 Google, Inc
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#ifndef _LMKD_H_
18#define _LMKD_H_
19
20#include <arpa/inet.h>
21#include <sys/cdefs.h>
22#include <sys/types.h>
23
24__BEGIN_DECLS
25
26/*
27 * Supported LMKD commands
28 */
29enum lmk_cmd {
30 LMK_TARGET = 0, /* Associate minfree with oom_adj_score */
31 LMK_PROCPRIO, /* Register a process and set its oom_adj_score */
32 LMK_PROCREMOVE, /* Unregister a process */
33};
34
35/*
36 * Max number of targets in LMK_TARGET command.
37 */
38#define MAX_TARGETS 6
39
40/*
41 * Max packet length in bytes.
42 * Longest packet is LMK_TARGET followed by MAX_TARGETS
43 * of minfree and oom_adj_score values
44 */
45#define CTRL_PACKET_MAX_SIZE (sizeof(int) * (MAX_TARGETS * 2 + 1))
46
47/* LMKD packet - first int is lmk_cmd followed by payload */
48typedef int LMKD_CTRL_PACKET[CTRL_PACKET_MAX_SIZE / sizeof(int)];
49
50/* Get LMKD packet command */
51inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) {
52 return (enum lmk_cmd)ntohl(pack[0]);
53}
54
55/* LMK_TARGET packet payload */
56struct lmk_target {
57 int minfree;
58 int oom_adj_score;
59};
60
61/*
62 * For LMK_TARGET packet get target_idx-th payload.
63 * Warning: no checks performed, caller should ensure valid parameters.
64 */
65inline void lmkd_pack_get_target(LMKD_CTRL_PACKET packet,
66 int target_idx, struct lmk_target *target) {
67 target->minfree = ntohl(packet[target_idx * 2 + 1]);
68 target->oom_adj_score = ntohl(packet[target_idx * 2 + 2]);
69}
70
71/*
72 * Prepare LMK_TARGET packet and return packet size in bytes.
73 * Warning: no checks performed, caller should ensure valid parameters.
74 */
75inline size_t lmkd_pack_set_target(LMKD_CTRL_PACKET packet,
76 struct lmk_target *targets,
77 size_t target_cnt) {
78 int idx = 0;
79 packet[idx++] = htonl(LMK_TARGET);
80 while (target_cnt) {
81 packet[idx++] = htonl(targets->minfree);
82 packet[idx++] = htonl(targets->oom_adj_score);
83 targets++;
84 target_cnt--;
85 }
86 return idx * sizeof(int);
87}
88
89/* LMK_PROCPRIO packet payload */
90struct lmk_procprio {
91 pid_t pid;
92 uid_t uid;
93 int oomadj;
94};
95
96/*
97 * For LMK_PROCPRIO packet get its payload.
98 * Warning: no checks performed, caller should ensure valid parameters.
99 */
100inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet,
101 struct lmk_procprio *params) {
102 params->pid = (pid_t)ntohl(packet[1]);
103 params->uid = (uid_t)ntohl(packet[2]);
104 params->oomadj = ntohl(packet[3]);
105}
106
107/*
108 * Prepare LMK_PROCPRIO packet and return packet size in bytes.
109 * Warning: no checks performed, caller should ensure valid parameters.
110 */
111inline size_t lmkd_pack_set_procprio(LMKD_CTRL_PACKET packet,
112 struct lmk_procprio *params) {
113 packet[0] = htonl(LMK_PROCPRIO);
114 packet[1] = htonl(params->pid);
115 packet[2] = htonl(params->uid);
116 packet[3] = htonl(params->oomadj);
117 return 4 * sizeof(int);
118}
119
120/* LMK_PROCREMOVE packet payload */
121struct lmk_procremove {
122 pid_t pid;
123};
124
125/*
126 * For LMK_PROCREMOVE packet get its payload.
127 * Warning: no checks performed, caller should ensure valid parameters.
128 */
129inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet,
130 struct lmk_procremove *params) {
131 params->pid = (pid_t)ntohl(packet[1]);
132}
133
134/*
135 * Prepare LMK_PROCREMOVE packet and return packet size in bytes.
136 * Warning: no checks performed, caller should ensure valid parameters.
137 */
138inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
139 struct lmk_procprio *params) {
140 packet[0] = htonl(LMK_PROCREMOVE);
141 packet[1] = htonl(params->pid);
142 return 2 * sizeof(int);
143}
144
145__END_DECLS
146
147#endif /* _LMKD_H_ */
diff --git a/lmkd/liblmkd_utils.c b/lmkd/liblmkd_utils.c
new file mode 100644
index 000000000..fa3b7a920
--- /dev/null
+++ b/lmkd/liblmkd_utils.c
@@ -0,0 +1,76 @@
1/*
2 * Copyright 2018 Google, Inc
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#include <errno.h>
18#include <fcntl.h>
19#include <sys/cdefs.h>
20#include <sys/stat.h>
21#include <sys/types.h>
22#include <stdio.h>
23#include <unistd.h>
24
25#include <liblmkd_utils.h>
26#include <cutils/sockets.h>
27
28int lmkd_connect() {
29 return socket_local_client("lmkd",
30 ANDROID_SOCKET_NAMESPACE_RESERVED,
31 SOCK_SEQPACKET);
32}
33
34int lmkd_register_proc(int sock, struct lmk_procprio *params) {
35 LMKD_CTRL_PACKET packet;
36 size_t size;
37 int ret;
38
39 size = lmkd_pack_set_procprio(packet, params);
40 ret = TEMP_FAILURE_RETRY(write(sock, packet, size));
41
42 return (ret < 0) ? -1 : 0;
43}
44
45int create_memcg(uid_t uid, pid_t pid) {
46 char buf[256];
47 int tasks_file;
48 int written;
49
50 snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u", uid);
51 if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
52 errno != EEXIST) {
53 return -1;
54 }
55
56 snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u", uid, pid);
57 if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
58 errno != EEXIST) {
59 return -1;
60 }
61
62 snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u/tasks", uid, pid);
63 tasks_file = open(buf, O_WRONLY);
64 if (tasks_file < 0) {
65 return -2;
66 }
67 written = snprintf(buf, sizeof(buf), "%u", pid);
68 if (__predict_false(written >= (int)sizeof(buf))) {
69 written = sizeof(buf) - 1;
70 }
71 written = TEMP_FAILURE_RETRY(write(tasks_file, buf, written));
72 close(tasks_file);
73
74 return (written < 0) ? -3 : 0;
75}
76
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 2a7fedb44..45fa863b2 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -16,7 +16,6 @@
16 16
17#define LOG_TAG "lowmemorykiller" 17#define LOG_TAG "lowmemorykiller"
18 18
19#include <arpa/inet.h>
20#include <errno.h> 19#include <errno.h>
21#include <inttypes.h> 20#include <inttypes.h>
22#include <sched.h> 21#include <sched.h>
@@ -34,6 +33,7 @@
34 33
35#include <cutils/properties.h> 34#include <cutils/properties.h>
36#include <cutils/sockets.h> 35#include <cutils/sockets.h>
36#include <lmkd.h>
37#include <log/log.h> 37#include <log/log.h>
38 38
39/* 39/*
@@ -71,19 +71,6 @@
71#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) 71#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
72#define EIGHT_MEGA (1 << 23) 72#define EIGHT_MEGA (1 << 23)
73 73
74enum lmk_cmd {
75 LMK_TARGET,
76 LMK_PROCPRIO,
77 LMK_PROCREMOVE,
78};
79
80#define MAX_TARGETS 6
81/*
82 * longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio
83 * values
84 */
85#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1))
86
87/* default to old in-kernel interface if no memory pressure events */ 74/* default to old in-kernel interface if no memory pressure events */
88static int use_inkernel_interface = 1; 75static int use_inkernel_interface = 1;
89static bool has_inkernel_module; 76static bool has_inkernel_module;
@@ -300,45 +287,49 @@ static void writefilestring(const char *path, char *s) {
300 close(fd); 287 close(fd);
301} 288}
302 289
303static void cmd_procprio(int pid, int uid, int oomadj) { 290static void cmd_procprio(LMKD_CTRL_PACKET packet) {
304 struct proc *procp; 291 struct proc *procp;
305 char path[80]; 292 char path[80];
306 char val[20]; 293 char val[20];
307 int soft_limit_mult; 294 int soft_limit_mult;
295 struct lmk_procprio params;
296
297 lmkd_pack_get_procprio(packet, &params);
308 298
309 if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) { 299 if (params.oomadj < OOM_SCORE_ADJ_MIN ||
310 ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj); 300 params.oomadj > OOM_SCORE_ADJ_MAX) {
301 ALOGE("Invalid PROCPRIO oomadj argument %d", params.oomadj);
311 return; 302 return;
312 } 303 }
313 304
314 snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid); 305 snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", params.pid);
315 snprintf(val, sizeof(val), "%d", oomadj); 306 snprintf(val, sizeof(val), "%d", params.oomadj);
316 writefilestring(path, val); 307 writefilestring(path, val);
317 308
318 if (use_inkernel_interface) 309 if (use_inkernel_interface)
319 return; 310 return;
320 311
321 if (oomadj >= 900) { 312 if (params.oomadj >= 900) {
322 soft_limit_mult = 0; 313 soft_limit_mult = 0;
323 } else if (oomadj >= 800) { 314 } else if (params.oomadj >= 800) {
324 soft_limit_mult = 0; 315 soft_limit_mult = 0;
325 } else if (oomadj >= 700) { 316 } else if (params.oomadj >= 700) {
326 soft_limit_mult = 0; 317 soft_limit_mult = 0;
327 } else if (oomadj >= 600) { 318 } else if (params.oomadj >= 600) {
328 // Launcher should be perceptible, don't kill it. 319 // Launcher should be perceptible, don't kill it.
329 oomadj = 200; 320 params.oomadj = 200;
330 soft_limit_mult = 1; 321 soft_limit_mult = 1;
331 } else if (oomadj >= 500) { 322 } else if (params.oomadj >= 500) {
332 soft_limit_mult = 0; 323 soft_limit_mult = 0;
333 } else if (oomadj >= 400) { 324 } else if (params.oomadj >= 400) {
334 soft_limit_mult = 0; 325 soft_limit_mult = 0;
335 } else if (oomadj >= 300) { 326 } else if (params.oomadj >= 300) {
336 soft_limit_mult = 1; 327 soft_limit_mult = 1;
337 } else if (oomadj >= 200) { 328 } else if (params.oomadj >= 200) {
338 soft_limit_mult = 2; 329 soft_limit_mult = 2;
339 } else if (oomadj >= 100) { 330 } else if (params.oomadj >= 100) {
340 soft_limit_mult = 10; 331 soft_limit_mult = 10;
341 } else if (oomadj >= 0) { 332 } else if (params.oomadj >= 0) {
342 soft_limit_mult = 20; 333 soft_limit_mult = 20;
343 } else { 334 } else {
344 // Persistent processes will have a large 335 // Persistent processes will have a large
@@ -346,11 +337,13 @@ static void cmd_procprio(int pid, int uid, int oomadj) {
346 soft_limit_mult = 64; 337 soft_limit_mult = 64;
347 } 338 }
348 339
349 snprintf(path, sizeof(path), "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes", uid, pid); 340 snprintf(path, sizeof(path),
341 "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
342 params.uid, params.pid);
350 snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA); 343 snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
351 writefilestring(path, val); 344 writefilestring(path, val);
352 345
353 procp = pid_lookup(pid); 346 procp = pid_lookup(params.pid);
354 if (!procp) { 347 if (!procp) {
355 procp = malloc(sizeof(struct proc)); 348 procp = malloc(sizeof(struct proc));
356 if (!procp) { 349 if (!procp) {
@@ -358,33 +351,38 @@ static void cmd_procprio(int pid, int uid, int oomadj) {
358 return; 351 return;
359 } 352 }
360 353
361 procp->pid = pid; 354 procp->pid = params.pid;
362 procp->uid = uid; 355 procp->uid = params.uid;
363 procp->oomadj = oomadj; 356 procp->oomadj = params.oomadj;
364 proc_insert(procp); 357 proc_insert(procp);
365 } else { 358 } else {
366 proc_unslot(procp); 359 proc_unslot(procp);
367 procp->oomadj = oomadj; 360 procp->oomadj = params.oomadj;
368 proc_slot(procp); 361 proc_slot(procp);
369 } 362 }
370} 363}
371 364
372static void cmd_procremove(int pid) { 365static void cmd_procremove(LMKD_CTRL_PACKET packet) {
366 struct lmk_procremove params;
367
373 if (use_inkernel_interface) 368 if (use_inkernel_interface)
374 return; 369 return;
375 370
376 pid_remove(pid); 371 lmkd_pack_get_procremove(packet, &params);
372 pid_remove(params.pid);
377} 373}
378 374
379static void cmd_target(int ntargets, int *params) { 375static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
380 int i; 376 int i;
377 struct lmk_target target;
381 378
382 if (ntargets > (int)ARRAY_SIZE(lowmem_adj)) 379 if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
383 return; 380 return;
384 381
385 for (i = 0; i < ntargets; i++) { 382 for (i = 0; i < ntargets; i++) {
386 lowmem_minfree[i] = ntohl(*params++); 383 lmkd_pack_get_target(packet, i, &target);
387 lowmem_adj[i] = ntohl(*params++); 384 lowmem_minfree[i] = target.minfree;
385 lowmem_adj[i] = target.oom_adj_score;
388 } 386 }
389 387
390 lowmem_targets_size = ntargets; 388 lowmem_targets_size = ntargets;
@@ -445,38 +443,42 @@ static int ctrl_data_read(int dsock_idx, char *buf, size_t bufsz) {
445} 443}
446 444
447static void ctrl_command_handler(int dsock_idx) { 445static void ctrl_command_handler(int dsock_idx) {
448 int ibuf[CTRL_PACKET_MAX / sizeof(int)]; 446 LMKD_CTRL_PACKET packet;
449 int len; 447 int len;
450 int cmd = -1; 448 enum lmk_cmd cmd;
451 int nargs; 449 int nargs;
452 int targets; 450 int targets;
453 451
454 len = ctrl_data_read(dsock_idx, (char *)ibuf, CTRL_PACKET_MAX); 452 len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE);
455 if (len <= 0) 453 if (len <= 0)
456 return; 454 return;
457 455
456 if (len < (int)sizeof(int)) {
457 ALOGE("Wrong control socket read length len=%d", len);
458 return;
459 }
460
461 cmd = lmkd_pack_get_cmd(packet);
458 nargs = len / sizeof(int) - 1; 462 nargs = len / sizeof(int) - 1;
459 if (nargs < 0) 463 if (nargs < 0)
460 goto wronglen; 464 goto wronglen;
461 465
462 cmd = ntohl(ibuf[0]);
463
464 switch(cmd) { 466 switch(cmd) {
465 case LMK_TARGET: 467 case LMK_TARGET:
466 targets = nargs / 2; 468 targets = nargs / 2;
467 if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj)) 469 if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
468 goto wronglen; 470 goto wronglen;
469 cmd_target(targets, &ibuf[1]); 471 cmd_target(targets, packet);
470 break; 472 break;
471 case LMK_PROCPRIO: 473 case LMK_PROCPRIO:
472 if (nargs != 3) 474 if (nargs != 3)
473 goto wronglen; 475 goto wronglen;
474 cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3])); 476 cmd_procprio(packet);
475 break; 477 break;
476 case LMK_PROCREMOVE: 478 case LMK_PROCREMOVE:
477 if (nargs != 1) 479 if (nargs != 1)
478 goto wronglen; 480 goto wronglen;
479 cmd_procremove(ntohl(ibuf[1])); 481 cmd_procremove(packet);
480 break; 482 break;
481 default: 483 default:
482 ALOGE("Received unknown command code %d", cmd); 484 ALOGE("Received unknown command code %d", cmd);