From d326dd3fe0d9bc171967572c7b5583a1e718dbaf Mon Sep 17 00:00:00 2001 From: SangYoun Kwak Date: Thu, 16 Feb 2023 11:38:34 +0900 Subject: [PATCH] lowmem-governor: Refactor to use the governor independently The governor was modified to use it independently. * The lists of apps/procs will be created by lowmem.c, and it will be passed to the governor(lowmem_governor_get_kill_candidates). * The governor will make an array(candidates) of "filtered and sorted". It consists of "struct task_info *". * The "struct task_info" was modified to store some informations from proc_app_info for governor. Also the pointer of oom_killed from proc_app_info was added for the killer. * According to the above change, killer of lowmem-controller.c was modified. * Codes that depend on resourced was removed from governor to lowmem.c. Change-Id: I1350f2b511d13e3dc7cb1b2efb9e37bc62381275 Signed-off-by: SangYoun Kwak --- src/common/procfs/procfs.h | 1 + src/resource-limiter/memory/lowmem-controller.c | 17 +- src/resource-limiter/memory/lowmem-governor.c | 234 ++++------------------ src/resource-limiter/memory/lowmem-governor.h | 37 +++- src/resource-limiter/memory/lowmem.c | 247 ++++++++++++++++++++++-- src/resource-limiter/memory/lowmem.h | 25 +-- 6 files changed, 317 insertions(+), 244 deletions(-) diff --git a/src/common/procfs/procfs.h b/src/common/procfs/procfs.h index df1b12b..9bc0fbd 100644 --- a/src/common/procfs/procfs.h +++ b/src/common/procfs/procfs.h @@ -71,6 +71,7 @@ extern "C" { #define PROC_ZONEINFO_PATH "/proc/zoneinfo" #define PROC_PAGETYPEINFO_PATH "/proc/pagetypeinfo" #define PROC_BUDDYINFO_PATH "/proc/buddyinfo" +#define PROC_APP_ATTR_PATH "/proc/%d/attr/current" enum meminfo_id { MEMINFO_ID_INVALID = -1, diff --git a/src/resource-limiter/memory/lowmem-controller.c b/src/resource-limiter/memory/lowmem-controller.c index 118b7cb..4e91048 100644 --- a/src/resource-limiter/memory/lowmem-controller.c +++ b/src/resource-limiter/memory/lowmem-controller.c @@ -46,7 +46,6 @@ static int lowmem_kill_victim(const struct task_info *tsk, int flags, int ret; char appname[PATH_MAX]; int sigterm = 0; - struct proc_app_info *pai; pid = tsk->pid; @@ -64,8 +63,11 @@ static int lowmem_kill_victim(const struct task_info *tsk, int flags, return RESOURCED_ERROR_FAIL; } - pai = tsk->pai; - if (pai) { + /** + * if tsk is an app, then proc_app_info_oom_killed is not NULL, + * else proc_app_info_oom_killed is NULL. + */ + if (tsk->proc_app_info_oom_killed != NULL) { resourced_proc_status_change(PROC_CGROUP_SET_TERMINATE_REQUEST, pid, NULL, NULL, PROC_TYPE_NONE); @@ -73,14 +75,13 @@ static int lowmem_kill_victim(const struct task_info *tsk, int flags, sigterm = 1; } else if (tsk->oom_score_lru > OOMADJ_BACKGRD_LOCKED && tsk->oom_score_lru < OOMADJ_BACKGRD_UNLOCKED) { - int app_flag = pai->flags; - sigterm = app_flag & PROC_SIGTERM; + sigterm = tsk->proc_app_info_flags & PROC_SIGTERM; } - if (pai->memory.oom_killed) + if (*(tsk->proc_app_info_oom_killed)) sigterm = 0; - pai->memory.oom_killed = true; + *(tsk->proc_app_info_oom_killed) = true; } if (sigterm) @@ -168,7 +169,7 @@ int lowmem_controller_kill_candidates(GArray *candidates, break; } - tsk = &g_array_index(candidates, struct task_info, i); + tsk = g_array_index(candidates, struct task_info *, i); killer_status = lowmem_check_kill_continued(tsk, flags, lmk_start_threshold_mb); diff --git a/src/resource-limiter/memory/lowmem-governor.c b/src/resource-limiter/memory/lowmem-governor.c index d249129..aaa4b9a 100644 --- a/src/resource-limiter/memory/lowmem-governor.c +++ b/src/resource-limiter/memory/lowmem-governor.c @@ -21,228 +21,64 @@ * @desc Provides governor function to sort out candidate process to kill. */ -#include -#include +#include +#include +#include +#include -#include "lowmem.h" -#include "lowmem-governor.h" -#include "procfs.h" -#include "proc-common.h" -#include "macro.h" #include "trace.h" +#include "lowmem-governor.h" -#define BUFF_MAX 255 -#define APP_ATTR_PATH "/proc/%d/attr/current" - -static unsigned long totalram_kb; - -static int get_privilege(pid_t pid, char *name, size_t len) -{ - char path[PATH_MAX]; - char attr[BUFF_MAX]; - size_t attr_len; - FILE *fp; - - snprintf(path, sizeof(path), APP_ATTR_PATH, pid); - - fp = fopen(path, "r"); - if (!fp) - return -errno; - - attr_len = fread(attr, 1, sizeof(attr) - 1, fp); - fclose(fp); - if (attr_len <= 0) - return -ENOENT; - - attr[attr_len] = '\0'; - - snprintf(name, len, "%s", attr); - return 0; -} - -static int is_app(pid_t pid) -{ - char attr[BUFF_MAX]; - size_t len; - int ret; - - ret = get_privilege(pid, attr, sizeof(attr)); - if (ret < 0) { - _E("Failed to get privilege of PID(%d).", pid); - return -1; - } - - len = strlen(attr) + 1; - - if (!strncmp("System", attr, len)) - return 0; - - if (!strncmp("User", attr, len)) - return 0; - - if (!strncmp("System::Privileged", attr, len)) - return 0; - - return 1; -} - -static int lowmem_get_pids_proc(GArray *pids) -{ - DIR *dp; - struct dirent *dentry; - - dp = opendir("/proc"); - if (!dp) { - _E("fail to open /proc"); - return RESOURCED_ERROR_FAIL; - } - while ((dentry = readdir(dp)) != NULL) { - struct task_info tsk; - pid_t pid = 0, pgid = 0; - int oom = 0; - - if (!isdigit(dentry->d_name[0])) - continue; - - pid = (pid_t)atoi(dentry->d_name); - if (pid < 1) - /* skip invalid pids or kernel processes */ - continue; - - pgid = getpgid(pid); - if (pgid < 1) - continue; - - if(is_app(pid) != 1) - continue; - - if (proc_get_oom_score_adj(pid, &oom) < 0) { - _D("pid(%d) was already terminated", pid); - continue; - } - - /** - * Check whether this array includes applications or not. - * If it doesn't require to get applications - * and pid has been already included in pai, - * skip to append. - */ - if (oom > OOMADJ_SU && oom <= OOMADJ_APP_MAX) - continue; - - /** - * Currently, for tasks in the memory cgroup, - * do not consider multiple tasks with one pgid. - */ - tsk.pid = pid; - tsk.pgid = pgid; - tsk.oom_score_adj = oom; - tsk.oom_score_lru = oom; - tsk.pids = NULL; - tsk.size = lowmem_get_task_mem_usage_rss(&tsk); - tsk.pai = NULL; - - g_array_append_val(pids, tsk); - } - - closedir(dp); - return RESOURCED_ERROR_NONE; -} - -static int compare_victims(const struct task_info *ta, const struct task_info *tb) +static int compare_victims(const struct task_info **ta, + const struct task_info **tb, + const unsigned long *totalram_kb) { unsigned int pa, pb; assert(ta != NULL); assert(tb != NULL); + assert(*ta != NULL); + assert(*tb != NULL); /** * followed by kernel badness point calculation using heuristic. * oom_score_adj is normalized by its unit, which varies -1000 ~ 1000. */ - pa = ta->oom_score_lru * (totalram_kb / 2000) + ta->size; - pb = tb->oom_score_lru * (totalram_kb / 2000) + tb->size; + pa = (*ta)->oom_score_lru * (*totalram_kb / 2000) + (*ta)->size; + pb = (*tb)->oom_score_lru * (*totalram_kb / 2000) + (*tb)->size; return pb - pa; } -GArray *lowmem_governor_get_kill_candidates(GSList *proc_app_list, int start_oom, int end_oom, int killer_flags) +int lowmem_governor_get_kill_candidates(GArray *candidates, + GArray *task_info_app_array, + GArray *task_info_proc_array, + unsigned long totalram_kb) { - GSList *iter, *iterchild; - GArray *candidates = g_array_new(false, false, sizeof(struct task_info)); - struct proc_app_info *pai = NULL; - int proc_app_count = 0; - int oom_score_adj; - - gslist_for_each_item(iter, proc_app_list) { - struct task_info ti; - - proc_app_count++; - pai = (struct proc_app_info *)iter->data; - if (!pai->main_pid) - continue; - - oom_score_adj = pai->memory.oom_score_adj; - if (oom_score_adj > end_oom || oom_score_adj < start_oom) - continue; + if (!candidates) + return -1; - // killer_flags: enum oom_killer_cb_flags - if ((killer_flags & OOM_REVISE) && pai->memory.oom_killed) + for (int i = 0; i < task_info_app_array->len; ++i) { + struct task_info *task = &g_array_index(task_info_app_array, + struct task_info, i); + if (!task->pid) continue; - - ti.pid = pai->main_pid; - ti.pgid = getpgid(ti.pid); - ti.oom_score_adj = oom_score_adj; - ti.pai = pai; - - /** - * Before oom_score_adj of favourite (oom_score = 270) applications is - * independent of lru_state, now we consider lru_state, while - * killing favourite process. - */ - - if (oom_score_adj == OOMADJ_FAVORITE && pai->lru_state >= PROC_BACKGROUND) - ti.oom_score_lru = OOMADJ_FAVORITE + OOMADJ_FAVORITE_APP_INCREASE * pai->lru_state; - else - ti.oom_score_lru = oom_score_adj; - - if (pai->childs) { - ti.pids = g_array_new(false, false, sizeof(pid_t)); - g_array_append_val(ti.pids, ti.pid); - gslist_for_each_item(iterchild, pai->childs) { - pid_t child = GPOINTER_TO_PID(iterchild->data); - g_array_append_val(ti.pids, child); - } - } else - ti.pids = NULL; - - g_array_append_val(candidates, ti); + g_array_append_val(candidates, task); } - if (!candidates->len) { - return candidates; - } - - _D("[LMK] candidate ratio=%d/%d", candidates->len, proc_app_count); - - for (int i = 0; i < candidates->len; i++) { - struct task_info *tsk; + if (!candidates->len) + return 0; - tsk = &g_array_index(candidates, struct task_info, i); - tsk->size = lowmem_get_task_mem_usage_rss(tsk); /* KB */ + if (task_info_proc_array) { + for (int i = 0; i < task_info_proc_array->len; ++i) { + struct task_info *task = &g_array_index( + task_info_proc_array, + struct task_info, i); + g_array_append_val(candidates, task); + } } - /** - * In case of start_oom == OOMADJ_SU, - * we're going to try to kill some of processes in /proc - * to handle low memory situation. - * It can find malicious system process even though it has low oom score. - */ - if (start_oom == OOMADJ_SU) - lowmem_get_pids_proc(candidates); + g_array_sort_with_data(candidates, (GCompareDataFunc)compare_victims, + &totalram_kb); - totalram_kb = lowmem_get_ktotalram(); - g_array_sort(candidates, (GCompareFunc)compare_victims); - - return candidates; + return candidates->len; } - diff --git a/src/resource-limiter/memory/lowmem-governor.h b/src/resource-limiter/memory/lowmem-governor.h index ebd34ae..86882c0 100644 --- a/src/resource-limiter/memory/lowmem-governor.h +++ b/src/resource-limiter/memory/lowmem-governor.h @@ -19,12 +19,45 @@ /** * @file lowmem-governor.h - * @desc Functions for governor features. + * @desc Function and structure for governor feature. **/ #ifndef __LOWMEM_GOVERNOR_H__ #define __LOWMEM_GOVERNOR_H__ -GArray *lowmem_governor_get_kill_candidates(GSList *proc_app_list, int start_oom, int end_oom, int killer_flags); +#include + +struct task_info { + /** + * Mostly, there are not multiple processes with the same pgid. + * So, for the frequent case, we use pid variable to avoid + * allocating arrays. + */ + pid_t pid; + GArray *pids; + pid_t pgid; + int oom_score_adj; /* equal to /proc//oom_score_adj */ + /** + * oom_score_lru is equal to oom_score_adj + * or adjusted by proc_app_info's lru_state + * for apps that are marked as favourite. + * It can be used to compare between apps/procs for LMK. + */ + int oom_score_lru; + int size; + /** + * proc_app_info_oom_killed and proc_app_info_flags + * are not used if task is not an app. + * Especially, proc_app_info_oom_killed is NULL when this task + * is not an app + */ + bool *proc_app_info_oom_killed; + int proc_app_info_flags; +}; + +int lowmem_governor_get_kill_candidates(GArray *candidates, + GArray *task_info_app_array, + GArray *task_info_proc_array, + unsigned long totalram_kb); #endif /* __LOWMEM_GOVERNOR_H__ */ diff --git a/src/resource-limiter/memory/lowmem.c b/src/resource-limiter/memory/lowmem.c index 98dcbfe..dd12fa5 100644 --- a/src/resource-limiter/memory/lowmem.c +++ b/src/resource-limiter/memory/lowmem.c @@ -50,7 +50,6 @@ #include "lowmem-monitor.h" #include "lowmem-system.h" #include "lowmem-limit.h" -#include "lowmem-governor.h" #include "lowmem-controller.h" #include "proc-common.h" #include "procfs.h" @@ -70,6 +69,7 @@ #include "fd-handler.h" #include "resourced-helper-worker.h" #include "dedup-common.h" +#include "const.h" #define MAX_PROACTIVE_HIGH_VICTIMS 4 #define FOREGROUND_VICTIMS 1 @@ -211,6 +211,11 @@ struct lowmem_worker { static struct lowmem_worker lmw; +/* Arrays for storing kill candidates and apps/procs */ +static GArray *lowmem_kill_candidates = NULL; +static GArray *lowmem_task_info_app_array = NULL; +static GArray *lowmem_task_info_proc_array = NULL; + //static int memlog_enabled; //static int memlog_nr_max = DEFAULT_MEMLOG_NR_MAX; /* remove logfiles to reduce to this threshold. @@ -390,6 +395,9 @@ static void lowmem_free_task_info_array(GArray *array) { int i; + if (array == NULL) + return; + for (i = 0; i < array->len; i++) { struct task_info *tsk; @@ -433,6 +441,195 @@ static void lowmem_oom_popup_once(void) } } +static int get_privilege(pid_t pid, char *name, size_t len) +{ + char path[PATH_MAX]; + char attr[MAX_NAME_LENGTH]; + size_t attr_len; + FILE *fp; + + snprintf(path, sizeof(path), PROC_APP_ATTR_PATH, pid); + + fp = fopen(path, "r"); + if (!fp) + return -errno; + + attr_len = fread(attr, 1, sizeof(attr) - 1, fp); + fclose(fp); + if (attr_len <= 0) + return -ENOENT; + + attr[attr_len] = '\0'; + + snprintf(name, len, "%s", attr); + return 0; +} + +static int is_app(pid_t pid) +{ + char attr[MAX_NAME_LENGTH]; + size_t len; + int ret; + + ret = get_privilege(pid, attr, sizeof(attr)); + if (ret < 0) { + _E("Failed to get privilege of PID=%d, ret=%d.", pid, ret); + return -1; + } + + len = strlen(attr) + 1; + + if (!strncmp("System", attr, len)) + return 0; + + if (!strncmp("User", attr, len)) + return 0; + + if (!strncmp("System::Privileged", attr, len)) + return 0; + + return 1; +} + +static GArray *lowmem_get_task_info_app(int killer_flags, int start_oom, int end_oom) +{ + GSList *iter = NULL; + GSList *proc_app_list = proc_app_list_open(); + + if (!lowmem_task_info_app_array) + lowmem_task_info_app_array = g_array_new(false, false, sizeof(struct task_info)); + + gslist_for_each_item(iter, proc_app_list) { + struct proc_app_info *pai = (struct proc_app_info *)(iter->data); + struct task_info task; + + if (!pai->main_pid) + continue; + + if (pai->memory.oom_score_adj > end_oom + || pai->memory.oom_score_adj < start_oom) + continue; + + if ((killer_flags & OOM_REVISE) && pai->memory.oom_killed) { + /** + * If it is not the first attempt to kill this app and + * the app is already killed + */ + continue; + } + + task.pid = pai->main_pid; + if (pai->childs) { + task.pids = g_array_new(false, false, sizeof(pid_t)); + g_array_append_val(task.pids, task.pid); + for (GSList *iter_child = pai->childs; iter_child != NULL; iter_child = g_slist_next(iter_child)) { + pid_t child = GPOINTER_TO_PID(iter_child->data); + g_array_append_val(task.pids, child); + } + } else { + task.pids = NULL; + } + task.pgid = getpgid(task.pid); + task.oom_score_adj = pai->memory.oom_score_adj; + task.size = lowmem_get_task_mem_usage_rss(&task); /* KB */ + task.proc_app_info_oom_killed = &(pai->memory.oom_killed); + task.proc_app_info_flags = pai->flags; + + /** + * Before oom_score_adj of favourite (oom_score = 270) + * applications is independent of lru_state, now we consider + * lru_state, while killing favourite process. + */ + if (task.oom_score_adj == OOMADJ_FAVORITE + && pai->lru_state >= PROC_BACKGROUND) { + task.oom_score_lru = + OOMADJ_FAVORITE + OOMADJ_FAVORITE_APP_INCREASE + * pai->lru_state; + } else { + task.oom_score_lru = pai->memory.oom_score_adj; + } + + g_array_append_val(lowmem_task_info_app_array, task); + } + + proc_app_list_close(); + + g_array_ref(lowmem_task_info_app_array); + return lowmem_task_info_app_array; +} + +static GArray *lowmem_get_task_info_proc() +{ + DIR *dp = NULL; + struct dirent *dentry = NULL; + + dp = opendir("/proc"); + if (!dp) { + _E("fail to open /proc"); + return NULL; + } + + if (!lowmem_task_info_proc_array) + lowmem_task_info_proc_array = g_array_new(false, false, sizeof(struct task_info)); + while ((dentry = readdir(dp)) != NULL) { + struct task_info task; + pid_t pid, pgid; + int oom_score_adj = 0; + + if (!isdigit(dentry->d_name[0])) + continue; + + pid = (pid_t)atoi(dentry->d_name); + if (pid < 1) + continue; /* skip invalid pids or kernel processes */ + + pgid = getpgid(pid); + if (pgid < 1) + continue; + + if(is_app(pid) != 1) + continue; + + if (proc_get_oom_score_adj(pid, &oom_score_adj) < 0) { + _D("pid(%d) was already terminated", pid); + continue; + } + + /** + * Check whether this array includes applications or not. + * If it doesn't require to get applications + * and pid has been already included in pai, + * skip to append. + */ + if (oom_score_adj > OOMADJ_SU && oom_score_adj <= OOMADJ_APP_MAX) + continue; + + /** + * Currently, for tasks in the memory cgroup, + * do not consider multiple tasks with one pgid. + */ + task.pid = pid; + task.pids = NULL; + task.pgid = pgid; + task.oom_score_adj = oom_score_adj; + task.oom_score_lru = oom_score_adj; + task.size = lowmem_get_task_mem_usage_rss(&task); + /** + * This task is not an app, so field variables below are not + * used in this task. If not app, oom_killed is NULL. + */ + task.proc_app_info_oom_killed = NULL; + task.proc_app_info_flags = -1; + + g_array_append_val(lowmem_task_info_proc_array, task); + } + + closedir(dp); + + g_array_ref(lowmem_task_info_proc_array); + return lowmem_task_info_proc_array; +} + /** * @brief Terminate up to max_victims processes after finding them from pai. It depends on proc_app_info lists @@ -451,34 +648,53 @@ static int lowmem_kill_victims(int max_victims, int start_oom, int end_oom, unsigned should_be_freed, int flags, unsigned int *total_size, int *completed, unsigned int threshold) { - GSList *proc_app_list = NULL; unsigned int total_victim_size = 0; + int candidates_cnt = 0; int victim_cnt = 0; int status = LOWMEM_RECLAIM_NONE; - GArray *candidates = NULL; - - proc_app_list = proc_app_list_open(); + GArray *task_info_app_array = NULL; + GArray *task_info_proc_array = NULL; + + task_info_app_array = lowmem_get_task_info_app(flags, start_oom, end_oom); + /** + * If start_oom == OOMADJ_SU, processes in /proc will be + * the lowmem_kill_candidates to handle low memory situation. + * Malicious system process can be found even though it has + * low oom score. + */ + task_info_proc_array = (start_oom == OOMADJ_SU) + ? lowmem_get_task_info_proc() + : NULL; /* Get the victim candidates from lowmem governor */ - candidates = lowmem_governor_get_kill_candidates(proc_app_list, start_oom, end_oom, flags); + if (!lowmem_kill_candidates) + lowmem_kill_candidates = g_array_new(false, false, sizeof(struct task_info *)); - proc_app_list_close(); - proc_app_list = NULL; + candidates_cnt = lowmem_governor_get_kill_candidates(lowmem_kill_candidates, + task_info_app_array, + task_info_proc_array, + totalram_kb); - if (!candidates->len) { + _D("[LMK] candidates_cnt=%d", candidates_cnt); + if (candidates_cnt <= 0) { status = LOWMEM_RECLAIM_NEXT_TYPE; goto leave; } - victim_cnt = lowmem_controller_kill_candidates(candidates, + victim_cnt = lowmem_controller_kill_candidates(lowmem_kill_candidates, should_be_freed, threshold, max_victims, flags, &status, &total_victim_size, lmk_start_threshold_mb, lowmem_oom_popup_once); - leave: - lowmem_free_task_info_array(candidates); + if (lowmem_kill_candidates) { + /* Prevents the GArray to be really freed */ + g_array_ref(lowmem_kill_candidates); + g_array_free(lowmem_kill_candidates, true); + } + lowmem_free_task_info_array(task_info_app_array); + lowmem_free_task_info_array(task_info_proc_array); *total_size = total_victim_size; if(*completed != LOWMEM_RECLAIM_CONT) *completed = status; @@ -662,6 +878,13 @@ process_again: LOWMEM_WORKER_IDLE(lmw); } g_async_queue_unref(lmw->queue); + + /* Free GArrays to save kill candidates and apps/procs */ + if (lowmem_kill_candidates) + g_array_free(lowmem_kill_candidates, true); + lowmem_free_task_info_array(lowmem_task_info_app_array); + lowmem_free_task_info_array(lowmem_task_info_proc_array); + pthread_exit(NULL); } diff --git a/src/resource-limiter/memory/lowmem.h b/src/resource-limiter/memory/lowmem.h index 2dfed7e..33ec0a8 100644 --- a/src/resource-limiter/memory/lowmem.h +++ b/src/resource-limiter/memory/lowmem.h @@ -28,7 +28,9 @@ #include #include + #include "fd-handler.h" +#include "lowmem-governor.h" #ifdef __cplusplus extern "C" { @@ -36,29 +38,6 @@ extern "C" { #define MAX_MEMORY_CGROUP_VICTIMS 10 - -struct task_info { - /* - * Mostly, there are not multiple processes with the same pgid. - * So, for the frequent case, we use pid variable to avoid - * allocating arrays. - */ - pid_t pid; - GArray *pids; - pid_t pgid; - /* oom_score_adj is smae as /proc//oom_score_adj */ - int oom_score_adj; - /* - * oom_score_lru is same as oom_score_adj or adjusted by - * proc_app_info lru_state for apps that are marked as favourite. - * - * oom_score_lru is the main value used in comparison for LMK. - */ - int oom_score_lru; - int size; - struct proc_app_info *pai; -}; - unsigned int lowmem_get_task_mem_usage_rss(const struct task_info *tsk); void lowmem_trigger_swap(pid_t pid, char *path, bool move); int lowmem_trigger_reclaim(int flags, int victims, enum oom_score score, int threshold); -- 2.7.4