Remove useless source code
[platform/core/system/resourced.git] / src / resource-optimizer / memory / dedup / dedup.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18 */
19
20 /*
21  * @file dedup.c
22  * @desc dedup process
23  */
24 #include <trace.h>
25 #include <stdlib.h>
26 #include <sys/stat.h>
27 #include <sys/vfs.h>
28 #include <unistd.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 #include <pthread.h>
32 #include <sys/sysinfo.h>
33 #include <sys/time.h>
34 #include <sys/resource.h>
35 #include <memory-cgroup.h>
36 #include <linux/loop.h>
37 #include <fcntl.h>
38 #include <string.h>
39 #include <time.h>
40
41 #include "macro.h"
42 #include "util.h"
43 #include "module.h"
44 #include "module-data.h"
45 #include "dedup-common.h"
46 #include "config-parser.h"
47 #include "lowmem-handler.h"
48 #include "notifier.h"
49 #include "const.h"
50 #include "file-helper.h"
51
52 #define DEDUP_PRIORITY                  20
53 #define DEDUP_ON_BOOT_TIME                      60
54 #define DEDUP_FULL_SCAN_INTERVAL        60
55 #define DEDUP_INIT_SCAN_INTERVAL        300
56 #define DEDUP_STAT_INTERVAL                     60
57
58 enum dedup_thread_op {
59         DEDUP_OP_ACTIVATE,
60         DEDUP_OP_SCANONCE,
61         DEDUP_OP_STAT,
62         DEDUP_OP_DEACTIVATE,
63         DEDUP_OP_MAX,
64 };
65
66 struct dedup_thread_bundle {
67         enum dedup_thread_op op;
68         enum ksm_scan_mode mode;
69 };
70
71 static struct module_ops dedup_module_ops;
72
73 //static enum dedup_state dedup_state; /* see src/common/dedup-common.h */
74 static bool dedup_activated = false;
75
76 static bool dedup_enable = false;
77 static bool dedup_at_boot_enable = false;
78 static enum dedup_mode dedup_mode = DEDUP_MODE_PERIODIC;
79 static bool dedup_on_lowmem = false;
80
81 static int dedup_at_boot_delay = 60000;
82 static int dedup_full_scan_interval = 600000;
83 static int dedup_stat_interval = 60000;
84 static int dedup_partial_scan_interval = 60000;
85
86 static GSource *dedup_activating_timer = NULL;
87 static GSource *dedup_scanning_timer = NULL;
88 static GSource *dedup_stat_timer = NULL;
89
90 static int dedup_do_scan(enum ksm_scan_mode);
91 static int dedup_do_stat(void);
92
93 /* dedup module parameters */
94 enum dedup_param {
95         DEDUP_PARAM_ENABLE = 0,
96         DEDUP_PARAM_AT_BOOT,
97         DEDUP_PARAM_AT_BOOT_DELAY,
98         DEDUP_PARAM_ON_LOWMEM,
99         DEDUP_PARAM_STAT_INTERVAL,
100         DEDUP_PARAM_FULL_SCAN_INTERVAL,
101         DEDUP_PARAM_PARTIAL_SCAN_INTERVAL,
102         DEDUP_PARAM_MAX,
103 };
104
105 /* ksm param types & value ranges & pathes */
106 enum ksm_param {
107         KSM_PARAM_PAGES_TO_SCAN = 0,
108         KSM_PARAM_SLEEP_MSECS,
109         KSM_PARAM_FULL_SCAN_INTERVAL,
110         KSM_PARAM_SCAN_BOOST,
111         KSM_PARAM_MAX,
112 };
113
114 static int ksm_param_ranges[KSM_PARAM_MAX][2] = {
115         {0, 10000}, /* KSM_PARAM_PAGES_TO_SCAN */
116         {0, 1000}, /* KSM_PARAM_SLEEP_MSECS */
117         {0, INT_MAX}, /* KSM_PARAM_FULL_SCAN_INTERVAL */
118         {100, 10000}, /* KSM_PARAM_SCAN_BOOST */
119 };
120 static unsigned int ksm_params[KSM_PARAM_MAX];
121 static const char *ksm_param_path[KSM_PARAM_MAX] = {
122         "/sys/kernel/mm/ksm/pages_to_scan",
123         "/sys/kernel/mm/ksm/sleep_millisecs",
124         "/sys/kernel/mm/ksm/full_scan_interval",
125         "/sys/kernel/mm/ksm/scan_boost",
126 };
127
128 /* ksm statistics */
129 #define KSM_STAT_WINDOW_MAX 10
130 static int ksm_stat_window;
131 static unsigned int ksm_stats[KSM_STAT_MAX][KSM_STAT_WINDOW_MAX];
132 static const char *ksm_stat_path[2] = {
133         "/sys/kernel/mm/ksm/pages_sharing",
134         "/sys/kernel/mm/ksm/pages_shared",
135 };
136
137 enum ksm_sysfs_state_val {
138         DEDUP_SYSFS_STOP  = 0,
139         DEDUP_SYSFS_RUN = 1,
140         DEDUP_SYSFS_ONE_SHOT = 8,
141 };
142
143 static inline int dedup_set_ksm_state(enum dedup_state state)
144 {
145         int val;
146
147         if (state == DEDUP_ON)
148                 val = DEDUP_SYSFS_RUN;
149         else if (state == DEDUP_ONE_SHOT)
150                 val = DEDUP_SYSFS_ONE_SHOT;
151         else
152                 val = DEDUP_SYSFS_STOP;
153         /* write value to /sys/kernel/mm/ksm/run */
154         return fwrite_int(DEDUP_SYSFS_KSM_RUN, val);
155 }
156
157 static inline int dedup_set_ksm_param(int param_num, unsigned int value)
158 {
159         return fwrite_uint(ksm_param_path[param_num], value);
160 }
161
162 static void dedup_set_ksm_params(void)
163 {
164         int i;
165         for (i = KSM_PARAM_PAGES_TO_SCAN; i < KSM_PARAM_MAX; i++)
166                 dedup_set_ksm_param(i, ksm_params[i]);
167 }
168
169 static int dedup_check_and_scanning_once(enum ksm_scan_mode mode)
170 {
171         int val;
172         fread_int(DEDUP_SYSFS_KSM_ONESHOT, &val);
173         if (val == KSM_SCAN_NONE)
174                 return fwrite_int(DEDUP_SYSFS_KSM_ONESHOT, mode);
175         return 0;
176 }
177
178 static int dedup_scanning_once(enum ksm_scan_mode mode)
179 {
180         int ret;
181         _D("[DEDUP] Invoke scanning once to KSM (mode: %d)", mode);
182         ret = dedup_check_and_scanning_once(mode);
183
184         return ret;
185 }
186
187 unsigned int dedup_get_ksm_stat(enum ksm_stat stat_type)
188 {
189         int last;
190         if (stat_type < KSM_STAT_PAGES_SHARING || stat_type >= KSM_STAT_MAX)
191                 return 0;
192         last = (ksm_stat_window == 0) ? KSM_STAT_WINDOW_MAX - 1 : ksm_stat_window - 1;
193         return ksm_stats[stat_type][last];
194 }
195
196 static int dedup_record_stat(void)
197 {
198         int val, ret;
199
200         ret = fread_int(ksm_stat_path[KSM_STAT_PAGES_SHARING], &val);
201         if (!ret)
202                 ksm_stats[KSM_STAT_PAGES_SHARING][ksm_stat_window] = val;
203         ret = fread_int(ksm_stat_path[KSM_STAT_PAGES_SHARED], &val);
204         if (!ret)
205                 ksm_stats[KSM_STAT_PAGES_SHARED][ksm_stat_window] = val;
206
207         _I("read ksm stat: pages_sharing: %d pages_shared: %d",
208                 ksm_stats[KSM_STAT_PAGES_SHARING][ksm_stat_window],
209                 ksm_stats[KSM_STAT_PAGES_SHARED][ksm_stat_window]);
210         ksm_stat_window = (ksm_stat_window + 1) % KSM_STAT_WINDOW_MAX;
211         return RESOURCED_ERROR_NONE;
212 }
213
214 static gboolean dedup_scanning_timer_cb(gpointer data)
215 {
216         dedup_scanning_timer = NULL;
217         if (dedup_get_state() == DEDUP_ONE_SHOT)
218                 dedup_do_scan(KSM_SCAN_FULL);
219         return false;
220 }
221
222 static gboolean dedup_stat_timer_cb(gpointer data)
223 {
224         dedup_stat_timer = NULL;
225         dedup_do_stat();
226         return false;
227 }
228
229 static void dedup_reset_scanning_timer(void)
230 {
231         _D("reset scan-timer %d seconds", dedup_full_scan_interval);
232         dedup_scanning_timer =
233                 g_timeout_source_new_seconds(dedup_full_scan_interval);
234         g_source_set_callback(dedup_scanning_timer,
235                         dedup_scanning_timer_cb, NULL, NULL);
236         g_source_attach(dedup_scanning_timer, NULL);
237 }
238
239 static void dedup_reset_stat_timer(void)
240 {
241         _D("reset stat-timer %d seconds", dedup_stat_interval);
242         dedup_stat_timer =
243                 g_timeout_source_new_seconds(dedup_stat_interval);
244         g_source_set_callback(dedup_stat_timer,
245                         dedup_stat_timer_cb, NULL, NULL);
246         g_source_attach(dedup_stat_timer, NULL);
247 }
248
249 static void dedup_deactivate_in_module(void)
250 {
251         if (dedup_activating_timer) {
252                 g_source_destroy(dedup_activating_timer);
253                 dedup_activating_timer = NULL;
254         }
255
256         if (dedup_scanning_timer) {
257                 g_source_destroy(dedup_scanning_timer);
258                 dedup_scanning_timer = NULL;
259         }
260
261         if (dedup_stat_timer) {
262                 g_source_destroy(dedup_stat_timer);
263                 dedup_stat_timer = NULL;
264         }
265
266         _D("stop KSM thread");
267
268         dedup_set_ksm_state(DEDUP_OFF);
269 }
270
271 static enum dedup_state parse_dedup_state(enum dedup_mode mode)
272 {
273         if (mode == DEDUP_MODE_PERIODIC)
274                 return DEDUP_ON;
275         else if (mode == DEDUP_MODE_ONESHOT)
276                 return DEDUP_ONE_SHOT;
277         else
278                 return DEDUP_OFF;
279 }
280
281 static void dedup_activate_in_module(void)
282 {
283         enum dedup_state state;
284
285         if (dedup_get_state() == DEDUP_ON
286                         || dedup_get_state() == DEDUP_ONE_SHOT)
287                 return;
288
289         if (dedup_activated)
290                 return;
291
292         /* make ksm activate */
293         state = parse_dedup_state(dedup_mode);
294         dedup_set_state(state);
295         dedup_set_ksm_params();
296         dedup_set_ksm_state(state);
297         _I("kernel deduplication thread is enabled");
298
299         if (dedup_activating_timer) {
300                 g_source_destroy(dedup_activating_timer);
301                 dedup_activating_timer = NULL;
302         }
303
304         if (!dedup_on_lowmem) {
305                 dedup_scanning_timer = g_timeout_source_new_seconds(dedup_full_scan_interval);
306                 g_source_set_callback(dedup_scanning_timer, dedup_scanning_timer_cb, NULL, NULL);
307                 g_source_attach(dedup_scanning_timer, NULL);
308         }
309
310         dedup_stat_timer = g_timeout_source_new_seconds(dedup_stat_interval);
311         g_source_set_callback(dedup_stat_timer, dedup_stat_timer_cb, NULL, NULL);
312         g_source_attach(dedup_stat_timer, NULL);
313
314         dedup_activated = true;
315 }
316
317 /* translations for ms -> ns and s -> ns */
318 #define DEDUP_ACT_STOMS         1000
319 #define DEDUP_ACT_MSTONS        1000000
320
321 static bool dedup_check_scan_interval
322 (struct timespec *now, struct timespec *old, unsigned long interval)
323 {
324         unsigned long diff;
325         diff = (now->tv_sec - old->tv_sec) * DEDUP_ACT_STOMS;
326         diff += (now->tv_nsec - old->tv_nsec) / DEDUP_ACT_MSTONS;
327         return (diff >= interval);
328 }
329
330 /* used in dedup_do_scan */
331 static struct timespec now, partial_begin, full_begin;
332 static int nr_dedup;
333
334 static int dedup_do_scan(enum ksm_scan_mode scan_mode)
335 {
336         enum ksm_scan_mode mode = KSM_SCAN_NONE;
337
338         clock_gettime(CLOCK_MONOTONIC, &now);
339         if (scan_mode == KSM_SCAN_FULL) {
340                 /* if full scan is requested frequently, it can be
341                  * substituted by partial scan
342                  */
343                 if (dedup_check_scan_interval(&now, &full_begin,
344                                 dedup_full_scan_interval))
345                         mode = KSM_SCAN_FULL;
346                 else if (dedup_on_lowmem &&
347                                 dedup_check_scan_interval(&now, &partial_begin,
348                                 dedup_partial_scan_interval))
349                         mode = KSM_SCAN_PARTIAL;
350         } else if (scan_mode == KSM_SCAN_PARTIAL) {
351                 if (dedup_check_scan_interval(&now, &partial_begin,
352                                 dedup_partial_scan_interval))
353                         mode = KSM_SCAN_PARTIAL;
354         }
355
356         if (mode != KSM_SCAN_NONE) {
357                 _I("[DEDUP] dedup: %d-th %s deduplication triggering", nr_dedup++,
358                                 (mode == KSM_SCAN_FULL ? "FULL" : "PARTIAL"));
359                 if (!dedup_on_lowmem) {
360                         dedup_scanning_once(KSM_SCAN_FULL);
361                         dedup_reset_scanning_timer();
362                 } else
363                         dedup_scanning_once(mode);
364
365                 if (mode == KSM_SCAN_FULL)
366                         full_begin = now;
367                 else
368                         partial_begin = now;
369         }
370
371         return RESOURCED_ERROR_NONE;
372 }
373
374 static int dedup_do_stat(void)
375 {
376         dedup_record_stat();
377         dedup_reset_stat_timer();
378         return RESOURCED_ERROR_NONE;
379 }
380
381 static int dedup_start_handler(void *data)
382 {
383         if (!data)
384                 return RESOURCED_ERROR_NO_DATA;
385
386         dedup_activate_in_module();
387
388         return RESOURCED_ERROR_NONE;
389 }
390
391 static int dedup_scan_handler(void *data)
392 {
393         int *scan_mode;
394
395         if (dedup_get_state() != DEDUP_ONE_SHOT)
396                 return 0;
397
398         if (dedup_scanning_timer) {
399                 g_source_destroy(dedup_scanning_timer);
400                 dedup_scanning_timer = NULL;
401         }
402
403         scan_mode = data;
404         return dedup_do_scan(*scan_mode);
405 }
406
407 static int dedup_stop_handler(void *data)
408 {
409         if (!data)
410                 return RESOURCED_ERROR_NO_DATA;
411
412         dedup_deactivate_in_module();
413
414         return RESOURCED_ERROR_NONE;
415 }
416
417 static gboolean dedup_activate_timer_cb(gpointer data)
418 {
419         dedup_activating_timer = NULL;
420         _D("[DEDUP] dedup activating callback called");
421         dedup_activate_in_module();
422         return false;
423 }
424
425 static int dedup_booting_done(void *data)
426 {
427         if (dedup_activated)
428                 return RESOURCED_ERROR_NONE;
429
430         if (dedup_at_boot_enable) {
431                 /* if dedup_at_boot_enable is disabled,
432                  * other daemon should activate dedup */
433                 _D("[DEDUP] dedup booting done is called");
434                 if (dedup_at_boot_delay > 0)
435                         dedup_activating_timer =
436                                 g_timeout_source_new_seconds(dedup_at_boot_delay);
437                 else
438                         dedup_activating_timer =
439                                 g_timeout_source_new_seconds(DEDUP_ON_BOOT_TIME);
440                 g_source_set_callback(dedup_activating_timer,
441                         dedup_activate_timer_cb, NULL, NULL);
442                 g_source_attach(dedup_activating_timer, NULL);
443         }
444
445         return RESOURCED_ERROR_NONE;
446 }
447
448 static int dedup_parse_config_file(void)
449 {
450         int arg_ksm_pages_to_scan = 100;
451         int arg_ksm_sleep = 20; // 20 msecs
452         int arg_ksm_full_scan_interval = 60000; // 60 seconds
453         int arg_ksm_scan_boost = 100;
454
455         struct dedup_conf *dedup_conf = get_dedup_conf();
456         if (!dedup_conf) {
457                 _E("Dedup configuration should not be NULL");
458                 return RESOURCED_ERROR_FAIL;
459         }
460
461         dedup_enable = dedup_conf->enable;
462         dedup_at_boot_enable = dedup_conf->boot_dedup_enable;
463         dedup_on_lowmem = dedup_conf->scan_on_lowmem;
464
465         if (!strncmp(dedup_conf->ksm.mode, "oneshot", 8))
466                 dedup_mode = DEDUP_MODE_ONESHOT;
467         else if(!strncmp(dedup_conf->ksm.mode, "periodic", 9))
468                 dedup_mode = DEDUP_MODE_PERIODIC;
469
470         if (dedup_conf->ksm.pages > ksm_param_ranges[KSM_PARAM_PAGES_TO_SCAN][0] &&
471                 dedup_conf->ksm.pages <= ksm_param_ranges[KSM_PARAM_PAGES_TO_SCAN][1])
472                 ksm_params[KSM_PARAM_PAGES_TO_SCAN] = dedup_conf->ksm.pages;
473         else
474                 ksm_params[KSM_PARAM_PAGES_TO_SCAN] = arg_ksm_pages_to_scan;
475
476         if (dedup_conf->ksm.boost_pages > ksm_param_ranges[KSM_PARAM_SCAN_BOOST][0] &&
477             dedup_conf->ksm.boost_pages <= ksm_param_ranges[KSM_PARAM_SCAN_BOOST][1])
478                 ksm_params[KSM_PARAM_SCAN_BOOST] = dedup_conf->ksm.boost_pages;
479         else
480                 ksm_params[KSM_PARAM_SCAN_BOOST] = arg_ksm_scan_boost;
481
482         ksm_params[KSM_PARAM_SLEEP_MSECS] = arg_ksm_sleep;
483         ksm_params[KSM_PARAM_FULL_SCAN_INTERVAL] = arg_ksm_full_scan_interval;
484
485         _I("[DEDUP] deduplication mode: %s", dedup_mode == DEDUP_MODE_PERIODIC ?
486                         "kernel-managed" : "resourced-triggered");
487         _I("[DEDUP] deduplication on boot: %s", dedup_at_boot_enable ? "true" : "false");
488         _I("[DEDUP] scanning is invoked by %s", dedup_on_lowmem ?
489                         "LOWMEM event" : "periodic timer");
490         _I("[DEDUP] full scan interval: %d sec", dedup_full_scan_interval);
491         _I("[DEDUP] stat monitoring interval: %d sec", dedup_stat_interval);
492
493         _I("[DEDUP] ksm pages to scan: %d", ksm_params[KSM_PARAM_PAGES_TO_SCAN]);
494         _I("[DEDUP] ksm sleep time: %d", ksm_params[KSM_PARAM_SLEEP_MSECS]);
495         _I("[DEDUP] ksm full scan interval: %d", ksm_params[KSM_PARAM_FULL_SCAN_INTERVAL]);
496         _I("[DEDUP] ksm scan boost: %d", ksm_params[KSM_PARAM_SCAN_BOOST]);
497
498         free_dedup_conf();
499
500         return RESOURCED_ERROR_NONE;
501 }
502
503 static inline void init_dedup_stat(void)
504 {
505         int i, j;
506         for (i = KSM_STAT_PAGES_SHARING; i < KSM_STAT_MAX; i++)
507                 for (j = 0; j < KSM_STAT_WINDOW_MAX; j++)
508                         ksm_stats[i][j] = 0;
509         ksm_stat_window = 0;
510 }
511
512 static int dedup_init(void)
513 {
514         int ret;
515
516         init_dedup_stat();
517         ret = dedup_parse_config_file();
518         if (ret < 0)
519                 return ret;
520
521         if (!dedup_enable)
522                 return -ENODEV;
523
524         /* initialize time variables */
525         if (!clock_gettime(CLOCK_MONOTONIC, &now)) {
526                 partial_begin = now;
527                 full_begin = now;
528         } else {
529                 _I("Time initialization failed");
530                 memset(&partial_begin, 0, sizeof(struct timespec));
531                 memset(&full_begin, 0, sizeof(struct timespec));
532         }
533
534         return ret;
535 }
536
537 static int resourced_dedup_check_runtime_support(void *data)
538 {
539         /*
540          * Check whether CONFIG_LKSM is enabled in kernel.
541          */
542         if (access("/sys/kernel/mm/ksm/run", R_OK) != 0) {
543                 _W("the kernel don't support KSM, please check kernel configuration");
544                 return -ENOENT;
545         }
546         if (access("/sys/kernel/mm/ksm/one_shot_scanning", R_OK) != 0) {
547                 _W("the kernel support KSM but not LKSM, please check kernel configuration");
548                 return -ENOENT;
549         }
550
551         return 0;
552 }
553
554 static int resourced_dedup_init(void *data)
555 {
556         int ret;
557
558         dedup_set_state(DEDUP_OFF);
559
560         ret = dedup_init();
561         if (ret != RESOURCED_ERROR_NONE)
562                 return ret;
563         _I("resourced dedup module is initialized");
564         register_notifier(RESOURCED_NOTIFIER_DEDUP_START, dedup_start_handler);
565         register_notifier(RESOURCED_NOTIFIER_DEDUP_SCAN, dedup_scan_handler);
566         register_notifier(RESOURCED_NOTIFIER_DEDUP_STOP, dedup_stop_handler);
567         register_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, dedup_booting_done);
568
569         return ret;
570 }
571
572 static int resourced_dedup_finalize(void *data)
573 {
574         unregister_notifier(RESOURCED_NOTIFIER_DEDUP_START, dedup_start_handler);
575         unregister_notifier(RESOURCED_NOTIFIER_DEDUP_SCAN, dedup_scan_handler);
576         unregister_notifier(RESOURCED_NOTIFIER_DEDUP_STOP, dedup_stop_handler);
577         unregister_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, dedup_booting_done);
578
579         dedup_deactivate_in_module();
580
581         _I("dedup module will be finalized");
582
583         return RESOURCED_ERROR_NONE;
584 }
585
586 static struct module_ops dedup_module_ops = {
587         .priority = MODULE_PRIORITY_NORMAL,
588         .name = "dedup",
589         .init = resourced_dedup_init,
590         .exit = resourced_dedup_finalize,
591         .check_runtime_support = resourced_dedup_check_runtime_support,
592 };
593
594 MODULE_REGISTER(&dedup_module_ops)