DEDUP: a dedup module to control LKSM 52/240852/5
authorSung-hun Kim <sfoon.kim@samsung.com>
Thu, 23 Jan 2020 07:18:20 +0000 (16:18 +0900)
committerSung-hun Kim <sfoon.kim@samsung.com>
Wed, 26 Aug 2020 02:22:19 +0000 (11:22 +0900)
To manage LKSM in the kernel, a dedup module is added to resourced codebase.

The resourced daemon checks memory pressure level when an event is delivered
from the kernel. When the memory pressure level is crossed the predefined
threshold (which is configured by memory.conf), the daemon requests to invoke
memory deduplication via sysfs interface.

In order to enable a dedup module in resourced, the kernel should support LKSM
features.

Change-Id: I6bec596d40e68e1d6cc0c047d80ef30700ab846e
Signed-off-by: Sung-hun Kim <sfoon.kim@samsung.com>
CMakeLists.txt
packaging/resourced.spec
src/CMakeLists.txt
src/common/dedup-common.c [new file with mode: 0644]
src/common/dedup-common.h [new file with mode: 0644]
src/common/memory-common.h
src/common/notifier.h
src/dedup/dedup.c [new file with mode: 0644]
src/dedup/dedup.conf [new file with mode: 0644]
src/memory/memory.conf
src/memory/vmpressure-lowmem-handler.c

index c4faec8..eb187d7 100644 (file)
@@ -55,6 +55,7 @@ SET(COMMON_SOURCE_DIR           ${SOURCE_DIR}/common)
 SET(CPU_SOURCE_DIR              ${SOURCE_DIR}/cpu)
 SET(VIP_SOURCE_DIR              ${SOURCE_DIR}/vip-agent)
 SET(BLOCK_SOURCE_DIR            ${SOURCE_DIR}/block)
+SET(DEDUP_SOURCE_DIR            ${SOURCE_DIR}/dedup)
 
 INSTALL(FILES ${CMAKE_SOURCE_DIR}/resourced.conf DESTINATION /etc/dbus-1/system.d)
 
index d5ff477..a33787d 100644 (file)
@@ -160,7 +160,7 @@ mkdir -p %{buildroot}/%{confdir}/configs
 #   https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#ordering
 
 # List of configuration files provided by this package - update when any new config file is added!
-%define config_files block cpu cpu-sched heart memory proc swap vip-process
+%define config_files block cpu cpu-sched heart memory proc swap vip-process dedup
 
 for i in %{confdir}/*.conf; do
        if [ "$i" = %{confdir}/"*.conf" ]; then
@@ -205,6 +205,7 @@ fi
 %{plugindir}/libfreezer.so
 %{plugindir}/libheart.so
 %{plugindir}/libswap.so
+%{plugindir}/libdedup.so
 %attr(700, root, root) %{TZ_SYS_ETC}/dump.d/module.d/dump_heart_data.sh
 
 %files bin
@@ -222,6 +223,7 @@ fi
 %{confdir}/configs/config-memory.conf
 %{confdir}/configs/config-proc.conf
 %{confdir}/configs/config-swap.conf
+%{confdir}/configs/config-dedup.conf
 %{confdir}/configs/config-vip-process.conf
 %{confdir}/configs/config-block.conf
 %{confdir}/configs/config-heart.conf
index ff2ecb5..f8cf79b 100644 (file)
@@ -141,6 +141,12 @@ TARGET_LINK_LIBRARIES(swap resourced-private-api ${RESOURCED_REQUIRE_PKGS_LDFLAG
 INSTALL(TARGETS swap DESTINATION ${MAKE_INSTALL_PREFIX}${RD_PLUGIN_PATH})
 INSTALL(FILES ${SWAP_SOURCE_DIR}/swap.conf DESTINATION ${RD_CONFIG_PATH})
 
+ADD_LIBRARY(dedup MODULE
+       ${DEDUP_SOURCE_DIR}/dedup.c)
+TARGET_LINK_LIBRARIES(dedup resourced-private-api ${RESOURCED_REQUIRE_PKGS_LDFLAGS})
+INSTALL(TARGETS dedup DESTINATION ${MAKE_INSTALL_PREFIX}${RD_PLUGIN_PATH})
+INSTALL(FILES ${DEDUP_SOURCE_DIR}/dedup.conf DESTINATION ${RD_CONFIG_PATH})
+
 ADD_LIBRARY(heart MODULE
        ${HEART_SOURCE_DIR}/heart.c
        ${HEART_SOURCE_DIR}/heart-abnormal.c
diff --git a/src/common/dedup-common.c b/src/common/dedup-common.c
new file mode 100644 (file)
index 0000000..30980b4
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#include "macro.h"
+#include "dedup-common.h"
+
+static enum dedup_state dedup_state;
+
+enum dedup_state dedup_get_state(void)
+{
+       return dedup_state;
+}
+
+void dedup_set_state(enum dedup_state state)
+{
+       if ((state != DEDUP_ON) && (state != DEDUP_OFF) &&
+                       state != DEDUP_ONE_SHOT)
+               return;
+
+       dedup_state = state;
+}
diff --git a/src/common/dedup-common.h b/src/common/dedup-common.h
new file mode 100644 (file)
index 0000000..4827f60
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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-common.h
+ * @desc dedup common process
+ **/
+
+#ifndef __DEDUP_COMMON_H__
+#define __DEDUP_COMMON_H__
+
+#include <stdio.h>
+#include "resourced.h"
+#include "memory-common.h"
+
+#define DEDUP_CONF_FILE                RD_CONFIG_FILE(dedup)
+#define DEDUP_SYSFS_KSM_PATH   "/sys/kernel/mm/ksm"
+
+/* KSM operations */
+#define DEDUP_SYSFS_KSM_RUN    "/sys/kernel/mm/ksm/run"
+#define DEDUP_SYSFS_KSM_ONESHOT "/sys/kernel/mm/ksm/one_shot_scanning"
+
+enum dedup_state {
+       DEDUP_ARG_START = -1,
+       DEDUP_OFF,
+       DEDUP_ON,
+       DEDUP_ONE_SHOT,
+       DEDUP_ARG_END,
+};
+
+enum dedup_mode {
+       DEDUP_MODE_NONE = 0,
+       DEDUP_MODE_PERIODIC,
+       DEDUP_MODE_ONESHOT,
+       DEDUP_MODE_MAX,
+};
+
+enum ksm_stat {
+       KSM_STAT_PAGES_SHARING = 0,
+       KSM_STAT_PAGES_SHARED,
+       KSM_STAT_MAX,
+};
+
+enum ksm_scan_mode {
+       KSM_SCAN_NONE = 0,
+       KSM_SCAN_PARTIAL,
+       KSM_SCAN_FULL,
+};
+
+enum dedup_state dedup_get_state(void);
+void dedup_set_state(enum dedup_state state);
+unsigned int dedup_get_ksm_stat(enum ksm_stat stat_type);
+
+#endif /* __DEDUP_COMMON_H__ */
index 1690930..09805dd 100644 (file)
@@ -57,6 +57,7 @@ enum memcg_type {
 
 enum {
        LOWMEM_NORMAL,
+       LOWMEM_DEDUP,
        LOWMEM_SWAP,
        LOWMEM_LOW,
        LOWMEM_MEDIUM,
index 9cda5ec..12a65bb 100644 (file)
@@ -96,6 +96,10 @@ enum notifier_type {
        RESOURCED_NOTIFIER_CPU_ON,
        RESOURCED_NOTIFIER_CPU_OFF,
 
+       RESOURCED_NOTIFIER_DEDUP_START,
+       RESOURCED_NOTIFIER_DEDUP_SCAN,
+       RESOURCED_NOTIFIER_DEDUP_STOP,
+
        RESOURCED_NOTIFIER_MAX,
 };
 
diff --git a/src/dedup/dedup.c b/src/dedup/dedup.c
new file mode 100644 (file)
index 0000000..ce93023
--- /dev/null
@@ -0,0 +1,704 @@
+/*
+ * 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)
diff --git a/src/dedup/dedup.conf b/src/dedup/dedup.conf
new file mode 100644 (file)
index 0000000..e0bd07a
--- /dev/null
@@ -0,0 +1,38 @@
+[DEDUP]
+## DEDUP module parameters
+# Enable/Disable KSM daemon. (default is disable)
+# Enable=yes or 1 or true / Disable=no or 0 or false
+# all numeric values should be given in milliseconds
+Enable=0
+DedupAtBoot=yes
+DedupAtBootDelayMs=60000
+# if FullScanTimerIntervalMs is 0, deduplication is performed
+# only when the system in low-memory state (triggered by other controller)
+# In LKSM, there are two scan modes: partial-scan and full-scan.
+#  - Partial-scan: the KSM daemon scans a subset of processes and their pages
+#  - Full-scan: the KSM daemon scans pages of whole processes
+# PartialScanIntervalMs: a minimum interval for partial scan
+# FullScanIntervalMs: a minimum interval for full scan
+ScanOnLowmem=true
+StatIntervalMs=60000
+PartialScanIntervalMs=10000
+FullScanIntervalMs=600000
+
+[KSM]
+## KSM parameters
+# KSM modes: periodic and oneshot
+# periodic mode: means that the kernel manages all behaviors of deduplication
+# oneshot mode: means that the KSM daemon only performs deduplication
+# when the resourced triggers it
+Mode=oneshot
+#Mode=periodic
+
+## KSM scanning parameter
+# PagesToScan: the number of pages that KSM daemon scans when it wakes up
+# Sleep: once the KSM daemon scans given pages, it sleeps for a given interval (ms)
+# FullScanIntervalMS: used in the periodic mode, the value decides the interval of full scanning
+# ScanBoost: for partial scans, PagesToScan is temporarily boosted by this value
+PagesToScan=100
+SleepMs=20
+FullScanIntervalMs=60000
+ScanBoost=1000
index 356ad9e..b0432c1 100644 (file)
@@ -7,6 +7,9 @@ PREDEFINE=launchpad_preloading_preinitializing_daemon
 PREDEFINE=process_pool_launchpad_preloading_preinitializing_daemon
 
 [Memory64]
+# Threshold to start dedup
+ThresholdDedup=16              # MB 
+
 # Threshold to start swap
 ThresholdSwap=15               # MB
 
@@ -23,6 +26,9 @@ ThresholdLeave=8              # MB
 NumMaxVictims=1
 
 [Memory256]
+# Threshold to start dedup
+ThresholdDedup=60              # MB 
+
 # Threshold to start swap
 ThresholdSwap=40               # MB
 
@@ -39,6 +45,9 @@ ThresholdLeave=20             # MB
 NumMaxVictims=2
 
 [Memory448]
+# Threshold to start dedup
+ThresholdDedup=120             # MB 
+
 # Threshold to start swap
 ThresholdSwap=100              # MB
 
@@ -61,6 +70,9 @@ ProactiveLeave=100            # MB
 NumMaxVictims=5
 
 [Memory512]
+# Threshold to start dedup
+ThresholdDedup=140             # MB 
+
 # Threshold to start swap
 ThresholdSwap=100              # MB
 
@@ -83,6 +95,9 @@ ProactiveLeave=100            # MB
 NumMaxVictims=5
 
 [Memory768]
+# Threshold to start dedup
+ThresholdDedup=180             # MB 
+
 # Threshold to start swap
 ThresholdSwap=150              # MB
 
@@ -105,6 +120,9 @@ ProactiveLeave=130          # MB
 NumMaxVictims=5
 
 [Memory1024]
+# Threshold to start dedup
+ThresholdDedup=400             # MB 
+
 # Threshold to start swap
 ThresholdSwap=300              # MB
 
@@ -127,6 +145,9 @@ ProactiveLeave=230          # MB
 NumMaxVictims=5
 
 [Memory2048]
+# Threshold to start dedup
+ThresholdDedup=400             # MB 
+
 # Threshold to start swap
 ThresholdSwap=300              # MB
 
index ab5542f..dde42a5 100644 (file)
@@ -67,6 +67,7 @@
 #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 */
@@ -311,7 +319,7 @@ static GPtrArray *vip_apps;
 
 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];
@@ -1255,6 +1263,38 @@ static void swap_act(void)
                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);
@@ -1334,10 +1374,14 @@ static void lowmem_trigger_memory_state_action(int mem_state)
        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:
@@ -1462,7 +1506,10 @@ static int set_memory_config(const char *section_name, const struct parse_result
        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)) {
@@ -1560,6 +1607,7 @@ static void setup_memcg_params(void)
                /* 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);
@@ -1569,6 +1617,7 @@ static void setup_memcg_params(void)
                /* 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);
@@ -1578,6 +1627,7 @@ static void setup_memcg_params(void)
                /* 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);
@@ -1587,6 +1637,7 @@ static void setup_memcg_params(void)
                /* 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);
@@ -1596,6 +1647,7 @@ static void setup_memcg_params(void)
                /* 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);
@@ -1605,6 +1657,7 @@ static void setup_memcg_params(void)
                /* 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);
@@ -1613,6 +1666,7 @@ static void setup_memcg_params(void)
        } 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);