+++ /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)