lmkd: Change procfs read routine to handle files larger than 1 page in size
Files like /proc/zoneinfo or /proc/<pid>/status can be larger than 4KB page size. Change reread_file routine to resize read buffer whenever it is not big enough to read the entire file. Start with 1-page buffer and double its size until it's big enough to read the entire file. Read /proc/zoneinfo during initialization to initialize the buffer to a big enough size and avoid re-allocations when under memory pressure. Bug: 137010962 Test: lmkd_unit_test Change-Id: If9a5b0d27c2f4de9063f0fd0f36f908ece87dcce Signed-off-by: Suren Baghdasaryan <surenb@google.com>
This commit is contained in:
parent
7c2f94bdf0
commit
a77b327bb9
1 changed files with 52 additions and 18 deletions
70
lmkd/lmkd.c
70
lmkd/lmkd.c
|
|
@ -424,28 +424,50 @@ static ssize_t read_all(int fd, char *buf, size_t max_len)
|
|||
* memory pressure to minimize file opening which by itself requires kernel
|
||||
* memory allocation and might result in a stall on memory stressed system.
|
||||
*/
|
||||
static int reread_file(struct reread_data *data, char *buf, size_t buf_size) {
|
||||
static char *reread_file(struct reread_data *data) {
|
||||
/* start with page-size buffer and increase if needed */
|
||||
static ssize_t buf_size = PAGE_SIZE;
|
||||
static char *new_buf, *buf = NULL;
|
||||
ssize_t size;
|
||||
|
||||
if (data->fd == -1) {
|
||||
data->fd = open(data->filename, O_RDONLY | O_CLOEXEC);
|
||||
if (data->fd == -1) {
|
||||
/* First-time buffer initialization */
|
||||
if (!buf && (buf = malloc(buf_size)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data->fd = TEMP_FAILURE_RETRY(open(data->filename, O_RDONLY | O_CLOEXEC));
|
||||
if (data->fd < 0) {
|
||||
ALOGE("%s open: %s", data->filename, strerror(errno));
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
size = read_all(data->fd, buf, buf_size - 1);
|
||||
if (size < 0) {
|
||||
ALOGE("%s read: %s", data->filename, strerror(errno));
|
||||
close(data->fd);
|
||||
data->fd = -1;
|
||||
return -1;
|
||||
while (true) {
|
||||
size = read_all(data->fd, buf, buf_size - 1);
|
||||
if (size < 0) {
|
||||
ALOGE("%s read: %s", data->filename, strerror(errno));
|
||||
close(data->fd);
|
||||
data->fd = -1;
|
||||
return NULL;
|
||||
}
|
||||
if (size < buf_size - 1) {
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Since we are reading /proc files we can't use fstat to find out
|
||||
* the real size of the file. Double the buffer size and keep retrying.
|
||||
*/
|
||||
if ((new_buf = realloc(buf, buf_size * 2)) == NULL) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
buf = new_buf;
|
||||
buf_size *= 2;
|
||||
}
|
||||
ALOG_ASSERT((size_t)size < buf_size - 1, "%s too large", data->filename);
|
||||
buf[size] = 0;
|
||||
|
||||
return 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static struct proc *pid_lookup(int pid) {
|
||||
|
|
@ -1200,13 +1222,13 @@ static int zoneinfo_parse(union zoneinfo *zi) {
|
|||
.filename = ZONEINFO_PATH,
|
||||
.fd = -1,
|
||||
};
|
||||
char buf[PAGE_SIZE];
|
||||
char *buf;
|
||||
char *save_ptr;
|
||||
char *line;
|
||||
|
||||
memset(zi, 0, sizeof(union zoneinfo));
|
||||
|
||||
if (reread_file(&file_data, buf, sizeof(buf)) < 0) {
|
||||
if ((buf = reread_file(&file_data)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -1254,13 +1276,13 @@ static int meminfo_parse(union meminfo *mi) {
|
|||
.filename = MEMINFO_PATH,
|
||||
.fd = -1,
|
||||
};
|
||||
char buf[PAGE_SIZE];
|
||||
char *buf;
|
||||
char *save_ptr;
|
||||
char *line;
|
||||
|
||||
memset(mi, 0, sizeof(union meminfo));
|
||||
|
||||
if (reread_file(&file_data, buf, sizeof(buf)) < 0) {
|
||||
if ((buf = reread_file(&file_data)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -1542,9 +1564,9 @@ static int find_and_kill_process(int min_score_adj) {
|
|||
static int64_t get_memory_usage(struct reread_data *file_data) {
|
||||
int ret;
|
||||
int64_t mem_usage;
|
||||
char buf[32];
|
||||
char *buf;
|
||||
|
||||
if (reread_file(file_data, buf, sizeof(buf)) < 0) {
|
||||
if ((buf = reread_file(file_data)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -2014,6 +2036,10 @@ static void init_poll_kernel() {
|
|||
#endif
|
||||
|
||||
static int init(void) {
|
||||
struct reread_data file_data = {
|
||||
.filename = ZONEINFO_PATH,
|
||||
.fd = -1,
|
||||
};
|
||||
struct epoll_event epev;
|
||||
int i;
|
||||
int ret;
|
||||
|
|
@ -2091,6 +2117,14 @@ static int init(void) {
|
|||
|
||||
memset(killcnt_idx, KILLCNT_INVALID_IDX, sizeof(killcnt_idx));
|
||||
|
||||
/*
|
||||
* Read zoneinfo as the biggest file we read to create and size the initial
|
||||
* read buffer and avoid memory re-allocations during memory pressure
|
||||
*/
|
||||
if (reread_file(&file_data) == NULL) {
|
||||
ALOGE("Failed to read %s: %s", file_data.filename, strerror(errno));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue