--- /dev/null
+/**
+ * resourced
+ *
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * 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.
+ */
+
+/**
+ * @file lowmem-governor.c
+ * @desc Provides governor function to sort out candidate process to kill.
+ */
+
+#include <unistd.h>
+#include <ctype.h>
+
+#include "lowmem.h"
+#include "lowmem-governor.h"
+#include "procfs.h"
+#include "proc-common.h"
+#include "macro.h"
+#include "trace.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)
+{
+ unsigned int pa, pb;
+
+ 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;
+
+ return pb - pa;
+}
+
+GArray *lowmem_governor_get_kill_candidates(GSList *proc_app_list, int start_oom, int end_oom, int killer_flags)
+{
+ 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;
+
+ // killer_flags: enum oom_killer_cb_flags
+ if ((killer_flags & OOM_REVISE) && pai->memory.oom_killed)
+ 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);
+ }
+
+ 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;
+
+ tsk = &g_array_index(candidates, struct task_info, i);
+ tsk->size = lowmem_get_task_mem_usage_rss(tsk); /* KB */
+ }
+
+ /**
+ * 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);
+
+ totalram_kb = lowmem_get_ktotalram();
+ g_array_sort(candidates, (GCompareFunc)compare_victims);
+
+ return candidates;
+}
+
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+/**
+ * @file lowmem-governor.h
+ * @desc Functions for governor features.
+ **/
+
+#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);
+
+#endif /* __LOWMEM_GOVERNOR_H__ */
#include "lowmem-dbus.h"
#include "lowmem-system.h"
#include "lowmem-limit.h"
+#include "lowmem-governor.h"
#include "proc-common.h"
#include "procfs.h"
#include "freezer.h"
(c)->callback = __cb; \
}
-#define BUFF_MAX 255
-#define APP_ATTR_PATH "/proc/%d/attr/current"
-
-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 void lowmem_queue_request(struct lowmem_worker *lmw,
struct lowmem_control *ctl)
{
return LOWMEM_RECLAIM_CONT;
}
-static int compare_victims(const struct task_info *ta, const struct task_info *tb)
-{
- unsigned int pa, pb;
-
- 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;
-
- return pb - pa;
-}
-
static void lowmem_free_task_info_array(GArray *array)
{
int i;
return should_be_freed_mb;
}
-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;
-}
-
/**
* @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)
{
- int total_count = 0;
GSList *proc_app_list = NULL;
int i, ret, victim = 0;
unsigned int victim_size = 0;
unsigned int total_victim_size = 0;
int status = LOWMEM_RECLAIM_NONE;
GArray *candidates = NULL;
- GSList *iter, *iterchild;
- struct proc_app_info *pai = NULL;
- int oom_score_adj;
int should_be_freed_kb = MBYTE_TO_KBYTE(should_be_freed);
- candidates = g_array_new(false, false, sizeof(struct task_info));
-
proc_app_list = proc_app_list_open();
- gslist_for_each_item(iter, proc_app_list) {
- struct task_info ti;
- total_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 ((flags & OOM_REVISE) && pai->memory.oom_killed)
- 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);
- }
+ /* Get the victim candidates from lowmem governor */
+ candidates = lowmem_governor_get_kill_candidates(proc_app_list, start_oom, end_oom, flags);
proc_app_list_close();
+ proc_app_list = NULL;
if (!candidates->len) {
status = LOWMEM_RECLAIM_NEXT_TYPE;
goto leave;
}
- else {
- _D("[LMK] candidate ratio=%d/%d", candidates->len, total_count);
- }
-
- for (i = 0; i < candidates->len; i++) {
- struct task_info *tsk;
-
- tsk = &g_array_index(candidates, struct task_info, i);
- tsk->size = lowmem_get_task_mem_usage_rss(tsk); /* KB */
- }
-
- /*
- * 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(candidates, (GCompareFunc)compare_victims);
for (i = 0; i < candidates->len; i++) {
struct task_info *tsk;