--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 2020 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 dedup.c
+ * @desc dedup process
+ */
+#include <trace.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <pthread.h>
+#include <sys/sysinfo.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <memory-common.h>
+#include <linux/loop.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+
+#include "macro.h"
+#include "util.h"
+#include "module.h"
+#include "module-data.h"
+#include "dedup-common.h"
+#include "config-parser.h"
+#include "lowmem-handler.h"
+#include "notifier.h"
+#include "const.h"
+#include "file-helper.h"
+
+#define DEDUP_PRIORITY 20
+#define DEDUP_ON_BOOT_TIME 60
+#define DEDUP_FULL_SCAN_INTERVAL 60
+#define DEDUP_INIT_SCAN_INTERVAL 300
+#define DEDUP_STAT_INTERVAL 60
+
+enum dedup_thread_op {
+ DEDUP_OP_ACTIVATE,
+ DEDUP_OP_SCANONCE,
+ DEDUP_OP_STAT,
+ DEDUP_OP_DEACTIVATE,
+ DEDUP_OP_MAX,
+};
+
+struct dedup_thread_bundle {
+ enum dedup_thread_op op;
+ enum ksm_scan_mode mode;
+};
+
+static struct module_ops dedup_module_ops;
+
+//static enum dedup_state dedup_state; /* see src/common/dedup-common.h */
+static bool dedup_activated = false;
+
+static bool dedup_enable = false;
+static bool dedup_at_boot_enable = false;
+static enum dedup_mode dedup_mode = DEDUP_MODE_NONE;
+static bool dedup_on_lowmem = false;
+
+static int dedup_at_boot_delay = 120;
+static int dedup_full_scan_interval = 600000;
+static int dedup_stat_interval = 60000;
+static int dedup_partial_scan_interval = 60000;
+
+static GSource *dedup_activating_timer = NULL;
+static GSource *dedup_scanning_timer = NULL;
+static GSource *dedup_stat_timer = NULL;
+
+static int dedup_do_scan(enum ksm_scan_mode);
+static int dedup_do_stat(void);
+
+/* dedup module parameters */
+enum dedup_param {
+ DEDUP_PARAM_ENABLE = 0,
+ DEDUP_PARAM_AT_BOOT,
+ DEDUP_PARAM_AT_BOOT_DELAY,
+ DEDUP_PARAM_ON_LOWMEM,
+ DEDUP_PARAM_STAT_INTERVAL,
+ DEDUP_PARAM_FULL_SCAN_INTERVAL,
+ DEDUP_PARAM_PARTIAL_SCAN_INTERVAL,
+ DEDUP_PARAM_MAX,
+};
+
+/* valid ranges of dedup module parameters */
+static int dedup_param_ranges[DEDUP_PARAM_MAX][2] = {
+ {-1, -1}, /* DEDUP_PARAM_ENABLE: boolean */
+ {-1, -1}, /* DEDUP_PARAM_AT_BOOT: boolean */
+ {0, 600000}, /* DEDUP_PARAM_AT_BOOT_DELAY */
+ {-1, -1}, /* DEDUP_PARAM_ON_LOWMEM: boolean */
+ {0, INT_MAX}, /* DEDUP_PARAM_STAT_INTERVAL */
+ {0, INT_MAX}, /* DEDUP_PARAM_FULL_SCAN_INTERVAL */
+ {0, INT_MAX}, /* DEDUP_PARAM_PARTIAL_SCAN_INTERVAL */
+};
+
+/* ksm param types & value ranges & pathes */
+enum ksm_param {
+ KSM_PARAM_PAGES_TO_SCAN = 0,
+ KSM_PARAM_SLEEP_MSECS,
+ KSM_PARAM_FULL_SCAN_INTERVAL,
+ KSM_PARAM_SCAN_BOOST,
+ KSM_PARAM_MAX,
+};
+
+static int ksm_param_ranges[KSM_PARAM_MAX][2] = {
+ {0, 10000}, /* KSM_PARAM_PAGES_TO_SCAN */
+ {0, 1000}, /* KSM_PARAM_SLEEP_MSECS */
+ {0, INT_MAX}, /* KSM_PARAM_FULL_SCAN_INTERVAL */
+ {100, 10000}, /* KSM_PARAM_SCAN_BOOST */
+};
+static unsigned int ksm_params[KSM_PARAM_MAX];
+static const char *ksm_param_path[KSM_PARAM_MAX] = {
+ "/sys/kernel/mm/ksm/pages_to_scan",
+ "/sys/kernel/mm/ksm/sleep_millisecs",
+ "/sys/kernel/mm/ksm/full_scan_interval",
+ "/sys/kernel/mm/ksm/scan_boost",
+};
+
+/* ksm statistics */
+#define KSM_STAT_WINDOW_MAX 10
+static int ksm_stat_window;
+static unsigned int ksm_stats[KSM_STAT_MAX][KSM_STAT_WINDOW_MAX];
+static const char *ksm_stat_path[2] = {
+ "/sys/kernel/mm/ksm/pages_sharing",
+ "/sys/kernel/mm/ksm/pages_shared",
+};
+
+enum ksm_sysfs_state_val {
+ DEDUP_SYSFS_STOP = 0,
+ DEDUP_SYSFS_RUN = 1,
+ DEDUP_SYSFS_ONE_SHOT = 8,
+};
+
+static inline int dedup_set_ksm_state(enum dedup_state state)
+{
+ int val;
+
+ if (state == DEDUP_ON)
+ val = DEDUP_SYSFS_RUN;
+ else if (state == DEDUP_ONE_SHOT)
+ val = DEDUP_SYSFS_ONE_SHOT;
+ else
+ val = DEDUP_SYSFS_STOP;
+ /* write value to /sys/kernel/mm/ksm/run */
+ return fwrite_int(DEDUP_SYSFS_KSM_RUN, val);
+}
+
+static inline int dedup_set_ksm_param(int param_num, unsigned int value)
+{
+ return fwrite_uint(ksm_param_path[param_num], value);
+}
+
+static void dedup_set_ksm_params(void)
+{
+ int i;
+ for (i = KSM_PARAM_PAGES_TO_SCAN; i < KSM_PARAM_MAX; i++)
+ dedup_set_ksm_param(i, ksm_params[i]);
+}
+
+static int dedup_check_and_scanning_once(enum ksm_scan_mode mode)
+{
+ int val;
+ fread_int(DEDUP_SYSFS_KSM_ONESHOT, &val);
+ if (val == KSM_SCAN_NONE)
+ return fwrite_int(DEDUP_SYSFS_KSM_ONESHOT, mode);
+ return 0;
+}
+
+static int dedup_scanning_once(enum ksm_scan_mode mode)
+{
+ int ret;
+ _D("Invoke scanning once to KSM (mode: %d)", mode);
+ ret = dedup_check_and_scanning_once(mode);
+
+ return ret;
+}
+
+unsigned int dedup_get_ksm_stat(enum ksm_stat stat_type)
+{
+ int last;
+ if (stat_type < KSM_STAT_PAGES_SHARING || stat_type >= KSM_STAT_MAX)
+ return 0;
+ last = (ksm_stat_window == 0) ? KSM_STAT_WINDOW_MAX : ksm_stat_window - 1;
+ return ksm_stats[stat_type][last];
+}
+
+static int dedup_record_stat(void)
+{
+ int val, ret;
+
+ ret = fread_int(ksm_stat_path[KSM_STAT_PAGES_SHARING], &val);
+ if (!ret)
+ ksm_stats[KSM_STAT_PAGES_SHARING][ksm_stat_window] = val;
+ ret = fread_int(ksm_stat_path[KSM_STAT_PAGES_SHARED], &val);
+ if (!ret)
+ ksm_stats[KSM_STAT_PAGES_SHARED][ksm_stat_window] = val;
+
+ _I("read ksm stat: pages_sharing: %d pages_shared: %d",
+ ksm_stats[KSM_STAT_PAGES_SHARING][ksm_stat_window],
+ ksm_stats[KSM_STAT_PAGES_SHARED][ksm_stat_window]);
+ ksm_stat_window = (ksm_stat_window + 1) % KSM_STAT_WINDOW_MAX;
+ return RESOURCED_ERROR_NONE;
+}
+
+static gboolean dedup_scanning_timer_cb(gpointer data)
+{
+ dedup_scanning_timer = NULL;
+ if (dedup_get_state() == DEDUP_ONE_SHOT)
+ dedup_do_scan(KSM_SCAN_FULL);
+ return false;
+}
+
+static gboolean dedup_stat_timer_cb(gpointer data)
+{
+ dedup_stat_timer = NULL;
+ dedup_do_stat();
+ return false;
+}
+
+static void dedup_reset_scanning_timer(void)
+{
+ _D("reset scan-timer %d seconds", dedup_full_scan_interval);
+ dedup_scanning_timer =
+ g_timeout_source_new_seconds(dedup_full_scan_interval);
+ g_source_set_callback(dedup_scanning_timer,
+ dedup_scanning_timer_cb, NULL, NULL);
+ g_source_attach(dedup_scanning_timer, NULL);
+}
+
+static void dedup_reset_stat_timer(void)
+{
+ _D("reset stat-timer %d seconds", dedup_stat_interval);
+ dedup_stat_timer =
+ g_timeout_source_new_seconds(dedup_stat_interval);
+ g_source_set_callback(dedup_stat_timer,
+ dedup_stat_timer_cb, NULL, NULL);
+ g_source_attach(dedup_stat_timer, NULL);
+}
+
+static void dedup_deactivate_in_module(void)
+{
+ if (dedup_activating_timer) {
+ g_source_destroy(dedup_activating_timer);
+ dedup_activating_timer = NULL;
+ }
+
+ if (dedup_scanning_timer) {
+ g_source_destroy(dedup_scanning_timer);
+ dedup_scanning_timer = NULL;
+ }
+
+ if (dedup_stat_timer) {
+ g_source_destroy(dedup_stat_timer);
+ dedup_stat_timer = NULL;
+ }
+
+ _D("stop KSM thread");
+
+ dedup_set_ksm_state(DEDUP_OFF);
+}
+
+static enum dedup_state parse_dedup_state(enum dedup_mode mode)
+{
+ if (mode == DEDUP_MODE_PERIODIC)
+ return DEDUP_ON;
+ else if (mode == DEDUP_MODE_ONESHOT)
+ return DEDUP_ONE_SHOT;
+ else
+ return DEDUP_OFF;
+}
+
+static void dedup_activate_in_module(void)
+{
+ enum dedup_state state;
+
+ if (dedup_get_state() == DEDUP_ON
+ || dedup_get_state() == DEDUP_ONE_SHOT)
+ return;
+
+ if (dedup_activated)
+ return;
+
+ /* make ksm activate */
+ state = parse_dedup_state(dedup_mode);
+ dedup_set_state(state);
+ dedup_set_ksm_params();
+ dedup_set_ksm_state(state);
+ _I("kernel deduplication thread is enabled");
+
+ if (dedup_activating_timer) {
+ g_source_destroy(dedup_activating_timer);
+ dedup_activating_timer = NULL;
+ }
+
+ if (!dedup_on_lowmem) {
+ dedup_scanning_timer = g_timeout_source_new_seconds(dedup_full_scan_interval);
+ g_source_set_callback(dedup_scanning_timer, dedup_scanning_timer_cb, NULL, NULL);
+ g_source_attach(dedup_scanning_timer, NULL);
+ }
+
+ dedup_stat_timer = g_timeout_source_new_seconds(dedup_stat_interval);
+ g_source_set_callback(dedup_stat_timer, dedup_stat_timer_cb, NULL, NULL);
+ g_source_attach(dedup_stat_timer, NULL);
+
+ dedup_activated = true;
+}
+
+/* translations for ms -> ns and s -> ns */
+#define DEDUP_ACT_STOMS 1000
+#define DEDUP_ACT_MSTONS 1000000
+
+static bool dedup_check_scan_interval
+(struct timespec *now, struct timespec *old, unsigned long interval)
+{
+ unsigned long diff;
+ diff = (now->tv_sec - old->tv_sec) * DEDUP_ACT_STOMS;
+ diff += (now->tv_nsec - old->tv_nsec) / DEDUP_ACT_MSTONS;
+ return (diff >= interval);
+}
+
+/* used in dedup_do_scan */
+static struct timespec now, partial_begin, full_begin;
+static int nr_dedup;
+
+static int dedup_do_scan(enum ksm_scan_mode scan_mode)
+{
+ enum ksm_scan_mode mode = KSM_SCAN_NONE;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ if (scan_mode == KSM_SCAN_FULL) {
+ /* if full scan is requested frequently, it can be
+ * substituted by partial scan
+ */
+ if (dedup_check_scan_interval(&now, &full_begin,
+ dedup_full_scan_interval))
+ mode = KSM_SCAN_FULL;
+ else if (dedup_on_lowmem &&
+ dedup_check_scan_interval(&now, &partial_begin,
+ dedup_partial_scan_interval))
+ mode = KSM_SCAN_PARTIAL;
+ } else if (scan_mode == KSM_SCAN_PARTIAL) {
+ if (dedup_check_scan_interval(&now, &partial_begin,
+ dedup_partial_scan_interval))
+ mode = KSM_SCAN_PARTIAL;
+ }
+
+ if (mode != KSM_SCAN_NONE) {
+ _I("dedup: %d-th %s deduplication triggering", nr_dedup++,
+ (mode == KSM_SCAN_FULL ? "FULL" : "PARTIAL"));
+ if (!dedup_on_lowmem) {
+ dedup_scanning_once(KSM_SCAN_FULL);
+ dedup_reset_scanning_timer();
+ } else
+ dedup_scanning_once(mode);
+
+ if (mode == KSM_SCAN_FULL)
+ full_begin = now;
+ else
+ partial_begin = now;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int dedup_do_stat(void)
+{
+ dedup_record_stat();
+ dedup_reset_stat_timer();
+ return RESOURCED_ERROR_NONE;
+}
+
+static int dedup_start_handler(void *data)
+{
+ if (!data)
+ return RESOURCED_ERROR_NO_DATA;
+
+ dedup_activate_in_module();
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int dedup_scan_handler(void *data)
+{
+ int *scan_mode;
+
+ if (dedup_get_state() != DEDUP_ONE_SHOT)
+ return 0;
+
+ if (dedup_scanning_timer) {
+ g_source_destroy(dedup_scanning_timer);
+ dedup_scanning_timer = NULL;
+ }
+
+ scan_mode = data;
+ return dedup_do_scan(*scan_mode);
+}
+
+static int dedup_stop_handler(void *data)
+{
+ if (!data)
+ return RESOURCED_ERROR_NO_DATA;
+
+ dedup_deactivate_in_module();
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static gboolean dedup_activate_timer_cb(gpointer data)
+{
+ dedup_activating_timer = NULL;
+ _D("dedup activating callback called");
+ dedup_activate_in_module();
+ return false;
+}
+
+static int dedup_booting_done(void *data)
+{
+ if (dedup_activated)
+ return RESOURCED_ERROR_NONE;
+
+ if (dedup_at_boot_enable) {
+ /* if dedup_at_boot_enable is disabled,
+ * other daemon should activate dedup */
+ _D("dedup booting done is called");
+ if (dedup_at_boot_delay > 0)
+ dedup_activating_timer =
+ g_timeout_source_new_seconds(dedup_at_boot_delay);
+ else
+ dedup_activating_timer =
+ g_timeout_source_new_seconds(DEDUP_ON_BOOT_TIME);
+ g_source_set_callback(dedup_activating_timer,
+ dedup_activate_timer_cb, NULL, NULL);
+ g_source_attach(dedup_activating_timer, NULL);
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int config_parse_dedup_modes(const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data)
+{
+ enum dedup_mode *mode = data;
+ char *word, *state;
+ size_t l;
+
+ if (is_empty(rvalue))
+ return 0;
+
+ *mode = 0;
+
+ FOREACH_WORD(word, l, rvalue, state) {
+ if (strneq(word, "oneshot", l))
+ *mode = DEDUP_MODE_ONESHOT;
+ else if (strneq(word, "periodic", l))
+ *mode = DEDUP_MODE_PERIODIC;
+ else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define DEDUP_SECTION "DEDUP"
+#define KSM_SECTION "KSM"
+
+static int config_parse_param(const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data)
+{
+ int val, *var = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+ assert(section);
+
+ val = atoi(rvalue);
+ if (!strncmp(section, DEDUP_SECTION, sizeof(DEDUP_SECTION))) {
+ if (val >= dedup_param_ranges[ltype][0] &&
+ val < dedup_param_ranges[ltype][1]) {
+ *var = val;
+ _I("Success to parse parameters, val: %d of %s in %s section",
+ val, lvalue, section);
+ } else
+ _E("Failed to parse parameters, ignoring: %s of %s in %s section",
+ rvalue, lvalue, section);
+ } else if (!strncmp(section, KSM_SECTION, sizeof(KSM_SECTION))) {
+ if (val >= ksm_param_ranges[ltype][0] &&
+ val < ksm_param_ranges[ltype][1]) {
+ *var = val;
+ _I("Success to parse parameters, val: %d of %s in %s section",
+ val, lvalue, section);
+ } else
+ _E("Failed to parse parameters, ignoring: %s of %s in %s section",
+ rvalue, lvalue, section);
+ } else
+ _E("Unknown section: %s", section);
+ return 0;
+}
+
+static int dedup_parse_config_file(void)
+{
+ int ret;
+ int arg_ksm_pages_to_scan = 100;
+ int arg_ksm_sleep = 20; /* 20 msecs */
+ int arg_ksm_full_scan_interval = 60000; /* 60 seconds */
+ int arg_ksm_scan_boost = 100;
+
+ const ConfigTableItem items[] = {
+ { "DEDUP", "Enable", config_parse_bool,
+ DEDUP_PARAM_ENABLE,
+ &dedup_enable },
+ { "DEDUP", "DedupAtBoot", config_parse_bool,
+ DEDUP_PARAM_AT_BOOT,
+ &dedup_at_boot_enable },
+ { "DEDUP", "DedupAtBootDelayMs", config_parse_param,
+ DEDUP_PARAM_AT_BOOT_DELAY,
+ &dedup_at_boot_delay },
+ { "DEDUP", "ScanOnLowmem", config_parse_bool,
+ DEDUP_PARAM_ON_LOWMEM,
+ &dedup_on_lowmem },
+ { "DEDUP", "StatIntervalMs", config_parse_param,
+ DEDUP_PARAM_STAT_INTERVAL,
+ &dedup_stat_interval },
+ { "DEDUP", "FullScanIntervalMs", config_parse_param,
+ DEDUP_PARAM_FULL_SCAN_INTERVAL,
+ &dedup_full_scan_interval },
+ { "DEDUP", "PartialScanIntervalMs", config_parse_param,
+ DEDUP_PARAM_PARTIAL_SCAN_INTERVAL,
+ &dedup_partial_scan_interval },
+ { "KSM", "Mode", config_parse_dedup_modes,
+ 0, &dedup_mode },
+ { "KSM", "PagesToScan", config_parse_param,
+ KSM_PARAM_PAGES_TO_SCAN,
+ &arg_ksm_pages_to_scan },
+ { "KSM", "SleepMs", config_parse_param,
+ KSM_PARAM_SLEEP_MSECS,
+ &arg_ksm_sleep },
+ { "KSM", "FullScanIntervalMs", config_parse_param,
+ KSM_PARAM_FULL_SCAN_INTERVAL,
+ &arg_ksm_full_scan_interval },
+ { "KSM", "ScanBoost", config_parse_param,
+ KSM_PARAM_SCAN_BOOST,
+ &arg_ksm_scan_boost },
+ { NULL, NULL, NULL,
+ 0, NULL }
+ };
+
+ ret = config_parse_new(DEDUP_CONF_FILE, (void*) items);
+ if (ret < 0) {
+ _E("Failed to parse configuration file: %s", strerror(-ret));
+ return ret;
+ }
+
+ ksm_params[KSM_PARAM_PAGES_TO_SCAN] = arg_ksm_pages_to_scan;
+ ksm_params[KSM_PARAM_SLEEP_MSECS] = arg_ksm_sleep;
+ ksm_params[KSM_PARAM_FULL_SCAN_INTERVAL] = arg_ksm_full_scan_interval;
+ ksm_params[KSM_PARAM_SCAN_BOOST] = arg_ksm_scan_boost;
+
+ /* since parameters are denoted in milliseconds,
+ it should be recalculated */
+ dedup_at_boot_delay /= 1000;
+ dedup_stat_interval /= 1000;
+ dedup_partial_scan_interval /= 1000;
+ dedup_full_scan_interval /= 1000;
+
+ _I("deduplication mode: %s", dedup_mode == DEDUP_MODE_PERIODIC ?
+ "kernel-managed" : "resourced-triggered");
+ _I("deduplication on boot: %s", dedup_at_boot_enable ? "true" : "false");
+ _I("scanning is invoked by %s", dedup_on_lowmem ?
+ "LOWMEM event" : "periodic timer");
+ _I("full scan interval: %d sec", dedup_full_scan_interval);
+ _I("stat monitoring interval: %d sec", dedup_stat_interval);
+
+ _I("ksm pages to scan: %d", arg_ksm_pages_to_scan);
+ _I("ksm sleep time: %d", arg_ksm_sleep);
+ _I("ksm full scan interval: %d", arg_ksm_full_scan_interval);
+ _I("ksm scan boost: %d", arg_ksm_scan_boost);
+
+ return 0;
+}
+
+static inline void init_dedup_stat(void)
+{
+ int i, j;
+ for (i = KSM_STAT_PAGES_SHARING; i < KSM_STAT_MAX; i++)
+ for (j = 0; j < KSM_STAT_WINDOW_MAX; j++)
+ ksm_stats[i][j] = 0;
+ ksm_stat_window = 0;
+}
+
+static int dedup_init(void)
+{
+ int ret;
+
+ init_dedup_stat();
+ ret = dedup_parse_config_file();
+ if (ret < 0)
+ return ret;
+
+ if (!dedup_enable)
+ return -ENODEV;
+
+ /* initialize time variables */
+ if (!clock_gettime(CLOCK_MONOTONIC, &now)) {
+ partial_begin = now;
+ full_begin = now;
+ } else {
+ _I("Time initialization failed");
+ memset(&partial_begin, 0, sizeof(struct timespec));
+ memset(&full_begin, 0, sizeof(struct timespec));
+ }
+
+ return ret;
+}
+
+static int resourced_dedup_check_runtime_support(void *data)
+{
+ /*
+ * Check whether CONFIG_LKSM is enabled in kernel.
+ */
+ if (access("/sys/kernel/mm/ksm/run", R_OK) != 0) {
+ _E("the kernel don't support KSM, please check kernel configuration");
+ return -ENOENT;
+ }
+ if (access("/sys/kernel/mm/ksm/one_shot_scanning", R_OK) != 0) {
+ _E("the kernel support KSM but not LKSM, please check kernel configuration");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static int resourced_dedup_init(void *data)
+{
+ int ret;
+
+ dedup_set_state(DEDUP_OFF);
+
+ ret = dedup_init();
+ if (ret != RESOURCED_ERROR_NONE)
+ return ret;
+ _I("resourced dedup module is initialized");
+ register_notifier(RESOURCED_NOTIFIER_DEDUP_START, dedup_start_handler);
+ register_notifier(RESOURCED_NOTIFIER_DEDUP_SCAN, dedup_scan_handler);
+ register_notifier(RESOURCED_NOTIFIER_DEDUP_STOP, dedup_stop_handler);
+ register_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, dedup_booting_done);
+
+ return ret;
+}
+
+static int resourced_dedup_finalize(void *data)
+{
+ unregister_notifier(RESOURCED_NOTIFIER_DEDUP_START, dedup_start_handler);
+ unregister_notifier(RESOURCED_NOTIFIER_DEDUP_SCAN, dedup_scan_handler);
+ unregister_notifier(RESOURCED_NOTIFIER_DEDUP_STOP, dedup_stop_handler);
+ unregister_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, dedup_booting_done);
+
+ dedup_deactivate_in_module();
+
+ _I("dedup module will be finalized");
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static struct module_ops dedup_module_ops = {
+ .priority = MODULE_PRIORITY_NORMAL,
+ .name = "dedup",
+ .init = resourced_dedup_init,
+ .exit = resourced_dedup_finalize,
+ .check_runtime_support = resourced_dedup_check_runtime_support,
+};
+
+MODULE_REGISTER(&dedup_module_ops)
#include "fd-handler.h"
#include "resourced-helper-worker.h"
#include "safe-kill.h"
+#include "dedup-common.h"
#define LOWMEM_NO_LIMIT 0
#define LOWMEM_THRES_INIT 0
/* thresholds for 64M RAM*/
#define PROACTIVE_64_THRES 10 /* MB */
#define PROACTIVE_64_LEAVE 30 /* MB */
+#define MEMCG_MEMORY_64_THRES_DEDUP 16 /* MB */
#define MEMCG_MEMORY_64_THRES_SWAP 15 /* MB */
#define MEMCG_MEMORY_64_THRES_LOW 8 /* MB */
#define MEMCG_MEMORY_64_THRES_MEDIUM 5 /* MB */
/* thresholds for 256M RAM */
#define PROACTIVE_256_THRES 50 /* MB */
#define PROACTIVE_256_LEAVE 80 /* MB */
+#define MEMCG_MEMORY_256_THRES_DEDUP 60 /* MB */
#define MEMCG_MEMORY_256_THRES_SWAP 40 /* MB */
#define MEMCG_MEMORY_256_THRES_LOW 20 /* MB */
#define MEMCG_MEMORY_256_THRES_MEDIUM 10 /* MB */
#define MEMCG_MEMORY_256_THRES_LEAVE 20 /* MB */
/* threshold for 448M RAM */
-#define PROACTIVE_448_THRES 80 /* MB */
+#define PROACTIVE_448_THRES 120 /* MB */
#define PROACTIVE_448_LEAVE 100 /* MB */
+#define MEMCG_MEMORY_448_THRES_DEDUP 60 /* MB */
#define MEMCG_MEMORY_448_THRES_SWAP 100 /* MB */
#define MEMCG_MEMORY_448_THRES_LOW 50 /* MB */
#define MEMCG_MEMORY_448_THRES_MEDIUM 40 /* MB */
/* threshold for 512M RAM */
#define PROACTIVE_512_THRES 80 /* MB */
#define PROACTIVE_512_LEAVE 100 /* MB */
+#define MEMCG_MEMORY_512_THRES_DEDUP 140 /* MB */
#define MEMCG_MEMORY_512_THRES_SWAP 100 /* MB */
#define MEMCG_MEMORY_512_THRES_LOW 50 /* MB */
#define MEMCG_MEMORY_512_THRES_MEDIUM 40 /* MB */
/* threshold for 768 RAM */
#define PROACTIVE_768_THRES 100 /* MB */
#define PROACTIVE_768_LEAVE 120 /* MB */
+#define MEMCG_MEMORY_768_THRES_DEDUP 180 /* MB */
#define MEMCG_MEMORY_768_THRES_SWAP 150 /* MB */
#define MEMCG_MEMORY_768_THRES_LOW 100 /* MB */
#define MEMCG_MEMORY_768_THRES_MEDIUM 60 /* MB */
/* threshold for more than 1024M RAM */
#define PROACTIVE_1024_THRES 150 /* MB */
#define PROACTIVE_1024_LEAVE 300 /* MB */
+#define MEMCG_MEMORY_1024_THRES_DEDUP 400 /* MB */
#define MEMCG_MEMORY_1024_THRES_SWAP 300 /* MB */
#define MEMCG_MEMORY_1024_THRES_LOW 200 /* MB */
#define MEMCG_MEMORY_1024_THRES_MEDIUM 100 /* MB */
/* threshold for more than 2048M RAM */
#define PROACTIVE_2048_THRES 200 /* MB */
#define PROACTIVE_2048_LEAVE 500 /* MB */
+#define MEMCG_MEMORY_2048_THRES_DEDUP 400 /* MB */
#define MEMCG_MEMORY_2048_THRES_SWAP 300 /* MB */
#define MEMCG_MEMORY_2048_THRES_LOW 200 /* MB */
#define MEMCG_MEMORY_2048_THRES_MEDIUM 160 /* MB */
static const char *convert_memstate_to_str(int mem_state)
{
- static const char *state_table[] = {"mem normal", "mem swap", "mem low",
+ static const char *state_table[] = {"mem normal", "mem dedup", "mem swap", "mem low",
"mem medium"};
if (mem_state >= 0 && mem_state < LOWMEM_MAX_LEVEL)
return state_table[mem_state];
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(MEMORY_LEVEL_NORMAL);
+ }
+ change_lowmem_state(LOWMEM_DEDUP);
+
+ 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 low_act(void)
{
change_lowmem_state(LOWMEM_LOW);
case LOWMEM_NORMAL:
normal_act();
break;
+ case LOWMEM_DEDUP:
+ dedup_act(KSM_SCAN_PARTIAL);
+ break;
case LOWMEM_SWAP:
swap_act();
break;
case LOWMEM_LOW:
+ dedup_act(KSM_SCAN_FULL);
low_act();
break;
case LOWMEM_MEDIUM:
if (strncmp(result->section, section_name, strlen(section_name)+1))
return RESOURCED_ERROR_NONE;
- if (!strncmp(result->name, "ThresholdSwap", strlen("ThresholdSwap")+1)) {
+ if (!strncmp(result->name, "ThresholdDedup", strlen("ThresholdDedup")+1)) {
+ int value = atoi(result->value);
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_DEDUP, value);
+ } else if (!strncmp(result->name, "ThresholdSwap", strlen("ThresholdSwap")+1)) {
int value = atoi(result->value);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, value);
} else if (!strncmp(result->name, "ThresholdLow", strlen("ThresholdLow")+1)) {
/* set thresholds for ram size 64M */
proactive_threshold = PROACTIVE_64_THRES;
proactive_leave = PROACTIVE_64_LEAVE;
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_DEDUP, MEMCG_MEMORY_64_THRES_DEDUP);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, MEMCG_MEMORY_64_THRES_SWAP);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, MEMCG_MEMORY_64_THRES_LOW);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, MEMCG_MEMORY_64_THRES_MEDIUM);
/* set thresholds for ram size 256M */
proactive_threshold = PROACTIVE_256_THRES;
proactive_leave = PROACTIVE_256_LEAVE;
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_DEDUP, MEMCG_MEMORY_256_THRES_DEDUP);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, MEMCG_MEMORY_256_THRES_SWAP);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, MEMCG_MEMORY_256_THRES_LOW);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, MEMCG_MEMORY_256_THRES_MEDIUM);
/* set thresholds for ram size 448M */
proactive_threshold = PROACTIVE_448_THRES;
proactive_leave = PROACTIVE_448_LEAVE;
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_DEDUP, MEMCG_MEMORY_448_THRES_DEDUP);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, MEMCG_MEMORY_448_THRES_SWAP);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, MEMCG_MEMORY_448_THRES_LOW);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, MEMCG_MEMORY_448_THRES_MEDIUM);
/* set thresholds for ram size 512M */
proactive_threshold = PROACTIVE_512_THRES;
proactive_leave = PROACTIVE_512_LEAVE;
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_DEDUP, MEMCG_MEMORY_512_THRES_DEDUP);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, MEMCG_MEMORY_512_THRES_SWAP);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, MEMCG_MEMORY_512_THRES_LOW);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, MEMCG_MEMORY_512_THRES_MEDIUM);
/* set thresholds for ram size 512M */
proactive_threshold = PROACTIVE_768_THRES;
proactive_leave = PROACTIVE_768_LEAVE;
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_DEDUP, MEMCG_MEMORY_768_THRES_DEDUP);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, MEMCG_MEMORY_768_THRES_SWAP);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, MEMCG_MEMORY_768_THRES_LOW);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, MEMCG_MEMORY_768_THRES_MEDIUM);
/* set thresholds for ram size more than 1G */
proactive_threshold = PROACTIVE_1024_THRES;
proactive_leave = PROACTIVE_1024_LEAVE;
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_DEDUP, MEMCG_MEMORY_1024_THRES_DEDUP);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, MEMCG_MEMORY_1024_THRES_SWAP);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, MEMCG_MEMORY_1024_THRES_LOW);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, MEMCG_MEMORY_1024_THRES_MEDIUM);
} else {
proactive_threshold = PROACTIVE_2048_THRES;
proactive_leave = PROACTIVE_2048_LEAVE;
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_DEDUP, MEMCG_MEMORY_2048_THRES_DEDUP);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, MEMCG_MEMORY_2048_THRES_SWAP);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, MEMCG_MEMORY_2048_THRES_LOW);
lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, MEMCG_MEMORY_2048_THRES_MEDIUM);