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 <sy.kwak@samsung.com>
#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,
int ret;
char appname[PATH_MAX];
int sigterm = 0;
- struct proc_app_info *pai;
pid = tsk->pid;
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);
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)
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);
* @desc Provides governor function to sort out candidate process to kill.
*/
-#include <unistd.h>
-#include <ctype.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <assert.h>
+#include <stdbool.h>
-#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;
}
-
/**
* @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 <stdbool.h>
+
+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/<pid>/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__ */
#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"
#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
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.
{
int i;
+ if (array == NULL)
+ return;
+
for (i = 0; i < array->len; i++) {
struct task_info *tsk;
}
}
+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
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;
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);
}
#include <proc-common.h>
#include <memory-cgroup.h>
+
#include "fd-handler.h"
+#include "lowmem-governor.h"
#ifdef __cplusplus
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/<pid>/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);