--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 2012 - 2019 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 vmpressure-lowmem-handler.c
+ *
+ * @desc lowmem handler using memcgroup
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <limits.h>
+#include <vconf.h>
+#include <unistd.h>
+#include <time.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/shm.h>
+#include <sys/sysinfo.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <ctype.h>
+#include <bundle.h>
+#include <eventsystem.h>
+#include <malloc.h>
+
+#include "trace.h"
+#include "cgroup.h"
+#include "lowmem-handler.h"
+#include "proc-common.h"
+#include "procfs.h"
+#include "freezer.h"
+#include "resourced.h"
+#include "macro.h"
+#include "notifier.h"
+#include "config-parser.h"
+#include "module.h"
+#include "swap-common.h"
+#include "cgroup.h"
+#include "memory-cgroup.h"
+#include "heart-common.h"
+#include "proc-main.h"
+#include "dbus-handler.h"
+#include "util.h"
+#include "fd-handler.h"
+#include "resourced-helper-worker.h"
+#include "safe-kill.h"
+#include "dedup-common.h"
+
+#define LOWMEM_THRES_INIT 0
+
+#define MEMPS_EXEC_PATH "usr/bin/memps"
+#define MEM_CONF_FILE RD_CONFIG_FILE(limiter)
+#define MEM_SECTION "Memory"
+#define MEM_VIP_SECTION "VIP_PROCESS"
+#define MEM_VIP_PREDEFINE "PREDEFINE"
+#define MEM_POPUP_SECTION "POPUP"
+#define MEM_POPUP_STRING "oom_popup"
+#define MEM_BG_RECLAIM_SECTION "BackgroundReclaim"
+#define MEM_BG_RECLAIM_STRING "AfterScreenDim"
+#define MEM_LOGGING_SECTION "Logging"
+
+#define BUF_MAX 1024
+#define MAX_VICTIMS_BETWEEN_CHECK 3
+#define MAX_PROACTIVE_LOW_VICTIMS 2
+#define MAX_PROACTIVE_HIGH_VICTIMS 4
+#define FOREGROUND_VICTIMS 1
+#define OOM_TIMER_INTERVAL 2
+#define OOM_KILLER_PRIORITY -20
+#define THRESHOLD_MARGIN 10 /* MB */
+
+#define MEM_SIZE_64 64 /* MB */
+#define MEM_SIZE_256 256 /* MB */
+#define MEM_SIZE_448 448 /* MB */
+#define MEM_SIZE_512 512 /* MB */
+#define MEM_SIZE_768 768 /* MB */
+#define MEM_SIZE_1024 1024 /* MB */
+#define MEM_SIZE_2048 2048 /* MB */
+
+/* thresholds for 64M RAM*/
+#define PROACTIVE_64_THRES 10 /* MB */
+#define PROACTIVE_64_LEAVE 30 /* MB */
+#define CGROUP_ROOT_64_THRES_DEDUP 16 /* MB */
+#define CGROUP_ROOT_64_THRES_SWAP 15 /* MB */
+#define CGROUP_ROOT_64_THRES_LOW 8 /* MB */
+#define CGROUP_ROOT_64_THRES_MEDIUM 5 /* MB */
+#define CGROUP_ROOT_64_THRES_LEAVE 8 /* MB */
+#define CGROUP_ROOT_64_NUM_VICTIMS 1
+
+/* thresholds for 256M RAM */
+#define PROACTIVE_256_THRES 50 /* MB */
+#define PROACTIVE_256_LEAVE 80 /* MB */
+#define CGROUP_ROOT_256_THRES_DEDUP 60 /* MB */
+#define CGROUP_ROOT_256_THRES_SWAP 40 /* MB */
+#define CGROUP_ROOT_256_THRES_LOW 20 /* MB */
+#define CGROUP_ROOT_256_THRES_MEDIUM 10 /* MB */
+#define CGROUP_ROOT_256_THRES_LEAVE 20 /* MB */
+#define CGROUP_ROOT_256_NUM_VICTIMS 2
+
+/* threshold for 448M RAM */
+#define PROACTIVE_448_THRES 80 /* MB */
+#define PROACTIVE_448_LEAVE 100 /* MB */
+#define CGROUP_ROOT_448_THRES_DEDUP 120 /* MB */
+#define CGROUP_ROOT_448_THRES_SWAP 100 /* MB */
+#define CGROUP_ROOT_448_THRES_LOW 60 /* MB */
+#define CGROUP_ROOT_448_THRES_MEDIUM 50 /* MB */
+#define CGROUP_ROOT_448_THRES_LEAVE 70 /* MB */
+#define CGROUP_ROOT_448_NUM_VICTIMS 5
+
+/* threshold for 512M RAM */
+#define PROACTIVE_512_THRES 100 /* MB */
+#define PROACTIVE_512_LEAVE 80 /* MB */
+#define CGROUP_ROOT_512_THRES_DEDUP 140 /* MB */
+#define CGROUP_ROOT_512_THRES_SWAP 100 /* MB */
+#define CGROUP_ROOT_512_THRES_LOW 70 /* MB */
+#define CGROUP_ROOT_512_THRES_MEDIUM 60 /* MB */
+#define CGROUP_ROOT_512_THRES_LEAVE 80 /* MB */
+#define CGROUP_ROOT_512_NUM_VICTIMS 5
+
+/* threshold for 768 RAM */
+#define PROACTIVE_768_THRES 100 /* MB */
+#define PROACTIVE_768_LEAVE 130 /* MB */
+#define CGROUP_ROOT_768_THRES_DEDUP 180 /* MB */
+#define CGROUP_ROOT_768_THRES_SWAP 150 /* MB */
+#define CGROUP_ROOT_768_THRES_LOW 90 /* MB */
+#define CGROUP_ROOT_768_THRES_MEDIUM 80 /* MB */
+#define CGROUP_ROOT_768_THRES_LEAVE 100 /* MB */
+#define CGROUP_ROOT_768_NUM_VICTIMS 5
+
+/* threshold for more than 1024M RAM */
+#define PROACTIVE_1024_THRES 230 /* MB */
+#define PROACTIVE_1024_LEAVE 150 /* MB */
+#define CGROUP_ROOT_1024_THRES_DEDUP 400 /* MB */
+#define CGROUP_ROOT_1024_THRES_SWAP 300 /* MB */
+#define CGROUP_ROOT_1024_THRES_LOW 120 /* MB */
+#define CGROUP_ROOT_1024_THRES_MEDIUM 100 /* MB */
+#define CGROUP_ROOT_1024_THRES_LEAVE 150 /* MB */
+#define CGROUP_ROOT_1024_NUM_VICTIMS 5
+
+/* threshold for more than 2048M RAM */
+#define PROACTIVE_2048_THRES 200 /* MB */
+#define PROACTIVE_2048_LEAVE 500 /* MB */
+#define CGROUP_ROOT_2048_THRES_DEDUP 400 /* MB */
+#define CGROUP_ROOT_2048_THRES_SWAP 300 /* MB */
+#define CGROUP_ROOT_2048_THRES_LOW 200 /* MB */
+#define CGROUP_ROOT_2048_THRES_MEDIUM 160 /* MB */
+#define CGROUP_ROOT_2048_THRES_LEAVE 300 /* MB */
+#define CGROUP_ROOT_2048_NUM_VICTIMS 10
+
+/* threshold for more than 3072M RAM */
+#define PROACTIVE_3072_THRES 300 /* MB */
+#define PROACTIVE_3072_LEAVE 700 /* MB */
+#define CGROUP_ROOT_3072_THRES_DEDUP 600 /* MB */
+#define CGROUP_ROOT_3072_THRES_SWAP 500 /* MB */
+#define CGROUP_ROOT_3072_THRES_LOW 400 /* MB */
+#define CGROUP_ROOT_3072_THRES_MEDIUM 250 /* MB */
+#define CGROUP_ROOT_3072_THRES_LEAVE 500 /* MB */
+#define CGROUP_ROOT_3072_NUM_VICTIMS 10
+
+static unsigned proactive_threshold;
+static unsigned proactive_leave;
+static unsigned lmk_start_threshold;
+
+static char *event_level = MEMCG_DEFAULT_EVENT_LEVEL;
+
+/**
+ * Resourced Low Memory Killer
+ * NOTE: planned to be moved to a separate file.
+ */
+/*-------------------------------------------------*/
+#define OOM_TIMER_INTERVAL_SEC 2
+#define LMW_LOOP_WAIT_TIMEOUT_MSEC OOM_TIMER_INTERVAL_SEC*(G_USEC_PER_SEC)
+#define LMW_RETRY_WAIT_TIMEOUT_MSEC (G_USEC_PER_SEC)
+
+struct lowmem_control {
+ /*
+ * For each queued request the following properties
+ * are required with two exceptions:
+ * - status is being set by LMK
+ * - callback is optional
+ */
+ /* Processing flags*/
+ unsigned int flags;
+ /* Indictator for OOM score of targeted processes */
+ enum cgroup_type type;
+
+ /* Desired size to be restored - level to be reached (MB)*/
+ unsigned int size;
+ /* Max number of processes to be considered */
+ unsigned int count;
+ /* Memory reclaim status */
+ int status;
+ /*
+ * Optional - if set, will be triggered by LMK once the request
+ * is handled.
+ */
+ void (*callback) (struct lowmem_control *);
+};
+
+struct lowmem_worker {
+ pthread_t worker_thread;
+ GAsyncQueue *queue;
+ int active;
+ int running;
+};
+
+static struct lowmem_worker lmw;
+
+static int memlog_enabled;
+static int memlog_nr_max = DEFAULT_MEMLOG_NR_MAX;
+/* remove logfiles to reduce to this threshold.
+ * it is about five-sixths of the memlog_nr_max. */
+static int memlog_remove_batch_thres = (DEFAULT_MEMLOG_NR_MAX * 5) / 6;
+static char *memlog_path = DEFAULT_MEMLOG_PATH;
+static char *memlog_prefix[MEMLOG_MAX];
+
+#define LOWMEM_WORKER_IS_ACTIVE(_lmw) g_atomic_int_get(&(_lmw)->active)
+#define LOWMEM_WORKER_ACTIVATE(_lmw) g_atomic_int_set(&(_lmw)->active, 1)
+#define LOWMEM_WORKER_DEACTIVATE(_lmw) g_atomic_int_set(&(_lmw)->active, 0)
+
+#define LOWMEM_WORKER_IS_RUNNING(_lmw) g_atomic_int_get(&(_lmw)->running)
+#define LOWMEM_WORKER_RUN(_lmw) g_atomic_int_set(&(_lmw)->running, 1)
+#define LOWMEM_WORKER_IDLE(_lmw) g_atomic_int_set(&(_lmw)->running, 0)
+
+#define LOWMEM_NEW_REQUEST() g_slice_new0(struct lowmem_control)
+
+#define LOWMEM_DESTROY_REQUEST(_ctl) \
+ g_slice_free(typeof(*(_ctl)), _ctl); \
+
+#define LOWMEM_SET_REQUEST(c, __flags, __type, __size, __count, __cb) \
+{ \
+ (c)->flags = __flags; (c)->type = __type; \
+ (c)->size = __size; (c)->count = __count; \
+ (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)
+{
+ if (LOWMEM_WORKER_IS_ACTIVE(lmw))
+ g_async_queue_push(lmw->queue, ctl);
+}
+
+/* internal */
+static void lowmem_drain_queue(struct lowmem_worker *lmw)
+{
+ struct lowmem_control *ctl;
+
+ g_async_queue_lock(lmw->queue);
+ while ((ctl = g_async_queue_try_pop_unlocked(lmw->queue))) {
+ if (ctl->callback)
+ ctl->callback(ctl);
+ LOWMEM_DESTROY_REQUEST(ctl);
+ }
+ g_async_queue_unlock(lmw->queue);
+}
+
+static void lowmem_request_destroy(gpointer data)
+{
+ struct lowmem_control *ctl = (struct lowmem_control*) data;
+
+ if (ctl->callback)
+ ctl->callback(ctl);
+ LOWMEM_DESTROY_REQUEST(ctl);
+}
+
+/*-------------------------------------------------*/
+
+/* low memory action function for cgroup */
+static void memory_cgroup_proactive_lmk_act(enum cgroup_type type, struct memcg_info *mi);
+/* low memory action function */
+static void high_mem_act(void);
+static void swap_activate_act(void);
+static void swap_compact_act(void);
+static void lmk_act(void);
+
+
+static size_t cur_mem_state = MEM_LEVEL_HIGH;
+static int num_max_victims = MAX_MEMORY_CGROUP_VICTIMS;
+static int num_vict_between_check = MAX_VICTIMS_BETWEEN_CHECK;
+
+static unsigned long totalram;
+static unsigned long ktotalram;
+
+static struct module_ops memory_modules_ops;
+static const struct module_ops *lowmem_ops;
+static bool oom_popup_enable;
+static bool oom_popup;
+static bool memcg_swap_status;
+static bool bg_reclaim;
+static int fragmentation_size;
+
+static const char *convert_cgroup_type_to_str(int type)
+{
+ static const char *type_table[] =
+ {"/", "VIP", "High", "Medium", "Lowest"};
+ if (type >= CGROUP_ROOT && type <= CGROUP_LOW)
+ return type_table[type];
+ else
+ return "Error";
+}
+
+static const char *convert_status_to_str(int status)
+{
+ static const char *status_table[] =
+ {"none", "done", "drop", "cont", "retry", "next_type"};
+ if(status >= LOWMEM_RECLAIM_NONE && status <= LOWMEM_RECLAIM_NEXT_TYPE)
+ return status_table[status];
+ return "error status";
+}
+
+static const char *convert_memstate_to_str(int mem_state)
+{
+ static const char *state_table[] = {"mem normal", "mem dedup", "mem swap", "mem low",
+ "mem medium"};
+ if (mem_state >= 0 && mem_state < MEM_LEVEL_MAX)
+ return state_table[mem_state];
+ return "";
+}
+
+static int lowmem_launch_oompopup(void)
+{
+ GVariantBuilder *const gv_builder = g_variant_builder_new(G_VARIANT_TYPE("a{ss}"));
+ g_variant_builder_add(gv_builder, "{ss}", "_SYSPOPUP_CONTENT_", "lowmemory_oom");
+
+ GVariant *const params = g_variant_new("(a{ss})", gv_builder);
+ g_variant_builder_unref(gv_builder);
+
+ int ret = d_bus_call_method_sync_gvariant(SYSTEM_POPUP_BUS_NAME,
+ SYSTEM_POPUP_PATH_SYSTEM, SYSTEM_POPUP_IFACE_SYSTEM,
+ "PopupLaunch", params);
+
+ g_variant_unref(params);
+
+ return ret;
+}
+
+static inline void get_total_memory(void)
+{
+ struct sysinfo si;
+ if (totalram)
+ return;
+
+ if (!sysinfo(&si)) {
+ totalram = si.totalram;
+ ktotalram = BYTE_TO_KBYTE(totalram);
+ }
+}
+
+static int lowmem_mem_usage_uss(pid_t pid, unsigned int *usage)
+{
+ unsigned int uss, zram = 0;
+ int ret;
+
+ *usage = 0;
+
+ /*
+ * In lowmem we need to know memory size of processes to
+ * for terminating apps. To get most real value of usage
+ * we should use USS + ZRAM usage for selected process.
+ *
+ * Those values will contain the most approximated amount
+ * of memory that will be freed after process termination.
+ */
+ ret = proc_get_uss(pid, &uss);
+ if (ret != RESOURCED_ERROR_NONE)
+ return ret;
+
+ if (swap_get_state() == SWAP_ON) {
+ ret = proc_get_zram_usage(pid, &zram);
+ /* If we don't get zram usage, it's not a problem */
+ if (ret != RESOURCED_ERROR_NONE)
+ zram = 0;
+ }
+ *usage = uss + zram;
+ return RESOURCED_ERROR_NONE;
+}
+
+unsigned int lowmem_get_task_mem_usage_rss(const struct task_info *tsk)
+{
+ unsigned int size = 0, total_size = 0;
+ int index, ret;
+ pid_t pid;
+
+ /*
+ * If pids are allocated only when there are multiple processes with
+ * the same pgid e.g., browser and web process. Mostly, single process
+ * is used.
+ */
+ if (tsk->pids == NULL) {
+ ret = proc_get_ram_usage(tsk->pid, &size);
+
+ /* If there is no proc entry for given pid the process
+ * should be abandoned during further processing
+ */
+ if (ret < 0)
+ _D("failed to get rss memory usage of %d", tsk->pid);
+
+ return size;
+ }
+
+ for (index = 0; index < tsk->pids->len; index++) {
+ pid = g_array_index(tsk->pids, pid_t, index);
+ ret = proc_get_ram_usage(pid, &size);
+ if (ret != RESOURCED_ERROR_NONE)
+ continue;
+ total_size += size;
+ }
+
+ return total_size;
+}
+
+static int memps_file_select(const struct dirent *entry)
+{
+ return strstr(entry->d_name, "memps") ? 1 : 0;
+}
+
+static char *strrstr(const char *str, const char *token)
+{
+ int len = strlen(token);
+ const char *p = str + strlen(str);
+
+ while (str <= --p)
+ if (p[0] == token[0] && strncmp(p, token, len) == 0)
+ return (char *)p;
+
+ return NULL;
+}
+
+static int timesort(const struct dirent **a, const struct dirent **b)
+{
+ long long time1 = 0;
+ long long time2 = 0;
+ char *ptr;
+
+ ptr = strrstr((*a)->d_name, "_");
+ if (ptr && *++ptr)
+ time1 = atoll(ptr);
+
+ ptr = strrstr((*b)->d_name, "_");
+ if (ptr && *++ptr)
+ time2 = atoll(ptr);
+
+ return (time1 - time2);
+}
+
+static int clear_logs(void *data)
+{
+ struct dirent **namelist;
+ int n, i, ret;
+ char fpath[BUF_MAX];
+ char *fname;
+ char *dir = (char*)data;
+ int len;
+
+ if (!memlog_enabled)
+ return RESOURCED_ERROR_NONE;
+
+ if (!dir)
+ return RESOURCED_ERROR_NONE;
+
+ len = strlen(dir);
+ if (len <= 0 || len >= sizeof fpath - 1) {
+ _E("Invalid parameter - Directory path is too short or too long");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ n = scandir(dir, &namelist, memps_file_select, timesort);
+
+ _D("num of log files %d", n);
+ if (n <= memlog_nr_max) {
+ while (n--)
+ free(namelist[n]);
+ free(namelist);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ strncpy(fpath, dir, sizeof fpath - 1);
+ fpath[sizeof fpath - 1] = '\0';
+ fname = fpath + len;
+ *fname++ = '/';
+
+ len = sizeof fpath - len - 1;
+ for (i = 0; i < n; i++) {
+ if (i < n - memlog_remove_batch_thres) {
+ if (strlen(namelist[i]->d_name) > len - 1)
+ continue;
+ strncpy(fname, namelist[i]->d_name, len - 1);
+ fpath[sizeof fpath - 1] = '\0';
+ _D("remove log file %s", fpath);
+ ret = remove(fpath);
+ if (ret < 0)
+ _E("%s file cannot removed", fpath);
+ }
+
+ free(namelist[i]);
+ }
+ free(namelist);
+ return RESOURCED_ERROR_NONE;
+}
+
+void make_memps_log(enum mem_log memlog, pid_t pid, char *victim_name)
+{
+ time_t now;
+ struct tm cur_tm;
+ char new_log[BUF_MAX];
+ static pid_t old_pid;
+ int oom_score_adj = 0, ret;
+ char *prefix;
+
+ if (!memlog_enabled)
+ return;
+
+ if (memlog < MEMLOG_MEMPS || memlog >= MEMLOG_MAX)
+ return;
+
+ prefix = memlog_prefix[memlog];
+
+ if (old_pid == pid)
+ return;
+
+ old_pid = pid;
+
+ now = time(NULL);
+
+ if (localtime_r(&now, &cur_tm) == NULL) {
+ _E("Fail to get localtime");
+ return;
+ }
+
+ snprintf(new_log, sizeof(new_log),
+ "%s/%s_%s_%d_%.4d%.2d%.2d%.2d%.2d%.2d", memlog_path, prefix, victim_name,
+ pid, (1900 + cur_tm.tm_year), 1 + cur_tm.tm_mon,
+ cur_tm.tm_mday, cur_tm.tm_hour, cur_tm.tm_min,
+ cur_tm.tm_sec);
+
+ ret = proc_get_oom_score_adj(pid, &oom_score_adj);
+ if (ret || oom_score_adj > OOMADJ_BACKGRD_LOCKED) {
+
+ _cleanup_fclose_ FILE *f = NULL;
+
+ f = fopen(new_log, "w");
+ if (!f) {
+ _E("fail to create memps log %s", new_log);
+ return;
+ }
+ proc_print_meninfo(f);
+
+ } else {
+
+ const char *argv[4] = {"/usr/bin/memps", "-f", NULL, NULL};
+
+ argv[2] = new_log;
+ exec_cmd(ARRAY_SIZE(argv), argv);
+ }
+
+ /* best effort to limit the number of logfiles up to memlog_nr_max */
+ clear_logs(memlog_path);
+}
+
+static int lowmem_kill_victim(const struct task_info *tsk,
+ int flags, int memps_log, unsigned int *victim_size)
+{
+ pid_t pid;
+ int ret;
+ char appname[PATH_MAX];
+ int sigterm = 0;
+ struct proc_app_info *pai;
+
+ pid = tsk->pid;
+
+ if (pid <= 0 || pid == getpid())
+ return RESOURCED_ERROR_FAIL;
+
+ ret = proc_get_cmdline(pid, appname, sizeof appname);
+ if (ret == RESOURCED_ERROR_FAIL)
+ return RESOURCED_ERROR_FAIL;
+
+ if (!strcmp("memps", appname) ||
+ !strcmp("crash-worker", appname) ||
+ !strcmp("system-syspopup", appname)) {
+ _E("%s(%d) was selected, skip it", appname, pid);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!memps_log)
+ make_memps_log(MEMLOG_MEMPS, pid, appname);
+
+ pai = tsk->pai;
+ if (pai) {
+ resourced_proc_status_change(PROC_CGROUP_SET_TERMINATE_REQUEST,
+ pid, NULL, NULL, PROC_TYPE_NONE);
+
+ if (tsk->oom_score_lru <= OOMADJ_BACKGRD_LOCKED) {
+ 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;
+ }
+
+ if (pai->memory.oom_killed)
+ sigterm = 0;
+
+ pai->memory.oom_killed = true;
+ }
+
+ if (sigterm)
+ safe_kill(pid, SIGTERM);
+ else
+ safe_kill(pid, SIGKILL);
+
+ _D("[LMK] we killed, force(%d), %d (%s) score = %d, size: rss = %u, sigterm = %d\n",
+ flags & OOM_FORCE, pid, appname, tsk->oom_score_adj,
+ tsk->size, sigterm);
+ *victim_size = tsk->size;
+
+ if (tsk->oom_score_lru > OOMADJ_FOREGRD_UNLOCKED)
+ return RESOURCED_ERROR_NONE;
+
+ if (oom_popup_enable && !oom_popup) {
+ lowmem_launch_oompopup();
+ oom_popup = true;
+ }
+ if (memps_log)
+ make_memps_log(MEMLOG_MEMPS, pid, appname);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+/* return LOWMEM_RECLAIM_CONT when killing should be continued */
+static int lowmem_check_kill_continued(struct task_info *tsk, int flags)
+{
+ unsigned int available;
+
+ /*
+ * Processes with the priority higher than perceptible are killed
+ * only when the available memory is less than dynamic oom threshold.
+ */
+ if (tsk->oom_score_lru > OOMADJ_BACKGRD_PERCEPTIBLE)
+ return LOWMEM_RECLAIM_CONT;
+
+ if (flags & (OOM_FORCE|OOM_SINGLE_SHOT)) {
+ _I("[LMK] %d is dropped during force kill, flag=%d",
+ tsk->pid, flags);
+ return LOWMEM_RECLAIM_DROP;
+ }
+ available = proc_get_mem_available();
+ if (available > lmk_start_threshold) {
+ _I("[LMK] available=%d MB, larger than %u MB, do not kill foreground",
+ available, lmk_start_threshold);
+ return LOWMEM_RECLAIM_RETRY;
+ }
+ 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 * (ktotalram / 2000) + ta->size;
+ pb = tb->oom_score_lru * (ktotalram / 2000) + tb->size;
+
+ return pb - pa;
+}
+
+static void lowmem_free_task_info_array(GArray *array)
+{
+ int i;
+
+ for (i = 0; i < array->len; i++) {
+ struct task_info *tsk;
+
+ tsk = &g_array_index(array, struct task_info, i);
+ if (tsk->pids)
+ g_array_free(tsk->pids, true);
+ }
+
+ g_array_free(array, true);
+}
+
+static inline int is_dynamic_process_killer(int flags)
+{
+ return (flags & OOM_FORCE) && !(flags & OOM_NOMEMORY_CHECK);
+}
+
+static unsigned int is_memory_recovered(unsigned int *avail, unsigned int thres)
+{
+ unsigned int available = proc_get_mem_available();
+ unsigned int should_be_freed = 0;
+
+ if (available < thres)
+ should_be_freed = thres - available;
+ /*
+ * free THRESHOLD_MARGIN more than real should be freed,
+ * because launching app is consuming up the memory.
+ */
+ if (should_be_freed > 0)
+ should_be_freed += THRESHOLD_MARGIN;
+
+ *avail = available;
+
+ return should_be_freed;
+}
+
+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;
+ }
+
+ /* VIP pids should be excluded from the LMK list */
+ if (cgroup_get_type(oom) == CGROUP_VIP)
+ 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
+ and it also reference systemservice cgroup
+ because some processes in this group don't have proc_app_info.
+ *
+ * @max_victims: max number of processes to be terminated
+ * @start_oom: find victims from start oom adj score value
+ * @end_oom: find victims to end oom adj score value
+ * @should_be_freed: amount of memory to be reclaimed (in MB)
+ * @total_size[out]: total size of possibly reclaimed memory (required)
+ * @completed: final outcome (optional)
+ * @threshold: desired value of memory available
+ */
+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, 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);
+ }
+
+ proc_app_list_close();
+
+ 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);
+ }
+
+ /*
+ * 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;
+
+ if (i >= max_victims) {
+ status = LOWMEM_RECLAIM_NEXT_TYPE;
+ break;
+ }
+
+ /*
+ * Available memory is checking only every
+ * num_vict_between_check process for reducing burden.
+ */
+ if (!(i % num_vict_between_check)) {
+ if (proc_get_mem_available() > threshold) {
+ status = LOWMEM_RECLAIM_DONE;
+ break;
+ }
+ }
+
+ if (!(flags & OOM_NOMEMORY_CHECK) &&
+ total_victim_size >= should_be_freed_kb) {
+ _D("[LMK] victim=%d, max_victims=%d, total_size=%uKB",
+ victim, max_victims, total_victim_size);
+ status = LOWMEM_RECLAIM_DONE;
+ break;
+ }
+
+ tsk = &g_array_index(candidates, struct task_info, i);
+
+ status = lowmem_check_kill_continued(tsk, flags);
+ if (status != LOWMEM_RECLAIM_CONT)
+ break;
+
+ _I("[LMK] select victims from proc_app_list pid(%d) with oom_score_adj(%d)\n", tsk->pid, tsk->oom_score_adj);
+
+ ret = lowmem_kill_victim(tsk, flags, i, &victim_size);
+ if (ret != RESOURCED_ERROR_NONE)
+ continue;
+ victim++;
+ total_victim_size += victim_size;
+ }
+
+leave:
+ lowmem_free_task_info_array(candidates);
+ *total_size = total_victim_size;
+ if(*completed != LOWMEM_RECLAIM_CONT)
+ *completed = status;
+ else
+ *completed = LOWMEM_RECLAIM_NEXT_TYPE;
+ return victim;
+}
+
+static int calculate_range_of_oom(enum cgroup_type type, int *min, int *max)
+{
+ if (type == CGROUP_VIP || type >= CGROUP_END || type <= CGROUP_TOP) {
+ _E("cgroup type (%d) is out of scope", type);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ *max = cgroup_get_highest_oom_score_adj(type);
+ *min = cgroup_get_lowest_oom_score_adj(type);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static void lowmem_handle_request(struct lowmem_control *ctl)
+{
+ int start_oom, end_oom;
+ int count = 0, victim_cnt = 0;
+ int max_victim_cnt = ctl->count;
+ int status = LOWMEM_RECLAIM_NONE;
+ unsigned int available = 0;
+ unsigned int total_size = 0;
+ unsigned int current_size = 0;
+ unsigned int reclaim_size, shortfall = 0;
+ enum cgroup_type cgroup_type = ctl->type;
+
+ available = proc_get_mem_available();
+ reclaim_size = ctl->size > available
+ ? ctl->size - available : 0;
+
+ if (!reclaim_size) {
+ status = LOWMEM_RECLAIM_DONE;
+ goto done;
+ }
+
+retry:
+ /* Prepare LMK to start doing it's job. Check preconditions. */
+ if (calculate_range_of_oom(cgroup_type, &start_oom, &end_oom))
+ goto done;
+
+ lmk_start_threshold = get_root_memcg_info()->threshold[MEM_LEVEL_OOM];
+ shortfall = is_memory_recovered(&available, ctl->size);
+
+ if (!shortfall || !reclaim_size) {
+ status = LOWMEM_RECLAIM_DONE;
+ goto done;
+ }
+
+ /* precaution */
+ current_size = 0;
+ victim_cnt = lowmem_kill_victims(max_victim_cnt, start_oom, end_oom,
+ reclaim_size, ctl->flags, ¤t_size, &status, ctl->size);
+
+ if (victim_cnt) {
+ current_size = KBYTE_TO_MBYTE(current_size);
+ reclaim_size -= reclaim_size > current_size
+ ? current_size : reclaim_size;
+ total_size += current_size;
+ count += victim_cnt;
+ _I("[LMK] current: kill %d victims, reclaim_size=%uMB from %d to %d status=%s",
+ victim_cnt, current_size,
+ start_oom, end_oom, convert_status_to_str(status));
+ }
+
+ if ((status == LOWMEM_RECLAIM_DONE) ||
+ (status == LOWMEM_RECLAIM_DROP) ||
+ (status == LOWMEM_RECLAIM_RETRY))
+ goto done;
+
+ /*
+ * If it doesn't finish reclaiming memory in first operation,
+ - if flags has OOM_IN_DEPTH,
+ try to find victims again in the active cgroup.
+ otherwise, just return because there is no more victims in the desired cgroup.
+ - if flags has OOM_REVISE,
+ it means that resourced can't find victims from proc_app_list.
+ So, it should search victims or malicious process from /proc.
+ But searching /proc leads to abnormal behaviour.
+ (Make sluggish or kill same victims continuously)
+ Thus, otherwise, just return in first operation and wait some period.
+ */
+ if (cgroup_type == CGROUP_LOW) {
+ cgroup_type = CGROUP_MEDIUM;
+ goto retry;
+ } else if ((cgroup_type == CGROUP_MEDIUM) && (ctl->flags & OOM_IN_DEPTH)) {
+ cgroup_type = CGROUP_HIGH;
+ if(ctl->flags & OOM_FORCE)
+ max_victim_cnt = FOREGROUND_VICTIMS;
+ goto retry;
+ } else if ((cgroup_type == CGROUP_HIGH) && (ctl->flags & OOM_IN_DEPTH)) {
+ status = LOWMEM_RECLAIM_RETRY;
+ ctl->type = CGROUP_ROOT;
+ }
+ else if (cgroup_type == CGROUP_ROOT) {
+ status = LOWMEM_RECLAIM_RETRY;
+ }
+done:
+ _I("[LMK] Done: killed %d processes reclaimed=%uMB remaining=%uMB shortfall=%uMB status=%s",
+ count, total_size, reclaim_size, shortfall, convert_status_to_str(status));
+
+ /* After we finish reclaiming it's worth to remove oldest memps logs */
+ if (count && memlog_enabled)
+ request_helper_worker(CLEAR_LOGS, memlog_path, clear_logs, NULL);
+ ctl->status = status;
+}
+
+static void *lowmem_reclaim_worker(void *arg)
+{
+ struct lowmem_worker *lmw = (struct lowmem_worker *)arg;
+
+ setpriority(PRIO_PROCESS, 0, OOM_KILLER_PRIORITY);
+
+ g_async_queue_ref(lmw->queue);
+
+ while (1) {
+ int try_count = 0;
+ struct lowmem_control *ctl;
+
+ LOWMEM_WORKER_IDLE(lmw);
+ /* Wait on any wake-up call */
+ ctl = g_async_queue_pop(lmw->queue);
+
+ if (ctl->flags & OOM_DROP)
+ LOWMEM_DESTROY_REQUEST(ctl);
+
+ if (!LOWMEM_WORKER_IS_ACTIVE(lmw) || !ctl)
+ break;
+
+ LOWMEM_WORKER_RUN(lmw);
+process_again:
+ _D("[LMK] %d tries", ++try_count);
+ lowmem_handle_request(ctl);
+ /**
+ * Case the process failed to reclaim requested amount of memory
+ * or still under have memory pressure - try the timeout wait.
+ * There is a chance this will get woken-up in a better reality.
+ */
+ if (ctl->status == LOWMEM_RECLAIM_RETRY &&
+ !(ctl->flags & OOM_SINGLE_SHOT)) {
+ unsigned int available = proc_get_mem_available();
+
+ if (available >= ctl->size) {
+ _I("[LMK] Memory restored: requested=%uMB available=%uMB\n",
+ ctl->size, available);
+ ctl->status = LOWMEM_RECLAIM_DONE;
+ if (ctl->callback)
+ ctl->callback(ctl);
+ LOWMEM_DESTROY_REQUEST(ctl);
+ LOWMEM_WORKER_IDLE(lmw);
+ continue;
+ }
+
+ if (LOWMEM_WORKER_IS_ACTIVE(lmw)) {
+ g_usleep(LMW_RETRY_WAIT_TIMEOUT_MSEC);
+ ctl->flags |= OOM_REVISE;
+ goto process_again;
+ }
+ }
+
+ /*
+ * The ctl callback would check available size again.
+ * And it is last point in reclaiming worker.
+ * Resourced sent SIGKILL signal to victim processes
+ * so it should wait for a some seconds until each processes returns memory.
+ */
+ g_usleep(LMW_LOOP_WAIT_TIMEOUT_MSEC);
+ if (ctl->callback)
+ ctl->callback(ctl);
+
+ /* The lmk becomes the owner of all queued requests .. */
+ LOWMEM_DESTROY_REQUEST(ctl);
+ LOWMEM_WORKER_IDLE(lmw);
+ }
+ g_async_queue_unref(lmw->queue);
+ pthread_exit(NULL);
+}
+
+static void change_lowmem_state(unsigned int mem_state)
+{
+ cur_mem_state = mem_state;
+ lmk_start_threshold = get_root_memcg_info()->threshold[MEM_LEVEL_OOM];
+
+ resourced_notify(RESOURCED_NOTIFIER_MEM_LEVEL_CHANGED,
+ (void *)&cur_mem_state);
+}
+
+/* only app can call this function
+ * that is, service cannot call the function
+ */
+static void lowmem_swap_memory(char *path)
+{
+ unsigned int available;
+
+ if (cur_mem_state == MEM_LEVEL_HIGH)
+ return;
+
+ if (swap_get_state() != SWAP_ON)
+ return;
+
+ available = proc_get_mem_available();
+ if (cur_mem_state != MEM_LEVEL_LOW &&
+ available <= get_root_memcg_info()->threshold[MEM_LEVEL_LOW])
+ swap_activate_act();
+
+ resourced_notify(RESOURCED_NOTIFIER_SWAP_START, path);
+ memcg_swap_status = true;
+}
+
+void lowmem_trigger_swap(pid_t pid, char *path, bool move)
+{
+ int error;
+ int oom_score_adj;
+ int lowest_oom_score_adj;
+
+ if (!path) {
+ _E("[SWAP] Unknown memory cgroup path to swap");
+ return;
+ }
+
+ /* In this case, corresponding process will be moved to memory CGROUP_LOW.
+ */
+ if (move) {
+ error = proc_get_oom_score_adj(pid, &oom_score_adj);
+ if (error) {
+ _E("[SWAP] Cannot get oom_score_adj of pid (%d)", pid);
+ return;
+ }
+
+ lowest_oom_score_adj = cgroup_get_lowest_oom_score_adj(CGROUP_LOW);
+
+ if (oom_score_adj < lowest_oom_score_adj) {
+ oom_score_adj = lowest_oom_score_adj;
+ /* End of this funciton, 'lowmem_swap_memory()' funciton will be called */
+ proc_set_oom_score_adj(pid, oom_score_adj, find_app_info(pid));
+ return;
+ }
+ }
+
+ /* Correponding process is already managed per app or service.
+ * In addition, if some process is already located in the CGROUP_LOW, then just do swap
+ */
+ resourced_notify(RESOURCED_NOTIFIER_SWAP_START, path);
+}
+
+static void memory_level_send_system_event(int lv)
+{
+ bundle *b;
+ const char *str;
+
+ switch (lv) {
+ case MEM_LEVEL_HIGH:
+ case MEM_LEVEL_MEDIUM:
+ case MEM_LEVEL_LOW:
+ str = EVT_VAL_MEMORY_NORMAL;
+ break;
+ case MEM_LEVEL_CRITICAL:
+ str = EVT_VAL_MEMORY_SOFT_WARNING;
+ break;
+ case MEM_LEVEL_OOM:
+ str = EVT_VAL_MEMORY_HARD_WARNING;
+ break;
+ default:
+ _E("Invalid state");
+ return;
+ }
+
+ b = bundle_create();
+ if (!b) {
+ _E("Failed to create bundle");
+ return;
+ }
+
+ bundle_add_str(b, EVT_KEY_LOW_MEMORY, str);
+ eventsystem_send_system_event(SYS_EVENT_LOW_MEMORY, b);
+ bundle_free(b);
+}
+
+static void high_mem_act(void)
+{
+ int ret, status;
+
+ ret = vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status);
+ if (ret)
+ _D("vconf_get_int fail %s", VCONFKEY_SYSMAN_LOW_MEMORY);
+ if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL) {
+ vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
+ VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
+ memory_level_send_system_event(MEM_LEVEL_HIGH);
+ }
+
+ change_lowmem_state(MEM_LEVEL_HIGH);
+
+ if (swap_get_state() == SWAP_ON && memcg_swap_status) {
+ resourced_notify(RESOURCED_NOTIFIER_SWAP_UNSET_LIMIT, get_memcg_info(CGROUP_LOW));
+ memcg_swap_status = false;
+ }
+ if (proc_get_freezer_status() == CGROUP_FREEZER_PAUSED)
+ resourced_notify(RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
+ (void *)CGROUP_FREEZER_ENABLED);
+}
+
+static void swap_activate_act(void)
+{
+ int ret, status;
+
+ ret = vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status);
+ if (ret)
+ _E("vconf get failed %s", VCONFKEY_SYSMAN_LOW_MEMORY);
+
+ if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL) {
+ vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
+ VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
+ memory_level_send_system_event(MEM_LEVEL_LOW);
+ }
+ change_lowmem_state(MEM_LEVEL_LOW);
+ if (proc_get_freezer_status() == CGROUP_FREEZER_PAUSED)
+ resourced_notify(RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
+ (void *)CGROUP_FREEZER_ENABLED);
+
+ if (swap_get_state() != SWAP_ON)
+ resourced_notify(RESOURCED_NOTIFIER_SWAP_ACTIVATE, NULL);
+}
+
+static void dedup_act(enum ksm_scan_mode mode)
+{
+ int ret, status;
+ int data;
+
+ if (dedup_get_state() != DEDUP_ONE_SHOT)
+ return;
+
+ if (proc_get_freezer_status() == CGROUP_FREEZER_PAUSED)
+ resourced_notify(RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
+ (void *)CGROUP_FREEZER_ENABLED);
+
+ if (mode == KSM_SCAN_PARTIAL) {
+ ret = vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status);
+ if (ret)
+ _E("vconf get failed %s", VCONFKEY_SYSMAN_LOW_MEMORY);
+
+ if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL) {
+ vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
+ VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
+ memory_level_send_system_event(MEM_LEVEL_MEDIUM);
+ }
+ change_lowmem_state(MEM_LEVEL_MEDIUM);
+
+ data = KSM_SCAN_PARTIAL;
+ resourced_notify(RESOURCED_NOTIFIER_DEDUP_SCAN, &data);
+ } else if (mode == KSM_SCAN_FULL) {
+ data = KSM_SCAN_FULL;
+ resourced_notify(RESOURCED_NOTIFIER_DEDUP_SCAN, &data);
+ }
+}
+
+static void swap_compact_act(void)
+{
+ change_lowmem_state(MEM_LEVEL_CRITICAL);
+ resourced_notify(RESOURCED_NOTIFIER_SWAP_COMPACT, (void *)SWAP_COMPACT_MEM_LEVEL_CRITICAL);
+ memory_level_send_system_event(MEM_LEVEL_CRITICAL);
+}
+
+static void medium_cb(struct lowmem_control *ctl)
+{
+ if (ctl->status == LOWMEM_RECLAIM_DONE)
+ oom_popup = false;
+ lowmem_change_memory_state(MEM_LEVEL_HIGH, 0);
+}
+
+static void lmk_act(void)
+{
+ unsigned int available;
+ int ret;
+ int status = VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL;
+
+ /*
+ * Don't trigger reclaim worker
+ * if it is already running
+ */
+ if (LOWMEM_WORKER_IS_RUNNING(&lmw))
+ return;
+
+ ret = vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status);
+ if (ret)
+ _D("vconf_get_int fail %s", VCONFKEY_SYSMAN_LOW_MEMORY);
+
+ memory_level_send_system_event(MEM_LEVEL_OOM);
+ if (status != VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING) {
+ if (proc_get_freezer_status() == CGROUP_FREEZER_ENABLED)
+ resourced_notify(RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
+ (void *)CGROUP_FREEZER_PAUSED);
+ vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
+ VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING);
+ }
+ available = proc_get_mem_available();
+
+ change_lowmem_state(MEM_LEVEL_OOM);
+
+ if (available < get_root_memcg_info()->threshold_leave) {
+ struct lowmem_control *ctl;
+
+ ctl = LOWMEM_NEW_REQUEST();
+ if (ctl) {
+ LOWMEM_SET_REQUEST(ctl, OOM_IN_DEPTH,
+ CGROUP_LOW, get_root_memcg_info()->threshold_leave,
+ num_max_victims, medium_cb);
+ lowmem_queue_request(&lmw, ctl);
+ }
+ }
+
+ resourced_notify(RESOURCED_NOTIFIER_SWAP_COMPACT, (void *)SWAP_COMPACT_MEM_LEVEL_OOM);
+
+ /*
+ * Flush resourced memory such as other processes.
+ * Resourced can use both many fast bins and sqlite3 cache memery.
+ */
+ malloc_trim(0);
+
+ return;
+}
+
+static void lowmem_trigger_memory_state_action(int mem_state)
+{
+ /*
+ * Check if the state we want to set is different from current
+ * But it should except this condition if mem_state is already medium.
+ * Otherwise, recalim worker couldn't run any more.
+ */
+ if (mem_state != MEM_LEVEL_OOM && cur_mem_state == mem_state)
+ return;
+
+ switch (mem_state) {
+ case MEM_LEVEL_HIGH:
+ high_mem_act();
+ break;
+ case MEM_LEVEL_MEDIUM:
+ dedup_act(KSM_SCAN_PARTIAL);
+ break;
+ case MEM_LEVEL_LOW:
+ swap_activate_act();
+ break;
+ case MEM_LEVEL_CRITICAL:
+ dedup_act(KSM_SCAN_FULL);
+ swap_compact_act();
+ break;
+ case MEM_LEVEL_OOM:
+ lmk_act();
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void lowmem_dump_cgroup_procs(struct memcg_info *mi)
+{
+ int i;
+ unsigned int size;
+ pid_t pid;
+ GArray *pids_array = NULL;
+
+ cgroup_get_pids(mi->name, &pids_array);
+
+ for (i = 0; i < pids_array->len; i++) {
+ pid = g_array_index(pids_array, pid_t, i);
+ lowmem_mem_usage_uss(pid, &size);
+ _I("pid = %d, size = %u KB", pid, size);
+ }
+ g_array_free(pids_array, true);
+}
+
+static void memory_cgroup_proactive_lmk_act(enum cgroup_type type, struct memcg_info *mi)
+{
+ struct lowmem_control *ctl;
+
+ /* To Do: only start to kill fg victim when no pending fg victim */
+ lowmem_dump_cgroup_procs(mi);
+
+ ctl = LOWMEM_NEW_REQUEST();
+ if (ctl) {
+ LOWMEM_SET_REQUEST(ctl, OOM_SINGLE_SHOT | OOM_IN_DEPTH, type,
+ mi->oomleave, num_max_victims, NULL);
+ lowmem_queue_request(&lmw, ctl);
+ }
+}
+
+static unsigned int check_mem_state(unsigned int available)
+{
+ int mem_state;
+ for (mem_state = MEM_LEVEL_MAX - 1; mem_state > MEM_LEVEL_HIGH; mem_state--) {
+ if (mem_state != MEM_LEVEL_OOM && available <= get_root_memcg_info()->threshold[mem_state])
+ break;
+ else if (mem_state == MEM_LEVEL_OOM && available <= lmk_start_threshold)
+ break;
+ }
+
+ return mem_state;
+}
+
+/*static int load_bg_reclaim_config(struct parse_result *result, void *user_data)
+{
+ if (!result)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (strncmp(result->section, MEM_BG_RECLAIM_SECTION, strlen(MEM_BG_RECLAIM_SECTION)+1))
+ return RESOURCED_ERROR_NONE;
+
+ if (!strncmp(result->name, MEM_BG_RECLAIM_STRING, strlen(MEM_BG_RECLAIM_STRING)+1)) {
+ if (!strncmp(result->value, "yes", strlen("yes")+1))
+ bg_reclaim = true;
+ else if (!strncmp(result->value, "no", strlen("no")+1))
+ bg_reclaim = false;
+ }
+
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int load_popup_config(struct parse_result *result, void *user_data)
+{
+ if (!result)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (strncmp(result->section, MEM_POPUP_SECTION, strlen(MEM_POPUP_SECTION)+1))
+ return RESOURCED_ERROR_NONE;
+
+ if (!strncmp(result->name, MEM_POPUP_STRING, strlen(MEM_POPUP_STRING)+1)) {
+ if (!strncmp(result->value, "yes", strlen("yes")+1))
+ oom_popup_enable = true;
+ else if (!strncmp(result->value, "no", strlen("no")+1))
+ oom_popup_enable = false;
+ }
+
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int load_mem_log_config(struct parse_result *result, void *user_data)
+{
+ if (!result)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (strncmp(result->section, MEM_LOGGING_SECTION, strlen(MEM_LOGGING_SECTION)+1))
+ return RESOURCED_ERROR_NONE;
+
+ if (!strncmp(result->name, "Enable", strlen("Enable")+1)) {
+ memlog_enabled = atoi(result->value);
+ } else if (!strncmp(result->name, "LogPath", strlen("LogPath")+1)) {
+ memlog_path = strdup(result->value);
+ } else if (!strncmp(result->name, "MaxNumLogfile", strlen("MaxNumLogfile")+1)) {
+ memlog_nr_max = atoi(result->value);
+ memlog_remove_batch_thres = (memlog_nr_max * 5) / 6;
+ } else if (!strncmp(result->name, "PrefixMemps", strlen("PrefixMemps")+1)) {
+ memlog_prefix[MEMLOG_MEMPS] = strdup(result->value);
+ } else if (!strncmp(result->name, "PrefixMempsMemLimit", strlen("PrefixMempsMemLimit")+1)) {
+ memlog_prefix[MEMLOG_MEMPS_MEMLIMIT] = strdup(result->value);
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int set_memory_config(struct parse_result *result, void *user_data)
+{
+ if (!result)
+ return RESOURCED_ERROR_NONE;
+
+ if (strncmp(result->section, MEM_SECTION, strlen(MEM_SECTION)+1))
+ return RESOURCED_ERROR_NONE;
+
+ if (!strncmp(result->name, "ThresholdDedup", strlen("ThresholdDedup")+1)) {
+ int value = atoi(result->value);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_MEDIUM, value);
+ } else if (!strncmp(result->name, "ThresholdSwap", strlen("ThresholdSwap")+1)) {
+ int value = atoi(result->value);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_LOW, value);
+ } else if (!strncmp(result->name, "ThresholdLow", strlen("ThresholdLow")+1)) {
+ int value = atoi(result->value);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_CRITICAL, value);
+ } else if (!strncmp(result->name, "ThresholdMedium", strlen("ThresholdMedium")+1)) {
+ int value = atoi(result->value);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_OOM, value);
+ } else if (!strncmp(result->name, "ThresholdLeave", strlen("ThresholdLeave")+1)) {
+ int value = atoi(result->value);
+ memcg_set_leave_threshold(CGROUP_ROOT, value);
+ } else if (!strncmp(result->name, "ThresholdRatioDedup", strlen("ThresholdRatioDedup")+1)) {
+ double ratio = atoi(result->value);
+ int value = (double)totalram * ratio / 100.0;
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_MEDIUM, BYTE_TO_MBYTE(value));
+ } else if (!strncmp(result->name, "ThresholdRatioSwap", strlen("ThresholdRatioSwap")+1)) {
+ double ratio = atoi(result->value);
+ int value = (double)totalram * ratio / 100.0;
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_LOW, BYTE_TO_MBYTE(value));
+ } else if (!strncmp(result->name, "ThresholdRatioLow", strlen("ThresholdRatioLow")+1)) {
+ double ratio = atoi(result->value);
+ int value = (double)totalram * ratio / 100.0;
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_CRITICAL, BYTE_TO_MBYTE(value));
+ } else if (!strncmp(result->name, "ThresholdRatioMedium", strlen("ThresholdRatioMedium")+1)) {
+ double ratio = atoi(result->value);
+ int value = (double)totalram * ratio / 100.0;
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_OOM, BYTE_TO_MBYTE(value));
+ } else if (!strncmp(result->name, "ThresholdRatioLeave", strlen("ThresholdRatioLeave")+1)) {
+ double ratio = atoi(result->value);
+ int value = (double)totalram * ratio / 100.0;
+ memcg_set_leave_threshold(CGROUP_ROOT, BYTE_TO_MBYTE(value));
+ } else if (!strncmp(result->name, "ForegroundRatio", strlen("ForegroundRatio")+1)) {
+ float ratio = atof(result->value);
+ memcg_info_set_limit(get_memcg_info(CGROUP_HIGH), ratio, totalram);
+ } else if (!strncmp(result->name, "BackgroundRatio", strlen("BackgroundRatio")+1)) {
+ float ratio = atof(result->value);
+ memcg_info_set_limit(get_memcg_info(CGROUP_MEDIUM), ratio, totalram);
+ } else if (!strncmp(result->name, "LowRatio", strlen("LowRatio")+1)) {
+ float ratio = atof(result->value);
+ memcg_info_set_limit(get_memcg_info(CGROUP_LOW), ratio, totalram);
+ } else if (!strncmp(result->name, "NumMaxVictims", strlen("NumMaxVictims")+1)) {
+ int value = atoi(result->value);
+ num_max_victims = value;
+ num_vict_between_check = value > MAX_MEMORY_CGROUP_VICTIMS/2
+ ? 3 : value > MAX_MEMORY_CGROUP_VICTIMS/4
+ ? 2 : 1;
+ } else if (!strncmp(result->name, "ProactiveThreshold", strlen("ProactiveThreshold")+1)) {
+ int value = atoi(result->value);
+ proactive_threshold = value;
+ } else if (!strncmp(result->name, "ProactiveLeave", strlen("ProactiveLeave")+1)) {
+ int value = atoi(result->value);
+ proactive_leave = value;
+ } else if (!strncmp(result->name, "EventLevel", strlen("EventLevel")+1)) {
+ if (strncmp(event_level, result->value, strlen(event_level)))
+ event_level = strdup(result->value);
+ if (!event_level)
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ } else if (!strncmp(result->name, "SWAPPINESS", strlen("SWAPPINESS")+1)) {
+ int value = atoi(result->value);
+ memcg_set_default_swappiness(value);
+ memcg_info_set_swappiness(get_memcg_info(CGROUP_ROOT), value);
+ } else if (!strncmp(result->name, "FOREGROUND_SWAPPINESS", strlen("FOREGROUND_SWAPPINESS")+1)) {
+ int value = atoi(result->value);
+ memcg_info_set_swappiness(get_memcg_info(CGROUP_HIGH), value);
+ } else if (!strncmp(result->name, "BACKGROUND_SWAPPINESS", strlen("BACKGROUND_SWAPPINESS")+1)) {
+ int value = atoi(result->value);
+ memcg_info_set_swappiness(get_memcg_info(CGROUP_MEDIUM), value);
+ } else if (!strncmp(result->name, "LOW_SWAPPINESS", strlen("LOW_SWAPPINESS")+1)) {
+ int value = atoi(result->value);
+ memcg_info_set_swappiness(get_memcg_info(CGROUP_LOW), value);
+ } else if (!strncmp(result->name, "NumFragSize", strlen("NumFragSize")+1)) {
+ fragmentation_size = atoi(result->value);
+ }
+
+ return RESOURCED_ERROR_NONE;
+}*/
+
+/* setup memcg parameters depending on total ram size. */
+static void setup_memcg_params(void)
+{
+ unsigned long long total_ramsize;
+
+ get_total_memory();
+ total_ramsize = BYTE_TO_MBYTE(totalram);
+
+ _D("Total: %llu MB", total_ramsize);
+ if (total_ramsize <= MEM_SIZE_64) {
+ /* set thresholds for ram size 64M */
+ proactive_threshold = PROACTIVE_64_THRES;
+ proactive_leave = PROACTIVE_64_LEAVE;
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_MEDIUM, CGROUP_ROOT_64_THRES_DEDUP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_LOW, CGROUP_ROOT_64_THRES_SWAP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_CRITICAL, CGROUP_ROOT_64_THRES_LOW);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_OOM, CGROUP_ROOT_64_THRES_MEDIUM);
+ memcg_set_leave_threshold(CGROUP_ROOT, CGROUP_ROOT_64_THRES_LEAVE);
+ num_max_victims = CGROUP_ROOT_64_NUM_VICTIMS;
+ } else if (total_ramsize <= MEM_SIZE_256) {
+ /* set thresholds for ram size 256M */
+ proactive_threshold = PROACTIVE_256_THRES;
+ proactive_leave = PROACTIVE_256_LEAVE;
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_MEDIUM, CGROUP_ROOT_256_THRES_DEDUP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_LOW, CGROUP_ROOT_256_THRES_SWAP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_CRITICAL, CGROUP_ROOT_256_THRES_LOW);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_OOM, CGROUP_ROOT_256_THRES_MEDIUM);
+ memcg_set_leave_threshold(CGROUP_ROOT, CGROUP_ROOT_256_THRES_LEAVE);
+ num_max_victims = CGROUP_ROOT_256_NUM_VICTIMS;
+ } else if (total_ramsize <= MEM_SIZE_448) {
+ /* set thresholds for ram size 448M */
+ proactive_threshold = PROACTIVE_448_THRES;
+ proactive_leave = PROACTIVE_448_LEAVE;
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_MEDIUM, CGROUP_ROOT_448_THRES_DEDUP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_LOW, CGROUP_ROOT_448_THRES_SWAP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_CRITICAL, CGROUP_ROOT_448_THRES_LOW);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_OOM, CGROUP_ROOT_448_THRES_MEDIUM);
+ memcg_set_leave_threshold(CGROUP_ROOT, CGROUP_ROOT_448_THRES_LEAVE);
+ num_max_victims = CGROUP_ROOT_448_NUM_VICTIMS;
+ } else if (total_ramsize <= MEM_SIZE_512) {
+ /* set thresholds for ram size 512M */
+ proactive_threshold = PROACTIVE_512_THRES;
+ proactive_leave = PROACTIVE_512_LEAVE;
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_MEDIUM, CGROUP_ROOT_512_THRES_DEDUP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_LOW, CGROUP_ROOT_512_THRES_SWAP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_CRITICAL, CGROUP_ROOT_512_THRES_LOW);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_OOM, CGROUP_ROOT_512_THRES_MEDIUM);
+ memcg_set_leave_threshold(CGROUP_ROOT, CGROUP_ROOT_512_THRES_LEAVE);
+ num_max_victims = CGROUP_ROOT_512_NUM_VICTIMS;
+ } else if (total_ramsize <= MEM_SIZE_768) {
+ /* set thresholds for ram size 512M */
+ proactive_threshold = PROACTIVE_768_THRES;
+ proactive_leave = PROACTIVE_768_LEAVE;
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_MEDIUM, CGROUP_ROOT_768_THRES_DEDUP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_LOW, CGROUP_ROOT_768_THRES_SWAP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_CRITICAL, CGROUP_ROOT_768_THRES_LOW);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_OOM, CGROUP_ROOT_768_THRES_MEDIUM);
+ memcg_set_leave_threshold(CGROUP_ROOT, CGROUP_ROOT_768_THRES_LEAVE);
+ num_max_victims = CGROUP_ROOT_768_NUM_VICTIMS;
+ } else if (total_ramsize <= MEM_SIZE_1024) {
+ /* set thresholds for ram size more than 1G */
+ proactive_threshold = PROACTIVE_1024_THRES;
+ proactive_leave = PROACTIVE_1024_LEAVE;
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_MEDIUM, CGROUP_ROOT_1024_THRES_DEDUP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_LOW, CGROUP_ROOT_1024_THRES_SWAP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_CRITICAL, CGROUP_ROOT_1024_THRES_LOW);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_OOM, CGROUP_ROOT_1024_THRES_MEDIUM);
+ memcg_set_leave_threshold(CGROUP_ROOT, CGROUP_ROOT_1024_THRES_LEAVE);
+ num_max_victims = CGROUP_ROOT_1024_NUM_VICTIMS;
+ } else if (total_ramsize <= MEM_SIZE_2048) {
+ proactive_threshold = PROACTIVE_2048_THRES;
+ proactive_leave = PROACTIVE_2048_LEAVE;
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_MEDIUM, CGROUP_ROOT_2048_THRES_DEDUP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_LOW, CGROUP_ROOT_2048_THRES_SWAP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_CRITICAL, CGROUP_ROOT_2048_THRES_LOW);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_OOM, CGROUP_ROOT_2048_THRES_MEDIUM);
+ memcg_set_leave_threshold(CGROUP_ROOT, CGROUP_ROOT_2048_THRES_LEAVE);
+ num_max_victims = CGROUP_ROOT_2048_NUM_VICTIMS;
+ } else {
+ proactive_threshold = PROACTIVE_3072_THRES;
+ proactive_leave = PROACTIVE_3072_LEAVE;
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_MEDIUM, CGROUP_ROOT_3072_THRES_DEDUP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_LOW, CGROUP_ROOT_3072_THRES_SWAP);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_CRITICAL, CGROUP_ROOT_3072_THRES_LOW);
+ memcg_set_threshold(CGROUP_ROOT, MEM_LEVEL_OOM, CGROUP_ROOT_3072_THRES_MEDIUM);
+ memcg_set_leave_threshold(CGROUP_ROOT, CGROUP_ROOT_3072_THRES_LEAVE);
+ num_max_victims = CGROUP_ROOT_3072_NUM_VICTIMS;
+ }
+
+}
+
+static void lowmem_move_memcgroup(int pid, int next_oom_score_adj, struct proc_app_info *pai)
+{
+ int cur_oom_score_adj;
+ int cur_memcg_idx;
+ struct memcg_info *mi;
+ int next_memcg_idx = cgroup_get_type(next_oom_score_adj);
+
+ if(next_memcg_idx < CGROUP_VIP || next_memcg_idx > CGROUP_LOW) {
+ _E("cgroup type (%d) should not be called", next_memcg_idx);
+ return;
+ }
+ mi = get_memcg_info(next_memcg_idx);
+
+ if (!mi) {
+ return;
+ }
+
+ if (!pai) {
+ cgroup_write_pid_fullpath(mi->name, pid);
+ return;
+ }
+
+ /* parent pid */
+ if (pai->main_pid == pid) {
+ cur_oom_score_adj = pai->memory.oom_score_adj;
+ cur_memcg_idx = cgroup_get_type(cur_oom_score_adj);
+
+ /* -1 means that this pid is not yet registered at the memory cgroup
+ * plz, reference proc_create_app_info function
+ */
+ if (cur_oom_score_adj != OOMADJ_APP_MAX + 10) {
+ /* VIP processes should not be asked to move. */
+ if (cur_memcg_idx <= CGROUP_VIP) {
+ _I("[DEBUG] pid: %d, name: %s, cur_oom_score_adj: %d", pid, pai->appid, cur_oom_score_adj);
+ _E("[DEBUG] current cgroup (%s) cannot be VIP or Root", convert_cgroup_type_to_str(cur_memcg_idx));
+ return;
+ }
+ }
+
+ _I("app (%s) memory cgroup move from %s to %s", pai->appid, convert_cgroup_type_to_str(cur_memcg_idx), convert_cgroup_type_to_str(next_memcg_idx));
+
+ if (cur_oom_score_adj == next_oom_score_adj) {
+ _D("next oom_score_adj (%d) is same with current one", next_oom_score_adj);
+ return;
+ }
+
+ proc_set_process_memory_state(pai, next_memcg_idx, mi, next_oom_score_adj);
+
+ if (!lowmem_limit_move_cgroup(pai))
+ return;
+
+ if(cur_memcg_idx == next_memcg_idx)
+ return;
+
+ cgroup_write_pid_fullpath(mi->name, pid);
+ if (next_memcg_idx == CGROUP_LOW)
+ lowmem_swap_memory(get_memcg_info(CGROUP_LOW)->name);
+ }
+ /* child pid */
+ else {
+ if (pai->memory.use_mem_limit)
+ return;
+
+ cgroup_write_pid_fullpath(mi->name, pid);
+ }
+}
+
+static int lowmem_activate_worker(void)
+{
+ int ret = RESOURCED_ERROR_NONE;
+
+ if (LOWMEM_WORKER_IS_ACTIVE(&lmw)) {
+ return ret;
+ }
+
+ lmw.queue = g_async_queue_new_full(lowmem_request_destroy);
+ if (!lmw.queue) {
+ _E("Failed to create request queue\n");
+ return RESOURCED_ERROR_FAIL;
+ }
+ LOWMEM_WORKER_ACTIVATE(&lmw);
+ ret = pthread_create(&lmw.worker_thread, NULL,
+ (void *)lowmem_reclaim_worker, (void *)&lmw);
+ if (ret) {
+ LOWMEM_WORKER_DEACTIVATE(&lmw);
+ _E("Failed to create LMK thread: %d\n", ret);
+ } else {
+ pthread_detach(lmw.worker_thread);
+ ret = RESOURCED_ERROR_NONE;
+ }
+ return ret;
+}
+
+static void lowmem_deactivate_worker(void)
+{
+ struct lowmem_control *ctl;
+
+ if (!LOWMEM_WORKER_IS_ACTIVE(&lmw))
+ return;
+
+ LOWMEM_WORKER_DEACTIVATE(&lmw);
+ lowmem_drain_queue(&lmw);
+
+ ctl = LOWMEM_NEW_REQUEST();
+ if (!ctl) {
+ _E("Critical - g_slice alloc failed - Lowmem cannot be deactivated");
+ return;
+ }
+ ctl->flags = OOM_DROP;
+ g_async_queue_push(lmw.queue, ctl);
+ g_async_queue_unref(lmw.queue);
+}
+
+static int lowmem_press_eventfd_read(int fd)
+{
+ uint64_t dummy_state;
+
+ return read(fd, &dummy_state, sizeof(dummy_state));
+}
+
+static void lowmem_press_root_cgroup_handler(void)
+{
+ static unsigned int prev_available;
+ unsigned int available;
+ int mem_state;
+
+ available = proc_get_mem_available();
+ if (prev_available == available)
+ return;
+
+ mem_state = check_mem_state(available);
+ lowmem_trigger_memory_state_action(mem_state);
+
+ prev_available = available;
+}
+
+static void lowmem_press_cgroup_handler(enum cgroup_type type, struct memcg_info *mi)
+{
+ unsigned int usage, threshold;
+ int ret;
+
+ ret = memcg_get_anon_usage(mi->name, &usage);
+ if (ret) {
+ _D("getting anonymous memory usage fails");
+ return;
+ }
+
+ threshold = mi->threshold[MEM_LEVEL_OOM];
+ if (usage >= threshold)
+ memory_cgroup_proactive_lmk_act(type, mi);
+ else
+ _I("anon page %u MB < medium threshold %u MB", BYTE_TO_MBYTE(usage),
+ BYTE_TO_MBYTE(threshold));
+}
+
+static bool lowmem_press_eventfd_handler(int fd, void *data)
+{
+ struct memcg_info *mi;
+ enum cgroup_type type = CGROUP_ROOT;
+
+ // FIXME: probably shouldn't get ignored
+ if (lowmem_press_eventfd_read(fd) < 0)
+ _E("Failed to read lowmem press event, %m\n");
+
+ for (type = CGROUP_ROOT; type < CGROUP_END; type++) {
+ if (!get_cgroup_tree(type) || !get_memcg_info(type))
+ continue;
+ mi = get_memcg_info(type);
+ if (fd == mi->evfd) {
+ /* call low memory handler for this memcg */
+ if (type == CGROUP_ROOT)
+ lowmem_press_root_cgroup_handler();
+ else {
+ lowmem_press_cgroup_handler(type, mi);
+ }
+ return true;
+ }
+ }
+
+ return true;
+}
+
+static int lowmem_press_register_eventfd(struct memcg_info *mi)
+{
+ int evfd;
+ const char *name = mi->name;
+ static fd_handler_h handler;
+
+ if (mi->threshold[MEM_LEVEL_OOM] == LOWMEM_THRES_INIT)
+ return 0;
+
+ evfd = memcg_set_eventfd(name, MEMCG_EVENTFD_MEMORY_PRESSURE,
+ event_level);
+
+ if (evfd < 0) {
+ int saved_errno = errno;
+ _E("fail to register event press fd %s cgroup", name);
+ return -saved_errno;
+ }
+
+ mi->evfd = evfd;
+
+ _I("register event fd success for %s cgroup", name);
+ add_fd_read_handler(evfd, lowmem_press_eventfd_handler, NULL, NULL, &handler);
+ return 0;
+}
+
+static int lowmem_press_setup_eventfd(void)
+{
+ unsigned int i;
+
+ for (i = CGROUP_ROOT; i < CGROUP_END; i++) {
+ if (!get_use_hierarchy(i))
+ continue;
+
+ lowmem_press_register_eventfd(get_memcg_info(i));
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static void lowmem_force_reclaim_cb(struct lowmem_control *ctl)
+{
+ lowmem_change_memory_state(MEM_LEVEL_HIGH, 0);
+}
+
+int lowmem_trigger_reclaim(int flags, int victims, enum cgroup_type type, int threshold)
+{
+ struct lowmem_control *ctl = LOWMEM_NEW_REQUEST();
+
+ if (!ctl)
+ return -ENOMEM;
+
+ flags |= OOM_FORCE | OOM_IN_DEPTH | OOM_SINGLE_SHOT;
+ victims = victims > 0 ? victims : MAX_MEMORY_CGROUP_VICTIMS;
+ type = type > 0 ? type : CGROUP_LOW;
+ threshold = threshold > 0 ? threshold : get_root_memcg_info()->threshold_leave;
+
+ lowmem_change_memory_state(MEM_LEVEL_CRITICAL, 1);
+ LOWMEM_SET_REQUEST(ctl, flags,
+ type, threshold, victims,
+ lowmem_force_reclaim_cb);
+ lowmem_queue_request(&lmw, ctl);
+
+ return 0;
+}
+
+void lowmem_trigger_swap_reclaim(enum cgroup_type type, int swap_size)
+{
+ int size, victims;
+
+ victims = num_max_victims > MAX_PROACTIVE_HIGH_VICTIMS
+ ? MAX_PROACTIVE_HIGH_VICTIMS : num_max_victims;
+
+ size = get_root_memcg_info()->threshold_leave + BYTE_TO_MBYTE(swap_size);
+ _I("reclaim from swap module, type : %d, size : %d, victims: %d", type, size, victims);
+ lowmem_trigger_reclaim(0, victims, type, size);
+}
+
+bool lowmem_fragmentated(void)
+{
+ struct buddyinfo bi;
+ int ret;
+
+ ret = proc_get_buddyinfo("Normal", &bi);
+ if (ret < 0)
+ return false;
+
+ /*
+ * The fragmentation_size is the minimum count of order-2 pages in "Normal" zone.
+ * If total buddy pages is smaller than fragmentation_size,
+ * resourced will detect kernel memory is fragmented.
+ * Default value is zero in low memory device.
+ */
+ if (bi.page[PAGE_32K] + (bi.page[PAGE_64K] << 1) + (bi.page[PAGE_128K] << 2) +
+ (bi.page[PAGE_256K] << 3) < fragmentation_size) {
+ _I("fragmentation detected, need to execute proactive oom killer");
+ return true;
+ }
+ return false;
+}
+
+static void lowmem_proactive_oom_killer(int flags, char *appid)
+{
+ unsigned int before;
+ int victims;
+
+ before = proc_get_mem_available();
+
+ /* If memory state is medium or normal, just return and kill in oom killer */
+ if (before < get_root_memcg_info()->threshold[MEM_LEVEL_OOM] || before > proactive_leave)
+ return;
+
+ victims = num_max_victims > MAX_PROACTIVE_HIGH_VICTIMS
+ ? MAX_PROACTIVE_HIGH_VICTIMS : num_max_victims;
+
+#ifdef HEART_SUPPORT
+ /*
+ * This branch is used only when HEART module is compiled in and
+ * it's MEMORY module must be enabled. Otherwise this is skipped.
+ */
+ struct heart_memory_data *md = heart_memory_get_memdata(appid, DATA_LATEST);
+ if (md) {
+ unsigned int rss, after, size;
+
+ rss = KBYTE_TO_MBYTE(md->avg_rss);
+
+ free(md);
+
+ after = before - rss;
+ /*
+ * after launching app, ensure that available memory is
+ * above threshold_leave
+ */
+ if (after >= get_root_memcg_info()->threshold[MEM_LEVEL_OOM])
+ return;
+
+ if (proactive_threshold - rss >= get_root_memcg_info()->threshold[MEM_LEVEL_OOM])
+ size = proactive_threshold;
+ else
+ size = rss + get_root_memcg_info()->threshold[MEM_LEVEL_OOM] + THRESHOLD_MARGIN;
+
+ _D("history based proactive LMK : avg rss %u, available %u required = %u MB",
+ rss, before, size);
+ lowmem_trigger_reclaim(0, victims, CGROUP_LOW, size);
+
+ return;
+ }
+#endif
+
+ /*
+ * When there is no history data for the launching app,
+ * it is necessary to check current fragmentation state or application manifest file.
+ * So, resourced feels proactive LMK is required, run oom killer based on dynamic
+ * threshold.
+ */
+ if (lowmem_fragmentated())
+ goto reclaim;
+
+ /*
+ * run proactive oom killer only when available is larger than
+ * dynamic process threshold
+ */
+ if (!proactive_threshold || before >= proactive_threshold)
+ return;
+
+ if (!(flags & PROC_LARGEMEMORY))
+ return;
+
+reclaim:
+ /*
+ * free THRESHOLD_MARGIN more than real should be freed,
+ * because launching app is consuming up the memory.
+ */
+ _D("Run threshold based proactive LMK: memory level to reach: %u\n",
+ proactive_leave + THRESHOLD_MARGIN);
+ lowmem_trigger_reclaim(0, victims, CGROUP_LOW, proactive_leave + THRESHOLD_MARGIN);
+}
+
+unsigned int lowmem_get_proactive_thres(void)
+{
+ return proactive_threshold;
+}
+
+static int lowmem_prelaunch_handler(void *data)
+{
+ struct proc_status *ps = (struct proc_status *)data;
+ struct proc_app_info *pai = ps->pai;
+
+ if (!pai || CHECK_BIT(pai->flags, PROC_SERVICEAPP))
+ return RESOURCED_ERROR_NONE;
+
+ lowmem_proactive_oom_killer(ps->pai->flags, ps->pai->appid);
+ return RESOURCED_ERROR_NONE;
+}
+
+int lowmem_control_handler(void *data)
+{
+ struct lowmem_control_data *lowmem_data;
+
+ lowmem_data = (struct lowmem_control_data *)data;
+ switch (lowmem_data->control_type) {
+ case LOWMEM_MOVE_CGROUP:
+ lowmem_move_memcgroup((pid_t)lowmem_data->pid,
+ lowmem_data->oom_score_adj, lowmem_data->pai);
+ break;
+ default:
+ break;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int lowmem_bg_reclaim_handler(void *data)
+{
+ if (swap_get_state() != SWAP_ON)
+ return RESOURCED_ERROR_NONE;
+
+ if (!bg_reclaim)
+ return RESOURCED_ERROR_NONE;
+
+ /*
+ * Proactively reclaiming memory used by long-lived background processes
+ * (such as widget instances) may be efficient on devices with limited
+ * memory constraints. The pages used by such processes could be reclaimed
+ * (if swap is enabled) earlier than they used to while minimizing the
+ * impact on the user experience.
+ */
+ resourced_notify(RESOURCED_NOTIFIER_SWAP_START, get_memcg_info(CGROUP_MEDIUM)->name);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static void load_configs(const char *path)
+{
+/* if (config_parse(path, set_memory_config, NULL))
+ _E("(%s-mem) parse Fail", path);
+
+ if (config_parse(path, load_popup_config, NULL))
+ _E("(%s-popup) parse Fail", path);
+
+ if (config_parse(path, load_bg_reclaim_config, NULL))
+ _E("(%s-bg-reclaim) parse Fail", path);
+
+ if (config_parse(path, load_mem_log_config, NULL))
+ _E("(%s-mem-log) parse Fail", path);*/
+
+ free_memcg_conf();
+}
+
+static void print_mem_configs(void)
+{
+ /* print info of Memory section */
+ for (int mem_lvl = 0; mem_lvl < MEM_LEVEL_MAX; mem_lvl++)
+ _I("set threshold for state '%s' to %u MB",
+ convert_memstate_to_str(mem_lvl), get_root_memcg_info()->threshold[mem_lvl]);
+
+ _I("set number of max victims as %d", num_max_victims);
+ _I("set threshold leave to %u MB", get_root_memcg_info()->threshold_leave);
+ _I("set proactive threshold to %u MB", proactive_threshold);
+ _I("set proactive low memory killer leave to %u MB", proactive_leave);
+
+ /* print info of POPUP section */
+ _I("oom popup is %s", oom_popup_enable == true ? "enabled" : "disabled");
+
+ /* print info of BackgroundReclaim section */
+ _I("Background reclaim is %s", bg_reclaim == true ? "enabled" : "disabled");
+
+ /* print info of Logging section */
+ _I("memory logging is %s", memlog_enabled == 1 ? "enabled" : "disabled");
+ _I("memory logging path is %s", memlog_path);
+ _I("the max number of memory logging is %d", memlog_nr_max);
+ _I("the batch threshold of memory log is %d", memlog_remove_batch_thres);
+ _I("prefix of memps is %s", memlog_prefix[MEMLOG_MEMPS]);
+ _I("prefix of memlimit memps is %s", memlog_prefix[MEMLOG_MEMPS_MEMLIMIT]);
+}
+
+/* To Do: should we need lowmem_fd_start, lowmem_fd_stop ?? */
+static int lowmem_init(void)
+{
+ int ret = RESOURCED_ERROR_NONE;
+
+ _D("resourced memory init start");
+
+ /* init memcg */
+ ret = cgroup_make_full_subdir(MEMCG_PATH);
+ ret_value_msg_if(ret < 0, ret, "memory cgroup init failed\n");
+ memcg_params_init();
+
+ setup_memcg_params();
+
+ /* default configuration */
+ load_configs(MEM_CONF_FILE);
+
+ /* this function should be called after parsing configurations */
+ memcg_write_params();
+ print_mem_configs();
+
+ /* make a worker thread called low memory killer */
+ ret = lowmem_activate_worker();
+ if (ret) {
+ _E("oom thread create failed\n");
+ return ret;
+ }
+
+ /* register threshold and event fd */
+ ret = lowmem_press_setup_eventfd();
+ if (ret) {
+ _E("eventfd setup failed");
+ return ret;
+ }
+
+ lowmem_dbus_init();
+ lowmem_limit_init();
+ lowmem_system_init();
+
+ register_notifier(RESOURCED_NOTIFIER_APP_PRELAUNCH, lowmem_prelaunch_handler);
+ register_notifier(RESOURCED_NOTIFIER_MEM_CONTROL, lowmem_control_handler);
+ register_notifier(RESOURCED_NOTIFIER_LCD_OFF, lowmem_bg_reclaim_handler);
+
+ return ret;
+}
+
+static int lowmem_exit(void)
+{
+ if (strncmp(event_level, MEMCG_DEFAULT_EVENT_LEVEL, sizeof(MEMCG_DEFAULT_EVENT_LEVEL)))
+ free(event_level);
+
+ lowmem_deactivate_worker();
+ lowmem_limit_exit();
+ lowmem_system_exit();
+
+ unregister_notifier(RESOURCED_NOTIFIER_APP_PRELAUNCH, lowmem_prelaunch_handler);
+ unregister_notifier(RESOURCED_NOTIFIER_MEM_CONTROL, lowmem_control_handler);
+ unregister_notifier(RESOURCED_NOTIFIER_LCD_OFF, lowmem_bg_reclaim_handler);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_memory_init(void *data)
+{
+ lowmem_ops = &memory_modules_ops;
+ return lowmem_init();
+}
+
+static int resourced_memory_finalize(void *data)
+{
+ return lowmem_exit();
+}
+
+void lowmem_change_memory_state(int state, int force)
+{
+ int mem_state;
+
+ if (force) {
+ mem_state = state;
+ } else {
+ unsigned int available = proc_get_mem_available();
+ mem_state = check_mem_state(available);
+ }
+
+ lowmem_trigger_memory_state_action(mem_state);
+}
+
+unsigned long lowmem_get_ktotalram(void)
+{
+ return ktotalram;
+}
+
+unsigned long lowmem_get_totalram(void)
+{
+ return totalram;
+}
+
+void lowmem_restore_memcg(struct proc_app_info *pai)
+{
+ char *cgpath;
+ int index, ret;
+ struct cgroup *cgroup = NULL;
+ struct memcg_info *mi = NULL;
+ pid_t pid = pai->main_pid;
+
+ ret = cgroup_pid_get_path("memory", pid, &cgpath);
+ if (ret < 0)
+ return;
+
+ for (index = CGROUP_END-1; index >= CGROUP_ROOT; index--) {
+ cgroup = get_cgroup_tree(index);
+ if (!cgroup)
+ continue;
+
+ mi = cgroup->memcg_info;
+ if (!mi)
+ continue;
+
+ if (!strcmp(cgroup->hashname, ""))
+ continue;
+ if (strstr(cgpath, cgroup->hashname))
+ break;
+ }
+ pai->memory.memcg_idx = index;
+ pai->memory.memcg_info = mi;
+ if(strstr(cgpath, pai->appid))
+ pai->memory.use_mem_limit = true;
+
+ free(cgpath);
+}
+
+static struct module_ops memory_modules_ops = {
+ .priority = MODULE_PRIORITY_HIGH,
+ .name = "lowmem",
+ .init = resourced_memory_init,
+ .exit = resourced_memory_finalize,
+};
+
+MODULE_REGISTER(&memory_modules_ops)