lmkd: Introduce liblmkd_utils for communicating with lmkd process
(cherry pick from commit 0f10051805
)
A number of tools and tests require communication with lmkd.
In order to avoid code duplication liblmkd_utils implements
functions commonly used when interacting with lmkd process.
Isolate communication protocol details into lmkd.h
Bug: 63631020
Change-Id: Id840983d55b7db60013d52dee0c3187943811822
Merged-In: Id840983d55b7db60013d52dee0c3187943811822
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
This commit is contained in:
parent
06f75707e1
commit
a92de71a51
5 changed files with 343 additions and 49 deletions
|
@ -6,6 +6,7 @@ cc_binary {
|
|||
"liblog",
|
||||
"libcutils",
|
||||
],
|
||||
local_include_dirs: ["include"],
|
||||
cflags: ["-Werror"],
|
||||
|
||||
init_rc: ["lmkd.rc"],
|
||||
|
@ -18,3 +19,17 @@ cc_binary {
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "liblmkd_utils",
|
||||
srcs: ["liblmkd_utils.c"],
|
||||
shared_libs: [
|
||||
"libcutils",
|
||||
],
|
||||
export_include_dirs: ["include"],
|
||||
cppflags: [
|
||||
"-g",
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
]
|
||||
}
|
||||
|
|
54
lmkd/include/liblmkd_utils.h
Normal file
54
lmkd/include/liblmkd_utils.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2018 Google, Inc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBLMKD_UTILS_H_
|
||||
#define _LIBLMKD_UTILS_H_
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <lmkd.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/*
|
||||
* Connects to lmkd process and returns socket handle.
|
||||
* On success returns socket handle.
|
||||
* On error, -1 is returned, and errno is set appropriately.
|
||||
*/
|
||||
int lmkd_connect();
|
||||
|
||||
/*
|
||||
* Registers a process with lmkd and sets its oomadj score.
|
||||
* On success returns 0.
|
||||
* On error, -1 is returned.
|
||||
* In the case of error errno is set appropriately.
|
||||
*/
|
||||
int lmkd_register_proc(int sock, struct lmk_procprio *params);
|
||||
|
||||
/*
|
||||
* Creates memcg directory for given process.
|
||||
* On success returns 0.
|
||||
* -1 is returned if path creation failed.
|
||||
* -2 is returned if tasks file open operation failed.
|
||||
* -3 is returned if tasks file write operation failed.
|
||||
* In the case of error errno is set appropriately.
|
||||
*/
|
||||
int create_memcg(uid_t uid, pid_t pid);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* _LIBLMKD_UTILS_H_ */
|
147
lmkd/include/lmkd.h
Normal file
147
lmkd/include/lmkd.h
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright 2018 Google, Inc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LMKD_H_
|
||||
#define _LMKD_H_
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/*
|
||||
* Supported LMKD commands
|
||||
*/
|
||||
enum lmk_cmd {
|
||||
LMK_TARGET = 0, /* Associate minfree with oom_adj_score */
|
||||
LMK_PROCPRIO, /* Register a process and set its oom_adj_score */
|
||||
LMK_PROCREMOVE, /* Unregister a process */
|
||||
};
|
||||
|
||||
/*
|
||||
* Max number of targets in LMK_TARGET command.
|
||||
*/
|
||||
#define MAX_TARGETS 6
|
||||
|
||||
/*
|
||||
* Max packet length in bytes.
|
||||
* Longest packet is LMK_TARGET followed by MAX_TARGETS
|
||||
* of minfree and oom_adj_score values
|
||||
*/
|
||||
#define CTRL_PACKET_MAX_SIZE (sizeof(int) * (MAX_TARGETS * 2 + 1))
|
||||
|
||||
/* LMKD packet - first int is lmk_cmd followed by payload */
|
||||
typedef int LMKD_CTRL_PACKET[CTRL_PACKET_MAX_SIZE / sizeof(int)];
|
||||
|
||||
/* Get LMKD packet command */
|
||||
inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) {
|
||||
return (enum lmk_cmd)ntohl(pack[0]);
|
||||
}
|
||||
|
||||
/* LMK_TARGET packet payload */
|
||||
struct lmk_target {
|
||||
int minfree;
|
||||
int oom_adj_score;
|
||||
};
|
||||
|
||||
/*
|
||||
* For LMK_TARGET packet get target_idx-th payload.
|
||||
* Warning: no checks performed, caller should ensure valid parameters.
|
||||
*/
|
||||
inline void lmkd_pack_get_target(LMKD_CTRL_PACKET packet,
|
||||
int target_idx, struct lmk_target *target) {
|
||||
target->minfree = ntohl(packet[target_idx * 2 + 1]);
|
||||
target->oom_adj_score = ntohl(packet[target_idx * 2 + 2]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare LMK_TARGET packet and return packet size in bytes.
|
||||
* Warning: no checks performed, caller should ensure valid parameters.
|
||||
*/
|
||||
inline size_t lmkd_pack_set_target(LMKD_CTRL_PACKET packet,
|
||||
struct lmk_target *targets,
|
||||
size_t target_cnt) {
|
||||
int idx = 0;
|
||||
packet[idx++] = htonl(LMK_TARGET);
|
||||
while (target_cnt) {
|
||||
packet[idx++] = htonl(targets->minfree);
|
||||
packet[idx++] = htonl(targets->oom_adj_score);
|
||||
targets++;
|
||||
target_cnt--;
|
||||
}
|
||||
return idx * sizeof(int);
|
||||
}
|
||||
|
||||
/* LMK_PROCPRIO packet payload */
|
||||
struct lmk_procprio {
|
||||
pid_t pid;
|
||||
uid_t uid;
|
||||
int oomadj;
|
||||
};
|
||||
|
||||
/*
|
||||
* For LMK_PROCPRIO packet get its payload.
|
||||
* Warning: no checks performed, caller should ensure valid parameters.
|
||||
*/
|
||||
inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet,
|
||||
struct lmk_procprio *params) {
|
||||
params->pid = (pid_t)ntohl(packet[1]);
|
||||
params->uid = (uid_t)ntohl(packet[2]);
|
||||
params->oomadj = ntohl(packet[3]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare LMK_PROCPRIO packet and return packet size in bytes.
|
||||
* Warning: no checks performed, caller should ensure valid parameters.
|
||||
*/
|
||||
inline size_t lmkd_pack_set_procprio(LMKD_CTRL_PACKET packet,
|
||||
struct lmk_procprio *params) {
|
||||
packet[0] = htonl(LMK_PROCPRIO);
|
||||
packet[1] = htonl(params->pid);
|
||||
packet[2] = htonl(params->uid);
|
||||
packet[3] = htonl(params->oomadj);
|
||||
return 4 * sizeof(int);
|
||||
}
|
||||
|
||||
/* LMK_PROCREMOVE packet payload */
|
||||
struct lmk_procremove {
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
/*
|
||||
* For LMK_PROCREMOVE packet get its payload.
|
||||
* Warning: no checks performed, caller should ensure valid parameters.
|
||||
*/
|
||||
inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet,
|
||||
struct lmk_procremove *params) {
|
||||
params->pid = (pid_t)ntohl(packet[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare LMK_PROCREMOVE packet and return packet size in bytes.
|
||||
* Warning: no checks performed, caller should ensure valid parameters.
|
||||
*/
|
||||
inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
|
||||
struct lmk_procprio *params) {
|
||||
packet[0] = htonl(LMK_PROCREMOVE);
|
||||
packet[1] = htonl(params->pid);
|
||||
return 2 * sizeof(int);
|
||||
}
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* _LMKD_H_ */
|
76
lmkd/liblmkd_utils.c
Normal file
76
lmkd/liblmkd_utils.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2018 Google, Inc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <liblmkd_utils.h>
|
||||
#include <cutils/sockets.h>
|
||||
|
||||
int lmkd_connect() {
|
||||
return socket_local_client("lmkd",
|
||||
ANDROID_SOCKET_NAMESPACE_RESERVED,
|
||||
SOCK_SEQPACKET);
|
||||
}
|
||||
|
||||
int lmkd_register_proc(int sock, struct lmk_procprio *params) {
|
||||
LMKD_CTRL_PACKET packet;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
size = lmkd_pack_set_procprio(packet, params);
|
||||
ret = TEMP_FAILURE_RETRY(write(sock, packet, size));
|
||||
|
||||
return (ret < 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
int create_memcg(uid_t uid, pid_t pid) {
|
||||
char buf[256];
|
||||
int tasks_file;
|
||||
int written;
|
||||
|
||||
snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u", uid);
|
||||
if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
|
||||
errno != EEXIST) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u", uid, pid);
|
||||
if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
|
||||
errno != EEXIST) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u/tasks", uid, pid);
|
||||
tasks_file = open(buf, O_WRONLY);
|
||||
if (tasks_file < 0) {
|
||||
return -2;
|
||||
}
|
||||
written = snprintf(buf, sizeof(buf), "%u", pid);
|
||||
if (__predict_false(written >= (int)sizeof(buf))) {
|
||||
written = sizeof(buf) - 1;
|
||||
}
|
||||
written = TEMP_FAILURE_RETRY(write(tasks_file, buf, written));
|
||||
close(tasks_file);
|
||||
|
||||
return (written < 0) ? -3 : 0;
|
||||
}
|
||||
|
100
lmkd/lmkd.c
100
lmkd/lmkd.c
|
@ -16,7 +16,6 @@
|
|||
|
||||
#define LOG_TAG "lowmemorykiller"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <sched.h>
|
||||
|
@ -34,6 +33,7 @@
|
|||
|
||||
#include <cutils/properties.h>
|
||||
#include <cutils/sockets.h>
|
||||
#include <lmkd.h>
|
||||
#include <log/log.h>
|
||||
|
||||
/*
|
||||
|
@ -71,19 +71,6 @@
|
|||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
|
||||
#define EIGHT_MEGA (1 << 23)
|
||||
|
||||
enum lmk_cmd {
|
||||
LMK_TARGET,
|
||||
LMK_PROCPRIO,
|
||||
LMK_PROCREMOVE,
|
||||
};
|
||||
|
||||
#define MAX_TARGETS 6
|
||||
/*
|
||||
* longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio
|
||||
* values
|
||||
*/
|
||||
#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1))
|
||||
|
||||
/* default to old in-kernel interface if no memory pressure events */
|
||||
static int use_inkernel_interface = 1;
|
||||
static bool has_inkernel_module;
|
||||
|
@ -300,45 +287,49 @@ static void writefilestring(const char *path, char *s) {
|
|||
close(fd);
|
||||
}
|
||||
|
||||
static void cmd_procprio(int pid, int uid, int oomadj) {
|
||||
static void cmd_procprio(LMKD_CTRL_PACKET packet) {
|
||||
struct proc *procp;
|
||||
char path[80];
|
||||
char val[20];
|
||||
int soft_limit_mult;
|
||||
struct lmk_procprio params;
|
||||
|
||||
if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) {
|
||||
ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
|
||||
lmkd_pack_get_procprio(packet, ¶ms);
|
||||
|
||||
if (params.oomadj < OOM_SCORE_ADJ_MIN ||
|
||||
params.oomadj > OOM_SCORE_ADJ_MAX) {
|
||||
ALOGE("Invalid PROCPRIO oomadj argument %d", params.oomadj);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
|
||||
snprintf(val, sizeof(val), "%d", oomadj);
|
||||
snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", params.pid);
|
||||
snprintf(val, sizeof(val), "%d", params.oomadj);
|
||||
writefilestring(path, val);
|
||||
|
||||
if (use_inkernel_interface)
|
||||
return;
|
||||
|
||||
if (oomadj >= 900) {
|
||||
if (params.oomadj >= 900) {
|
||||
soft_limit_mult = 0;
|
||||
} else if (oomadj >= 800) {
|
||||
} else if (params.oomadj >= 800) {
|
||||
soft_limit_mult = 0;
|
||||
} else if (oomadj >= 700) {
|
||||
} else if (params.oomadj >= 700) {
|
||||
soft_limit_mult = 0;
|
||||
} else if (oomadj >= 600) {
|
||||
} else if (params.oomadj >= 600) {
|
||||
// Launcher should be perceptible, don't kill it.
|
||||
oomadj = 200;
|
||||
params.oomadj = 200;
|
||||
soft_limit_mult = 1;
|
||||
} else if (oomadj >= 500) {
|
||||
} else if (params.oomadj >= 500) {
|
||||
soft_limit_mult = 0;
|
||||
} else if (oomadj >= 400) {
|
||||
} else if (params.oomadj >= 400) {
|
||||
soft_limit_mult = 0;
|
||||
} else if (oomadj >= 300) {
|
||||
} else if (params.oomadj >= 300) {
|
||||
soft_limit_mult = 1;
|
||||
} else if (oomadj >= 200) {
|
||||
} else if (params.oomadj >= 200) {
|
||||
soft_limit_mult = 2;
|
||||
} else if (oomadj >= 100) {
|
||||
} else if (params.oomadj >= 100) {
|
||||
soft_limit_mult = 10;
|
||||
} else if (oomadj >= 0) {
|
||||
} else if (params.oomadj >= 0) {
|
||||
soft_limit_mult = 20;
|
||||
} else {
|
||||
// Persistent processes will have a large
|
||||
|
@ -346,11 +337,13 @@ static void cmd_procprio(int pid, int uid, int oomadj) {
|
|||
soft_limit_mult = 64;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes", uid, pid);
|
||||
snprintf(path, sizeof(path),
|
||||
"/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
|
||||
params.uid, params.pid);
|
||||
snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
|
||||
writefilestring(path, val);
|
||||
|
||||
procp = pid_lookup(pid);
|
||||
procp = pid_lookup(params.pid);
|
||||
if (!procp) {
|
||||
procp = malloc(sizeof(struct proc));
|
||||
if (!procp) {
|
||||
|
@ -358,33 +351,38 @@ static void cmd_procprio(int pid, int uid, int oomadj) {
|
|||
return;
|
||||
}
|
||||
|
||||
procp->pid = pid;
|
||||
procp->uid = uid;
|
||||
procp->oomadj = oomadj;
|
||||
procp->pid = params.pid;
|
||||
procp->uid = params.uid;
|
||||
procp->oomadj = params.oomadj;
|
||||
proc_insert(procp);
|
||||
} else {
|
||||
proc_unslot(procp);
|
||||
procp->oomadj = oomadj;
|
||||
procp->oomadj = params.oomadj;
|
||||
proc_slot(procp);
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_procremove(int pid) {
|
||||
static void cmd_procremove(LMKD_CTRL_PACKET packet) {
|
||||
struct lmk_procremove params;
|
||||
|
||||
if (use_inkernel_interface)
|
||||
return;
|
||||
|
||||
pid_remove(pid);
|
||||
lmkd_pack_get_procremove(packet, ¶ms);
|
||||
pid_remove(params.pid);
|
||||
}
|
||||
|
||||
static void cmd_target(int ntargets, int *params) {
|
||||
static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
|
||||
int i;
|
||||
struct lmk_target target;
|
||||
|
||||
if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
|
||||
return;
|
||||
|
||||
for (i = 0; i < ntargets; i++) {
|
||||
lowmem_minfree[i] = ntohl(*params++);
|
||||
lowmem_adj[i] = ntohl(*params++);
|
||||
lmkd_pack_get_target(packet, i, &target);
|
||||
lowmem_minfree[i] = target.minfree;
|
||||
lowmem_adj[i] = target.oom_adj_score;
|
||||
}
|
||||
|
||||
lowmem_targets_size = ntargets;
|
||||
|
@ -445,38 +443,42 @@ static int ctrl_data_read(int dsock_idx, char *buf, size_t bufsz) {
|
|||
}
|
||||
|
||||
static void ctrl_command_handler(int dsock_idx) {
|
||||
int ibuf[CTRL_PACKET_MAX / sizeof(int)];
|
||||
LMKD_CTRL_PACKET packet;
|
||||
int len;
|
||||
int cmd = -1;
|
||||
enum lmk_cmd cmd;
|
||||
int nargs;
|
||||
int targets;
|
||||
|
||||
len = ctrl_data_read(dsock_idx, (char *)ibuf, CTRL_PACKET_MAX);
|
||||
len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE);
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
if (len < (int)sizeof(int)) {
|
||||
ALOGE("Wrong control socket read length len=%d", len);
|
||||
return;
|
||||
}
|
||||
|
||||
cmd = lmkd_pack_get_cmd(packet);
|
||||
nargs = len / sizeof(int) - 1;
|
||||
if (nargs < 0)
|
||||
goto wronglen;
|
||||
|
||||
cmd = ntohl(ibuf[0]);
|
||||
|
||||
switch(cmd) {
|
||||
case LMK_TARGET:
|
||||
targets = nargs / 2;
|
||||
if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
|
||||
goto wronglen;
|
||||
cmd_target(targets, &ibuf[1]);
|
||||
cmd_target(targets, packet);
|
||||
break;
|
||||
case LMK_PROCPRIO:
|
||||
if (nargs != 3)
|
||||
goto wronglen;
|
||||
cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3]));
|
||||
cmd_procprio(packet);
|
||||
break;
|
||||
case LMK_PROCREMOVE:
|
||||
if (nargs != 1)
|
||||
goto wronglen;
|
||||
cmd_procremove(ntohl(ibuf[1]));
|
||||
cmd_procremove(packet);
|
||||
break;
|
||||
default:
|
||||
ALOGE("Received unknown command code %d", cmd);
|
||||
|
|
Loading…
Reference in a new issue