1baa489faSSeongJae Park // SPDX-License-Identifier: GPL-2.0 2baa489faSSeongJae Park 3baa489faSSeongJae Park #include <sys/mman.h> 407115fccSStefan Roesch #include <sys/prctl.h> 507115fccSStefan Roesch #include <sys/wait.h> 6baa489faSSeongJae Park #include <stdbool.h> 7baa489faSSeongJae Park #include <time.h> 8baa489faSSeongJae Park #include <string.h> 9baa489faSSeongJae Park #include <numa.h> 10baa489faSSeongJae Park #include <unistd.h> 11baa489faSSeongJae Park #include <fcntl.h> 12baa489faSSeongJae Park #include <stdint.h> 13baa489faSSeongJae Park #include <err.h> 14baa489faSSeongJae Park 15baa489faSSeongJae Park #include "../kselftest.h" 16baa489faSSeongJae Park #include <include/vdso/time64.h> 17af605d26SPeter Xu #include "vm_util.h" 18baa489faSSeongJae Park 19baa489faSSeongJae Park #define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/" 20baa489faSSeongJae Park #define KSM_FP(s) (KSM_SYSFS_PATH s) 21baa489faSSeongJae Park #define KSM_SCAN_LIMIT_SEC_DEFAULT 120 22baa489faSSeongJae Park #define KSM_PAGE_COUNT_DEFAULT 10l 23baa489faSSeongJae Park #define KSM_PROT_STR_DEFAULT "rw" 24baa489faSSeongJae Park #define KSM_USE_ZERO_PAGES_DEFAULT false 25baa489faSSeongJae Park #define KSM_MERGE_ACROSS_NODES_DEFAULT true 2607115fccSStefan Roesch #define KSM_MERGE_TYPE_DEFAULT 0 27baa489faSSeongJae Park #define MB (1ul << 20) 28baa489faSSeongJae Park 29baa489faSSeongJae Park struct ksm_sysfs { 30baa489faSSeongJae Park unsigned long max_page_sharing; 31baa489faSSeongJae Park unsigned long merge_across_nodes; 32baa489faSSeongJae Park unsigned long pages_to_scan; 33baa489faSSeongJae Park unsigned long run; 34baa489faSSeongJae Park unsigned long sleep_millisecs; 35baa489faSSeongJae Park unsigned long stable_node_chains_prune_millisecs; 36baa489faSSeongJae Park unsigned long use_zero_pages; 37baa489faSSeongJae Park }; 38baa489faSSeongJae Park 3907115fccSStefan Roesch enum ksm_merge_type { 4007115fccSStefan Roesch KSM_MERGE_MADVISE, 4107115fccSStefan Roesch KSM_MERGE_PRCTL, 4207115fccSStefan Roesch KSM_MERGE_LAST = KSM_MERGE_PRCTL 4307115fccSStefan Roesch }; 4407115fccSStefan Roesch 45baa489faSSeongJae Park enum ksm_test_name { 46baa489faSSeongJae Park CHECK_KSM_MERGE, 47baa489faSSeongJae Park CHECK_KSM_UNMERGE, 4807115fccSStefan Roesch CHECK_KSM_GET_MERGE_TYPE, 49baa489faSSeongJae Park CHECK_KSM_ZERO_PAGE_MERGE, 50baa489faSSeongJae Park CHECK_KSM_NUMA_MERGE, 51baa489faSSeongJae Park KSM_MERGE_TIME, 52baa489faSSeongJae Park KSM_MERGE_TIME_HUGE_PAGES, 53baa489faSSeongJae Park KSM_UNMERGE_TIME, 54baa489faSSeongJae Park KSM_COW_TIME 55baa489faSSeongJae Park }; 56baa489faSSeongJae Park 5707115fccSStefan Roesch int debug; 5807115fccSStefan Roesch 59baa489faSSeongJae Park static int ksm_write_sysfs(const char *file_path, unsigned long val) 60baa489faSSeongJae Park { 61baa489faSSeongJae Park FILE *f = fopen(file_path, "w"); 62baa489faSSeongJae Park 63baa489faSSeongJae Park if (!f) { 64baa489faSSeongJae Park fprintf(stderr, "f %s\n", file_path); 65baa489faSSeongJae Park perror("fopen"); 66baa489faSSeongJae Park return 1; 67baa489faSSeongJae Park } 68baa489faSSeongJae Park if (fprintf(f, "%lu", val) < 0) { 69baa489faSSeongJae Park perror("fprintf"); 70baa489faSSeongJae Park fclose(f); 71baa489faSSeongJae Park return 1; 72baa489faSSeongJae Park } 73baa489faSSeongJae Park fclose(f); 74baa489faSSeongJae Park 75baa489faSSeongJae Park return 0; 76baa489faSSeongJae Park } 77baa489faSSeongJae Park 78baa489faSSeongJae Park static int ksm_read_sysfs(const char *file_path, unsigned long *val) 79baa489faSSeongJae Park { 80baa489faSSeongJae Park FILE *f = fopen(file_path, "r"); 81baa489faSSeongJae Park 82baa489faSSeongJae Park if (!f) { 83baa489faSSeongJae Park fprintf(stderr, "f %s\n", file_path); 84baa489faSSeongJae Park perror("fopen"); 85baa489faSSeongJae Park return 1; 86baa489faSSeongJae Park } 87baa489faSSeongJae Park if (fscanf(f, "%lu", val) != 1) { 88baa489faSSeongJae Park perror("fscanf"); 89baa489faSSeongJae Park fclose(f); 90baa489faSSeongJae Park return 1; 91baa489faSSeongJae Park } 92baa489faSSeongJae Park fclose(f); 93baa489faSSeongJae Park 94baa489faSSeongJae Park return 0; 95baa489faSSeongJae Park } 96baa489faSSeongJae Park 9707115fccSStefan Roesch static void ksm_print_sysfs(void) 9807115fccSStefan Roesch { 9907115fccSStefan Roesch unsigned long max_page_sharing, pages_sharing, pages_shared; 10007115fccSStefan Roesch unsigned long full_scans, pages_unshared, pages_volatile; 10107115fccSStefan Roesch unsigned long stable_node_chains, stable_node_dups; 10207115fccSStefan Roesch long general_profit; 10307115fccSStefan Roesch 10407115fccSStefan Roesch if (ksm_read_sysfs(KSM_FP("pages_shared"), &pages_shared) || 10507115fccSStefan Roesch ksm_read_sysfs(KSM_FP("pages_sharing"), &pages_sharing) || 10607115fccSStefan Roesch ksm_read_sysfs(KSM_FP("max_page_sharing"), &max_page_sharing) || 10707115fccSStefan Roesch ksm_read_sysfs(KSM_FP("full_scans"), &full_scans) || 10807115fccSStefan Roesch ksm_read_sysfs(KSM_FP("pages_unshared"), &pages_unshared) || 10907115fccSStefan Roesch ksm_read_sysfs(KSM_FP("pages_volatile"), &pages_volatile) || 11007115fccSStefan Roesch ksm_read_sysfs(KSM_FP("stable_node_chains"), &stable_node_chains) || 11107115fccSStefan Roesch ksm_read_sysfs(KSM_FP("stable_node_dups"), &stable_node_dups) || 11207115fccSStefan Roesch ksm_read_sysfs(KSM_FP("general_profit"), (unsigned long *)&general_profit)) 11307115fccSStefan Roesch return; 11407115fccSStefan Roesch 11507115fccSStefan Roesch printf("pages_shared : %lu\n", pages_shared); 11607115fccSStefan Roesch printf("pages_sharing : %lu\n", pages_sharing); 11707115fccSStefan Roesch printf("max_page_sharing : %lu\n", max_page_sharing); 11807115fccSStefan Roesch printf("full_scans : %lu\n", full_scans); 11907115fccSStefan Roesch printf("pages_unshared : %lu\n", pages_unshared); 12007115fccSStefan Roesch printf("pages_volatile : %lu\n", pages_volatile); 12107115fccSStefan Roesch printf("stable_node_chains: %lu\n", stable_node_chains); 12207115fccSStefan Roesch printf("stable_node_dups : %lu\n", stable_node_dups); 12307115fccSStefan Roesch printf("general_profit : %ld\n", general_profit); 12407115fccSStefan Roesch } 12507115fccSStefan Roesch 12607115fccSStefan Roesch static void ksm_print_procfs(void) 12707115fccSStefan Roesch { 12807115fccSStefan Roesch const char *file_name = "/proc/self/ksm_stat"; 12907115fccSStefan Roesch char buffer[512]; 13007115fccSStefan Roesch FILE *f = fopen(file_name, "r"); 13107115fccSStefan Roesch 13207115fccSStefan Roesch if (!f) { 13307115fccSStefan Roesch fprintf(stderr, "f %s\n", file_name); 13407115fccSStefan Roesch perror("fopen"); 13507115fccSStefan Roesch return; 13607115fccSStefan Roesch } 13707115fccSStefan Roesch 13807115fccSStefan Roesch while (fgets(buffer, sizeof(buffer), f)) 13907115fccSStefan Roesch printf("%s", buffer); 14007115fccSStefan Roesch 14107115fccSStefan Roesch fclose(f); 14207115fccSStefan Roesch } 14307115fccSStefan Roesch 144baa489faSSeongJae Park static int str_to_prot(char *prot_str) 145baa489faSSeongJae Park { 146baa489faSSeongJae Park int prot = 0; 147baa489faSSeongJae Park 148baa489faSSeongJae Park if ((strchr(prot_str, 'r')) != NULL) 149baa489faSSeongJae Park prot |= PROT_READ; 150baa489faSSeongJae Park if ((strchr(prot_str, 'w')) != NULL) 151baa489faSSeongJae Park prot |= PROT_WRITE; 152baa489faSSeongJae Park if ((strchr(prot_str, 'x')) != NULL) 153baa489faSSeongJae Park prot |= PROT_EXEC; 154baa489faSSeongJae Park 155baa489faSSeongJae Park return prot; 156baa489faSSeongJae Park } 157baa489faSSeongJae Park 158baa489faSSeongJae Park static void print_help(void) 159baa489faSSeongJae Park { 160baa489faSSeongJae Park printf("usage: ksm_tests [-h] <test type> [-a prot] [-p page_count] [-l timeout]\n" 161baa489faSSeongJae Park "[-z use_zero_pages] [-m merge_across_nodes] [-s size]\n"); 162baa489faSSeongJae Park 163baa489faSSeongJae Park printf("Supported <test type>:\n" 164baa489faSSeongJae Park " -M (page merging)\n" 165baa489faSSeongJae Park " -Z (zero pages merging)\n" 166baa489faSSeongJae Park " -N (merging of pages in different NUMA nodes)\n" 167baa489faSSeongJae Park " -U (page unmerging)\n" 168baa489faSSeongJae Park " -P evaluate merging time and speed.\n" 169baa489faSSeongJae Park " For this test, the size of duplicated memory area (in MiB)\n" 170baa489faSSeongJae Park " must be provided using -s option\n" 171baa489faSSeongJae Park " -H evaluate merging time and speed of area allocated mostly with huge pages\n" 172baa489faSSeongJae Park " For this test, the size of duplicated memory area (in MiB)\n" 173baa489faSSeongJae Park " must be provided using -s option\n" 174baa489faSSeongJae Park " -D evaluate unmerging time and speed when disabling KSM.\n" 175baa489faSSeongJae Park " For this test, the size of duplicated memory area (in MiB)\n" 176baa489faSSeongJae Park " must be provided using -s option\n" 177baa489faSSeongJae Park " -C evaluate the time required to break COW of merged pages.\n\n"); 178baa489faSSeongJae Park 179baa489faSSeongJae Park printf(" -a: specify the access protections of pages.\n" 180baa489faSSeongJae Park " <prot> must be of the form [rwx].\n" 181baa489faSSeongJae Park " Default: %s\n", KSM_PROT_STR_DEFAULT); 182baa489faSSeongJae Park printf(" -p: specify the number of pages to test.\n" 183baa489faSSeongJae Park " Default: %ld\n", KSM_PAGE_COUNT_DEFAULT); 184baa489faSSeongJae Park printf(" -l: limit the maximum running time (in seconds) for a test.\n" 185baa489faSSeongJae Park " Default: %d seconds\n", KSM_SCAN_LIMIT_SEC_DEFAULT); 186baa489faSSeongJae Park printf(" -z: change use_zero_pages tunable\n" 187baa489faSSeongJae Park " Default: %d\n", KSM_USE_ZERO_PAGES_DEFAULT); 188baa489faSSeongJae Park printf(" -m: change merge_across_nodes tunable\n" 189baa489faSSeongJae Park " Default: %d\n", KSM_MERGE_ACROSS_NODES_DEFAULT); 19007115fccSStefan Roesch printf(" -d: turn debugging output on\n"); 191baa489faSSeongJae Park printf(" -s: the size of duplicated memory area (in MiB)\n"); 19207115fccSStefan Roesch printf(" -t: KSM merge type\n" 19307115fccSStefan Roesch " Default: 0\n" 19407115fccSStefan Roesch " 0: madvise merging\n" 19507115fccSStefan Roesch " 1: prctl merging\n"); 196baa489faSSeongJae Park 197baa489faSSeongJae Park exit(0); 198baa489faSSeongJae Park } 199baa489faSSeongJae Park 200baa489faSSeongJae Park static void *allocate_memory(void *ptr, int prot, int mapping, char data, size_t map_size) 201baa489faSSeongJae Park { 202baa489faSSeongJae Park void *map_ptr = mmap(ptr, map_size, PROT_WRITE, mapping, -1, 0); 203baa489faSSeongJae Park 204baa489faSSeongJae Park if (!map_ptr) { 205baa489faSSeongJae Park perror("mmap"); 206baa489faSSeongJae Park return NULL; 207baa489faSSeongJae Park } 208baa489faSSeongJae Park memset(map_ptr, data, map_size); 209baa489faSSeongJae Park if (mprotect(map_ptr, map_size, prot)) { 210baa489faSSeongJae Park perror("mprotect"); 211baa489faSSeongJae Park munmap(map_ptr, map_size); 212baa489faSSeongJae Park return NULL; 213baa489faSSeongJae Park } 214baa489faSSeongJae Park 215baa489faSSeongJae Park return map_ptr; 216baa489faSSeongJae Park } 217baa489faSSeongJae Park 218baa489faSSeongJae Park static int ksm_do_scan(int scan_count, struct timespec start_time, int timeout) 219baa489faSSeongJae Park { 220baa489faSSeongJae Park struct timespec cur_time; 221baa489faSSeongJae Park unsigned long cur_scan, init_scan; 222baa489faSSeongJae Park 223baa489faSSeongJae Park if (ksm_read_sysfs(KSM_FP("full_scans"), &init_scan)) 224baa489faSSeongJae Park return 1; 225baa489faSSeongJae Park cur_scan = init_scan; 226baa489faSSeongJae Park 227baa489faSSeongJae Park while (cur_scan < init_scan + scan_count) { 228baa489faSSeongJae Park if (ksm_read_sysfs(KSM_FP("full_scans"), &cur_scan)) 229baa489faSSeongJae Park return 1; 230baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &cur_time)) { 231baa489faSSeongJae Park perror("clock_gettime"); 232baa489faSSeongJae Park return 1; 233baa489faSSeongJae Park } 234baa489faSSeongJae Park if ((cur_time.tv_sec - start_time.tv_sec) > timeout) { 235baa489faSSeongJae Park printf("Scan time limit exceeded\n"); 236baa489faSSeongJae Park return 1; 237baa489faSSeongJae Park } 238baa489faSSeongJae Park } 239baa489faSSeongJae Park 240baa489faSSeongJae Park return 0; 241baa489faSSeongJae Park } 242baa489faSSeongJae Park 24307115fccSStefan Roesch static int ksm_merge_pages(int merge_type, void *addr, size_t size, 24407115fccSStefan Roesch struct timespec start_time, int timeout) 245baa489faSSeongJae Park { 24607115fccSStefan Roesch if (merge_type == KSM_MERGE_MADVISE) { 247baa489faSSeongJae Park if (madvise(addr, size, MADV_MERGEABLE)) { 248baa489faSSeongJae Park perror("madvise"); 249baa489faSSeongJae Park return 1; 250baa489faSSeongJae Park } 25107115fccSStefan Roesch } else if (merge_type == KSM_MERGE_PRCTL) { 25207115fccSStefan Roesch if (prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0)) { 25307115fccSStefan Roesch perror("prctl"); 25407115fccSStefan Roesch return 1; 25507115fccSStefan Roesch } 25607115fccSStefan Roesch } 25707115fccSStefan Roesch 258baa489faSSeongJae Park if (ksm_write_sysfs(KSM_FP("run"), 1)) 259baa489faSSeongJae Park return 1; 260baa489faSSeongJae Park 261baa489faSSeongJae Park /* Since merging occurs only after 2 scans, make sure to get at least 2 full scans */ 262baa489faSSeongJae Park if (ksm_do_scan(2, start_time, timeout)) 263baa489faSSeongJae Park return 1; 264baa489faSSeongJae Park 265baa489faSSeongJae Park return 0; 266baa489faSSeongJae Park } 267baa489faSSeongJae Park 268baa489faSSeongJae Park static int ksm_unmerge_pages(void *addr, size_t size, 269baa489faSSeongJae Park struct timespec start_time, int timeout) 270baa489faSSeongJae Park { 271baa489faSSeongJae Park if (madvise(addr, size, MADV_UNMERGEABLE)) { 272baa489faSSeongJae Park perror("madvise"); 273baa489faSSeongJae Park return 1; 274baa489faSSeongJae Park } 275baa489faSSeongJae Park return 0; 276baa489faSSeongJae Park } 277baa489faSSeongJae Park 278baa489faSSeongJae Park static bool assert_ksm_pages_count(long dupl_page_count) 279baa489faSSeongJae Park { 280baa489faSSeongJae Park unsigned long max_page_sharing, pages_sharing, pages_shared; 281baa489faSSeongJae Park 282baa489faSSeongJae Park if (ksm_read_sysfs(KSM_FP("pages_shared"), &pages_shared) || 283baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("pages_sharing"), &pages_sharing) || 284baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("max_page_sharing"), &max_page_sharing)) 285baa489faSSeongJae Park return false; 286baa489faSSeongJae Park 28707115fccSStefan Roesch if (debug) { 28807115fccSStefan Roesch ksm_print_sysfs(); 28907115fccSStefan Roesch ksm_print_procfs(); 29007115fccSStefan Roesch } 29107115fccSStefan Roesch 292baa489faSSeongJae Park /* 293baa489faSSeongJae Park * Since there must be at least 2 pages for merging and 1 page can be 294baa489faSSeongJae Park * shared with the limited number of pages (max_page_sharing), sometimes 295baa489faSSeongJae Park * there are 'leftover' pages that cannot be merged. For example, if there 296baa489faSSeongJae Park * are 11 pages and max_page_sharing = 10, then only 10 pages will be 297baa489faSSeongJae Park * merged and the 11th page won't be affected. As a result, when the number 298baa489faSSeongJae Park * of duplicate pages is divided by max_page_sharing and the remainder is 1, 299baa489faSSeongJae Park * pages_shared and pages_sharing values will be equal between dupl_page_count 300baa489faSSeongJae Park * and dupl_page_count - 1. 301baa489faSSeongJae Park */ 302baa489faSSeongJae Park if (dupl_page_count % max_page_sharing == 1 || dupl_page_count % max_page_sharing == 0) { 303baa489faSSeongJae Park if (pages_shared == dupl_page_count / max_page_sharing && 304baa489faSSeongJae Park pages_sharing == pages_shared * (max_page_sharing - 1)) 305baa489faSSeongJae Park return true; 306baa489faSSeongJae Park } else { 307baa489faSSeongJae Park if (pages_shared == (dupl_page_count / max_page_sharing + 1) && 308baa489faSSeongJae Park pages_sharing == dupl_page_count - pages_shared) 309baa489faSSeongJae Park return true; 310baa489faSSeongJae Park } 311baa489faSSeongJae Park 312baa489faSSeongJae Park return false; 313baa489faSSeongJae Park } 314baa489faSSeongJae Park 315baa489faSSeongJae Park static int ksm_save_def(struct ksm_sysfs *ksm_sysfs) 316baa489faSSeongJae Park { 317baa489faSSeongJae Park if (ksm_read_sysfs(KSM_FP("max_page_sharing"), &ksm_sysfs->max_page_sharing) || 318baa489faSSeongJae Park numa_available() ? 0 : 319baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("merge_across_nodes"), &ksm_sysfs->merge_across_nodes) || 320baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("sleep_millisecs"), &ksm_sysfs->sleep_millisecs) || 321baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("pages_to_scan"), &ksm_sysfs->pages_to_scan) || 322baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("run"), &ksm_sysfs->run) || 323baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("stable_node_chains_prune_millisecs"), 324baa489faSSeongJae Park &ksm_sysfs->stable_node_chains_prune_millisecs) || 325baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("use_zero_pages"), &ksm_sysfs->use_zero_pages)) 326baa489faSSeongJae Park return 1; 327baa489faSSeongJae Park 328baa489faSSeongJae Park return 0; 329baa489faSSeongJae Park } 330baa489faSSeongJae Park 331baa489faSSeongJae Park static int ksm_restore(struct ksm_sysfs *ksm_sysfs) 332baa489faSSeongJae Park { 333baa489faSSeongJae Park if (ksm_write_sysfs(KSM_FP("max_page_sharing"), ksm_sysfs->max_page_sharing) || 334baa489faSSeongJae Park numa_available() ? 0 : 335baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("merge_across_nodes"), ksm_sysfs->merge_across_nodes) || 336baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("pages_to_scan"), ksm_sysfs->pages_to_scan) || 337baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("run"), ksm_sysfs->run) || 338baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("sleep_millisecs"), ksm_sysfs->sleep_millisecs) || 339baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("stable_node_chains_prune_millisecs"), 340baa489faSSeongJae Park ksm_sysfs->stable_node_chains_prune_millisecs) || 341baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("use_zero_pages"), ksm_sysfs->use_zero_pages)) 342baa489faSSeongJae Park return 1; 343baa489faSSeongJae Park 344baa489faSSeongJae Park return 0; 345baa489faSSeongJae Park } 346baa489faSSeongJae Park 34707115fccSStefan Roesch static int check_ksm_merge(int merge_type, int mapping, int prot, 34807115fccSStefan Roesch long page_count, int timeout, size_t page_size) 349baa489faSSeongJae Park { 350baa489faSSeongJae Park void *map_ptr; 351baa489faSSeongJae Park struct timespec start_time; 352baa489faSSeongJae Park 353baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 354baa489faSSeongJae Park perror("clock_gettime"); 355baa489faSSeongJae Park return KSFT_FAIL; 356baa489faSSeongJae Park } 357baa489faSSeongJae Park 358baa489faSSeongJae Park /* fill pages with the same data and merge them */ 359baa489faSSeongJae Park map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count); 360baa489faSSeongJae Park if (!map_ptr) 361baa489faSSeongJae Park return KSFT_FAIL; 362baa489faSSeongJae Park 36307115fccSStefan Roesch if (ksm_merge_pages(merge_type, map_ptr, page_size * page_count, start_time, timeout)) 364baa489faSSeongJae Park goto err_out; 365baa489faSSeongJae Park 366baa489faSSeongJae Park /* verify that the right number of pages are merged */ 367baa489faSSeongJae Park if (assert_ksm_pages_count(page_count)) { 368baa489faSSeongJae Park printf("OK\n"); 369baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 37007115fccSStefan Roesch if (merge_type == KSM_MERGE_PRCTL) 37107115fccSStefan Roesch prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0); 372baa489faSSeongJae Park return KSFT_PASS; 373baa489faSSeongJae Park } 374baa489faSSeongJae Park 375baa489faSSeongJae Park err_out: 376baa489faSSeongJae Park printf("Not OK\n"); 377baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 378baa489faSSeongJae Park return KSFT_FAIL; 379baa489faSSeongJae Park } 380baa489faSSeongJae Park 38107115fccSStefan Roesch static int check_ksm_unmerge(int merge_type, int mapping, int prot, int timeout, size_t page_size) 382baa489faSSeongJae Park { 383baa489faSSeongJae Park void *map_ptr; 384baa489faSSeongJae Park struct timespec start_time; 385baa489faSSeongJae Park int page_count = 2; 386baa489faSSeongJae Park 387baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 388baa489faSSeongJae Park perror("clock_gettime"); 389baa489faSSeongJae Park return KSFT_FAIL; 390baa489faSSeongJae Park } 391baa489faSSeongJae Park 392baa489faSSeongJae Park /* fill pages with the same data and merge them */ 393baa489faSSeongJae Park map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count); 394baa489faSSeongJae Park if (!map_ptr) 395baa489faSSeongJae Park return KSFT_FAIL; 396baa489faSSeongJae Park 39707115fccSStefan Roesch if (ksm_merge_pages(merge_type, map_ptr, page_size * page_count, start_time, timeout)) 398baa489faSSeongJae Park goto err_out; 399baa489faSSeongJae Park 400baa489faSSeongJae Park /* change 1 byte in each of the 2 pages -- KSM must automatically unmerge them */ 401baa489faSSeongJae Park memset(map_ptr, '-', 1); 402baa489faSSeongJae Park memset(map_ptr + page_size, '+', 1); 403baa489faSSeongJae Park 404baa489faSSeongJae Park /* get at least 1 scan, so KSM can detect that the pages were modified */ 405baa489faSSeongJae Park if (ksm_do_scan(1, start_time, timeout)) 406baa489faSSeongJae Park goto err_out; 407baa489faSSeongJae Park 408baa489faSSeongJae Park /* check that unmerging was successful and 0 pages are currently merged */ 409baa489faSSeongJae Park if (assert_ksm_pages_count(0)) { 410baa489faSSeongJae Park printf("OK\n"); 411baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 412baa489faSSeongJae Park return KSFT_PASS; 413baa489faSSeongJae Park } 414baa489faSSeongJae Park 415baa489faSSeongJae Park err_out: 416baa489faSSeongJae Park printf("Not OK\n"); 417baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 418baa489faSSeongJae Park return KSFT_FAIL; 419baa489faSSeongJae Park } 420baa489faSSeongJae Park 42107115fccSStefan Roesch static int check_ksm_zero_page_merge(int merge_type, int mapping, int prot, long page_count, 42207115fccSStefan Roesch int timeout, bool use_zero_pages, size_t page_size) 423baa489faSSeongJae Park { 424baa489faSSeongJae Park void *map_ptr; 425baa489faSSeongJae Park struct timespec start_time; 426baa489faSSeongJae Park 427baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 428baa489faSSeongJae Park perror("clock_gettime"); 429baa489faSSeongJae Park return KSFT_FAIL; 430baa489faSSeongJae Park } 431baa489faSSeongJae Park 432baa489faSSeongJae Park if (ksm_write_sysfs(KSM_FP("use_zero_pages"), use_zero_pages)) 433baa489faSSeongJae Park return KSFT_FAIL; 434baa489faSSeongJae Park 435baa489faSSeongJae Park /* fill pages with zero and try to merge them */ 436baa489faSSeongJae Park map_ptr = allocate_memory(NULL, prot, mapping, 0, page_size * page_count); 437baa489faSSeongJae Park if (!map_ptr) 438baa489faSSeongJae Park return KSFT_FAIL; 439baa489faSSeongJae Park 44007115fccSStefan Roesch if (ksm_merge_pages(merge_type, map_ptr, page_size * page_count, start_time, timeout)) 441baa489faSSeongJae Park goto err_out; 442baa489faSSeongJae Park 443baa489faSSeongJae Park /* 444baa489faSSeongJae Park * verify that the right number of pages are merged: 445baa489faSSeongJae Park * 1) if use_zero_pages is set to 1, empty pages are merged 446baa489faSSeongJae Park * with the kernel zero page instead of with each other; 447baa489faSSeongJae Park * 2) if use_zero_pages is set to 0, empty pages are not treated specially 448baa489faSSeongJae Park * and merged as usual. 449baa489faSSeongJae Park */ 450baa489faSSeongJae Park if (use_zero_pages && !assert_ksm_pages_count(0)) 451baa489faSSeongJae Park goto err_out; 452baa489faSSeongJae Park else if (!use_zero_pages && !assert_ksm_pages_count(page_count)) 453baa489faSSeongJae Park goto err_out; 454baa489faSSeongJae Park 455baa489faSSeongJae Park printf("OK\n"); 456baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 457baa489faSSeongJae Park return KSFT_PASS; 458baa489faSSeongJae Park 459baa489faSSeongJae Park err_out: 460baa489faSSeongJae Park printf("Not OK\n"); 461baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 462baa489faSSeongJae Park return KSFT_FAIL; 463baa489faSSeongJae Park } 464baa489faSSeongJae Park 465baa489faSSeongJae Park static int get_next_mem_node(int node) 466baa489faSSeongJae Park { 467baa489faSSeongJae Park 468baa489faSSeongJae Park long node_size; 469baa489faSSeongJae Park int mem_node = 0; 470baa489faSSeongJae Park int i, max_node = numa_max_node(); 471baa489faSSeongJae Park 472baa489faSSeongJae Park for (i = node + 1; i <= max_node + node; i++) { 473baa489faSSeongJae Park mem_node = i % (max_node + 1); 474baa489faSSeongJae Park node_size = numa_node_size(mem_node, NULL); 475baa489faSSeongJae Park if (node_size > 0) 476baa489faSSeongJae Park break; 477baa489faSSeongJae Park } 478baa489faSSeongJae Park return mem_node; 479baa489faSSeongJae Park } 480baa489faSSeongJae Park 481baa489faSSeongJae Park static int get_first_mem_node(void) 482baa489faSSeongJae Park { 483baa489faSSeongJae Park return get_next_mem_node(numa_max_node()); 484baa489faSSeongJae Park } 485baa489faSSeongJae Park 48607115fccSStefan Roesch static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeout, 48707115fccSStefan Roesch bool merge_across_nodes, size_t page_size) 488baa489faSSeongJae Park { 489baa489faSSeongJae Park void *numa1_map_ptr, *numa2_map_ptr; 490baa489faSSeongJae Park struct timespec start_time; 491baa489faSSeongJae Park int page_count = 2; 492baa489faSSeongJae Park int first_node; 493baa489faSSeongJae Park 494baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 495baa489faSSeongJae Park perror("clock_gettime"); 496baa489faSSeongJae Park return KSFT_FAIL; 497baa489faSSeongJae Park } 498baa489faSSeongJae Park 499baa489faSSeongJae Park if (numa_available() < 0) { 500baa489faSSeongJae Park perror("NUMA support not enabled"); 501baa489faSSeongJae Park return KSFT_SKIP; 502baa489faSSeongJae Park } 503baa489faSSeongJae Park if (numa_num_configured_nodes() <= 1) { 504baa489faSSeongJae Park printf("At least 2 NUMA nodes must be available\n"); 505baa489faSSeongJae Park return KSFT_SKIP; 506baa489faSSeongJae Park } 507baa489faSSeongJae Park if (ksm_write_sysfs(KSM_FP("merge_across_nodes"), merge_across_nodes)) 508baa489faSSeongJae Park return KSFT_FAIL; 509baa489faSSeongJae Park 510baa489faSSeongJae Park /* allocate 2 pages in 2 different NUMA nodes and fill them with the same data */ 511baa489faSSeongJae Park first_node = get_first_mem_node(); 512baa489faSSeongJae Park numa1_map_ptr = numa_alloc_onnode(page_size, first_node); 513baa489faSSeongJae Park numa2_map_ptr = numa_alloc_onnode(page_size, get_next_mem_node(first_node)); 514baa489faSSeongJae Park if (!numa1_map_ptr || !numa2_map_ptr) { 515baa489faSSeongJae Park perror("numa_alloc_onnode"); 516baa489faSSeongJae Park return KSFT_FAIL; 517baa489faSSeongJae Park } 518baa489faSSeongJae Park 519baa489faSSeongJae Park memset(numa1_map_ptr, '*', page_size); 520baa489faSSeongJae Park memset(numa2_map_ptr, '*', page_size); 521baa489faSSeongJae Park 522baa489faSSeongJae Park /* try to merge the pages */ 52307115fccSStefan Roesch if (ksm_merge_pages(merge_type, numa1_map_ptr, page_size, start_time, timeout) || 52407115fccSStefan Roesch ksm_merge_pages(merge_type, numa2_map_ptr, page_size, start_time, timeout)) 525baa489faSSeongJae Park goto err_out; 526baa489faSSeongJae Park 527baa489faSSeongJae Park /* 528baa489faSSeongJae Park * verify that the right number of pages are merged: 529baa489faSSeongJae Park * 1) if merge_across_nodes was enabled, 2 duplicate pages will be merged; 530baa489faSSeongJae Park * 2) if merge_across_nodes = 0, there must be 0 merged pages, since there is 531baa489faSSeongJae Park * only 1 unique page in each node and they can't be shared. 532baa489faSSeongJae Park */ 533baa489faSSeongJae Park if (merge_across_nodes && !assert_ksm_pages_count(page_count)) 534baa489faSSeongJae Park goto err_out; 535baa489faSSeongJae Park else if (!merge_across_nodes && !assert_ksm_pages_count(0)) 536baa489faSSeongJae Park goto err_out; 537baa489faSSeongJae Park 538baa489faSSeongJae Park numa_free(numa1_map_ptr, page_size); 539baa489faSSeongJae Park numa_free(numa2_map_ptr, page_size); 540baa489faSSeongJae Park printf("OK\n"); 541baa489faSSeongJae Park return KSFT_PASS; 542baa489faSSeongJae Park 543baa489faSSeongJae Park err_out: 544baa489faSSeongJae Park numa_free(numa1_map_ptr, page_size); 545baa489faSSeongJae Park numa_free(numa2_map_ptr, page_size); 546baa489faSSeongJae Park printf("Not OK\n"); 547baa489faSSeongJae Park return KSFT_FAIL; 548baa489faSSeongJae Park } 549baa489faSSeongJae Park 55007115fccSStefan Roesch static int ksm_merge_hugepages_time(int merge_type, int mapping, int prot, 55107115fccSStefan Roesch int timeout, size_t map_size) 552baa489faSSeongJae Park { 553baa489faSSeongJae Park void *map_ptr, *map_ptr_orig; 554baa489faSSeongJae Park struct timespec start_time, end_time; 555baa489faSSeongJae Park unsigned long scan_time_ns; 556baa489faSSeongJae Park int pagemap_fd, n_normal_pages, n_huge_pages; 557baa489faSSeongJae Park 558baa489faSSeongJae Park map_size *= MB; 559baa489faSSeongJae Park size_t len = map_size; 560baa489faSSeongJae Park 561baa489faSSeongJae Park len -= len % HPAGE_SIZE; 562baa489faSSeongJae Park map_ptr_orig = mmap(NULL, len + HPAGE_SIZE, PROT_READ | PROT_WRITE, 563baa489faSSeongJae Park MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE, -1, 0); 564baa489faSSeongJae Park map_ptr = map_ptr_orig + HPAGE_SIZE - (uintptr_t)map_ptr_orig % HPAGE_SIZE; 565baa489faSSeongJae Park 566baa489faSSeongJae Park if (map_ptr_orig == MAP_FAILED) 567baa489faSSeongJae Park err(2, "initial mmap"); 568baa489faSSeongJae Park 569*d6a14d9dSRyan Roberts if (madvise(map_ptr, len, MADV_HUGEPAGE)) 570baa489faSSeongJae Park err(2, "MADV_HUGEPAGE"); 571baa489faSSeongJae Park 572baa489faSSeongJae Park pagemap_fd = open("/proc/self/pagemap", O_RDONLY); 573baa489faSSeongJae Park if (pagemap_fd < 0) 574baa489faSSeongJae Park err(2, "open pagemap"); 575baa489faSSeongJae Park 576baa489faSSeongJae Park n_normal_pages = 0; 577baa489faSSeongJae Park n_huge_pages = 0; 578baa489faSSeongJae Park for (void *p = map_ptr; p < map_ptr + len; p += HPAGE_SIZE) { 579baa489faSSeongJae Park if (allocate_transhuge(p, pagemap_fd) < 0) 580baa489faSSeongJae Park n_normal_pages++; 581baa489faSSeongJae Park else 582baa489faSSeongJae Park n_huge_pages++; 583baa489faSSeongJae Park } 584baa489faSSeongJae Park printf("Number of normal pages: %d\n", n_normal_pages); 585baa489faSSeongJae Park printf("Number of huge pages: %d\n", n_huge_pages); 586baa489faSSeongJae Park 587baa489faSSeongJae Park memset(map_ptr, '*', len); 588baa489faSSeongJae Park 589baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 590baa489faSSeongJae Park perror("clock_gettime"); 591baa489faSSeongJae Park goto err_out; 592baa489faSSeongJae Park } 59307115fccSStefan Roesch if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout)) 594baa489faSSeongJae Park goto err_out; 595baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 596baa489faSSeongJae Park perror("clock_gettime"); 597baa489faSSeongJae Park goto err_out; 598baa489faSSeongJae Park } 599baa489faSSeongJae Park 600baa489faSSeongJae Park scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 601baa489faSSeongJae Park (end_time.tv_nsec - start_time.tv_nsec); 602baa489faSSeongJae Park 603baa489faSSeongJae Park printf("Total size: %lu MiB\n", map_size / MB); 604baa489faSSeongJae Park printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, 605baa489faSSeongJae Park scan_time_ns % NSEC_PER_SEC); 606baa489faSSeongJae Park printf("Average speed: %.3f MiB/s\n", (map_size / MB) / 607baa489faSSeongJae Park ((double)scan_time_ns / NSEC_PER_SEC)); 608baa489faSSeongJae Park 609baa489faSSeongJae Park munmap(map_ptr_orig, len + HPAGE_SIZE); 610baa489faSSeongJae Park return KSFT_PASS; 611baa489faSSeongJae Park 612baa489faSSeongJae Park err_out: 613baa489faSSeongJae Park printf("Not OK\n"); 614baa489faSSeongJae Park munmap(map_ptr_orig, len + HPAGE_SIZE); 615baa489faSSeongJae Park return KSFT_FAIL; 616baa489faSSeongJae Park } 617baa489faSSeongJae Park 61807115fccSStefan Roesch static int ksm_merge_time(int merge_type, int mapping, int prot, int timeout, size_t map_size) 619baa489faSSeongJae Park { 620baa489faSSeongJae Park void *map_ptr; 621baa489faSSeongJae Park struct timespec start_time, end_time; 622baa489faSSeongJae Park unsigned long scan_time_ns; 623baa489faSSeongJae Park 624baa489faSSeongJae Park map_size *= MB; 625baa489faSSeongJae Park 626baa489faSSeongJae Park map_ptr = allocate_memory(NULL, prot, mapping, '*', map_size); 627baa489faSSeongJae Park if (!map_ptr) 628baa489faSSeongJae Park return KSFT_FAIL; 629baa489faSSeongJae Park 630baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 631baa489faSSeongJae Park perror("clock_gettime"); 632baa489faSSeongJae Park goto err_out; 633baa489faSSeongJae Park } 63407115fccSStefan Roesch if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout)) 635baa489faSSeongJae Park goto err_out; 636baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 637baa489faSSeongJae Park perror("clock_gettime"); 638baa489faSSeongJae Park goto err_out; 639baa489faSSeongJae Park } 640baa489faSSeongJae Park 641baa489faSSeongJae Park scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 642baa489faSSeongJae Park (end_time.tv_nsec - start_time.tv_nsec); 643baa489faSSeongJae Park 644baa489faSSeongJae Park printf("Total size: %lu MiB\n", map_size / MB); 645baa489faSSeongJae Park printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, 646baa489faSSeongJae Park scan_time_ns % NSEC_PER_SEC); 647baa489faSSeongJae Park printf("Average speed: %.3f MiB/s\n", (map_size / MB) / 648baa489faSSeongJae Park ((double)scan_time_ns / NSEC_PER_SEC)); 649baa489faSSeongJae Park 650baa489faSSeongJae Park munmap(map_ptr, map_size); 651baa489faSSeongJae Park return KSFT_PASS; 652baa489faSSeongJae Park 653baa489faSSeongJae Park err_out: 654baa489faSSeongJae Park printf("Not OK\n"); 655baa489faSSeongJae Park munmap(map_ptr, map_size); 656baa489faSSeongJae Park return KSFT_FAIL; 657baa489faSSeongJae Park } 658baa489faSSeongJae Park 65907115fccSStefan Roesch static int ksm_unmerge_time(int merge_type, int mapping, int prot, int timeout, size_t map_size) 660baa489faSSeongJae Park { 661baa489faSSeongJae Park void *map_ptr; 662baa489faSSeongJae Park struct timespec start_time, end_time; 663baa489faSSeongJae Park unsigned long scan_time_ns; 664baa489faSSeongJae Park 665baa489faSSeongJae Park map_size *= MB; 666baa489faSSeongJae Park 667baa489faSSeongJae Park map_ptr = allocate_memory(NULL, prot, mapping, '*', map_size); 668baa489faSSeongJae Park if (!map_ptr) 669baa489faSSeongJae Park return KSFT_FAIL; 670baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 671baa489faSSeongJae Park perror("clock_gettime"); 672baa489faSSeongJae Park goto err_out; 673baa489faSSeongJae Park } 67407115fccSStefan Roesch if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout)) 675baa489faSSeongJae Park goto err_out; 676baa489faSSeongJae Park 677baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 678baa489faSSeongJae Park perror("clock_gettime"); 679baa489faSSeongJae Park goto err_out; 680baa489faSSeongJae Park } 681baa489faSSeongJae Park if (ksm_unmerge_pages(map_ptr, map_size, start_time, timeout)) 682baa489faSSeongJae Park goto err_out; 683baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 684baa489faSSeongJae Park perror("clock_gettime"); 685baa489faSSeongJae Park goto err_out; 686baa489faSSeongJae Park } 687baa489faSSeongJae Park 688baa489faSSeongJae Park scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 689baa489faSSeongJae Park (end_time.tv_nsec - start_time.tv_nsec); 690baa489faSSeongJae Park 691baa489faSSeongJae Park printf("Total size: %lu MiB\n", map_size / MB); 692baa489faSSeongJae Park printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, 693baa489faSSeongJae Park scan_time_ns % NSEC_PER_SEC); 694baa489faSSeongJae Park printf("Average speed: %.3f MiB/s\n", (map_size / MB) / 695baa489faSSeongJae Park ((double)scan_time_ns / NSEC_PER_SEC)); 696baa489faSSeongJae Park 697baa489faSSeongJae Park munmap(map_ptr, map_size); 698baa489faSSeongJae Park return KSFT_PASS; 699baa489faSSeongJae Park 700baa489faSSeongJae Park err_out: 701baa489faSSeongJae Park printf("Not OK\n"); 702baa489faSSeongJae Park munmap(map_ptr, map_size); 703baa489faSSeongJae Park return KSFT_FAIL; 704baa489faSSeongJae Park } 705baa489faSSeongJae Park 70607115fccSStefan Roesch static int ksm_cow_time(int merge_type, int mapping, int prot, int timeout, size_t page_size) 707baa489faSSeongJae Park { 708baa489faSSeongJae Park void *map_ptr; 709baa489faSSeongJae Park struct timespec start_time, end_time; 710baa489faSSeongJae Park unsigned long cow_time_ns; 711baa489faSSeongJae Park 712baa489faSSeongJae Park /* page_count must be less than 2*page_size */ 713baa489faSSeongJae Park size_t page_count = 4000; 714baa489faSSeongJae Park 715baa489faSSeongJae Park map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count); 716baa489faSSeongJae Park if (!map_ptr) 717baa489faSSeongJae Park return KSFT_FAIL; 718baa489faSSeongJae Park 719baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 720baa489faSSeongJae Park perror("clock_gettime"); 721baa489faSSeongJae Park return KSFT_FAIL; 722baa489faSSeongJae Park } 723baa489faSSeongJae Park for (size_t i = 0; i < page_count - 1; i = i + 2) 724baa489faSSeongJae Park memset(map_ptr + page_size * i, '-', 1); 725baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 726baa489faSSeongJae Park perror("clock_gettime"); 727baa489faSSeongJae Park return KSFT_FAIL; 728baa489faSSeongJae Park } 729baa489faSSeongJae Park 730baa489faSSeongJae Park cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 731baa489faSSeongJae Park (end_time.tv_nsec - start_time.tv_nsec); 732baa489faSSeongJae Park 733baa489faSSeongJae Park printf("Total size: %lu MiB\n\n", (page_size * page_count) / MB); 734baa489faSSeongJae Park printf("Not merged pages:\n"); 735baa489faSSeongJae Park printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC, 736baa489faSSeongJae Park cow_time_ns % NSEC_PER_SEC); 737baa489faSSeongJae Park printf("Average speed: %.3f MiB/s\n\n", ((page_size * (page_count / 2)) / MB) / 738baa489faSSeongJae Park ((double)cow_time_ns / NSEC_PER_SEC)); 739baa489faSSeongJae Park 740baa489faSSeongJae Park /* Create 2000 pairs of duplicate pages */ 741baa489faSSeongJae Park for (size_t i = 0; i < page_count - 1; i = i + 2) { 742baa489faSSeongJae Park memset(map_ptr + page_size * i, '+', i / 2 + 1); 743baa489faSSeongJae Park memset(map_ptr + page_size * (i + 1), '+', i / 2 + 1); 744baa489faSSeongJae Park } 74507115fccSStefan Roesch if (ksm_merge_pages(merge_type, map_ptr, page_size * page_count, start_time, timeout)) 746baa489faSSeongJae Park goto err_out; 747baa489faSSeongJae Park 748baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 749baa489faSSeongJae Park perror("clock_gettime"); 750baa489faSSeongJae Park goto err_out; 751baa489faSSeongJae Park } 752baa489faSSeongJae Park for (size_t i = 0; i < page_count - 1; i = i + 2) 753baa489faSSeongJae Park memset(map_ptr + page_size * i, '-', 1); 754baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 755baa489faSSeongJae Park perror("clock_gettime"); 756baa489faSSeongJae Park goto err_out; 757baa489faSSeongJae Park } 758baa489faSSeongJae Park 759baa489faSSeongJae Park cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 760baa489faSSeongJae Park (end_time.tv_nsec - start_time.tv_nsec); 761baa489faSSeongJae Park 762baa489faSSeongJae Park printf("Merged pages:\n"); 763baa489faSSeongJae Park printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC, 764baa489faSSeongJae Park cow_time_ns % NSEC_PER_SEC); 765baa489faSSeongJae Park printf("Average speed: %.3f MiB/s\n", ((page_size * (page_count / 2)) / MB) / 766baa489faSSeongJae Park ((double)cow_time_ns / NSEC_PER_SEC)); 767baa489faSSeongJae Park 768baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 769baa489faSSeongJae Park return KSFT_PASS; 770baa489faSSeongJae Park 771baa489faSSeongJae Park err_out: 772baa489faSSeongJae Park printf("Not OK\n"); 773baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 774baa489faSSeongJae Park return KSFT_FAIL; 775baa489faSSeongJae Park } 776baa489faSSeongJae Park 777baa489faSSeongJae Park int main(int argc, char *argv[]) 778baa489faSSeongJae Park { 779baa489faSSeongJae Park int ret, opt; 780baa489faSSeongJae Park int prot = 0; 781baa489faSSeongJae Park int ksm_scan_limit_sec = KSM_SCAN_LIMIT_SEC_DEFAULT; 78207115fccSStefan Roesch int merge_type = KSM_MERGE_TYPE_DEFAULT; 783baa489faSSeongJae Park long page_count = KSM_PAGE_COUNT_DEFAULT; 784baa489faSSeongJae Park size_t page_size = sysconf(_SC_PAGESIZE); 785baa489faSSeongJae Park struct ksm_sysfs ksm_sysfs_old; 786baa489faSSeongJae Park int test_name = CHECK_KSM_MERGE; 787baa489faSSeongJae Park bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT; 788baa489faSSeongJae Park bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT; 789baa489faSSeongJae Park long size_MB = 0; 790baa489faSSeongJae Park 79107115fccSStefan Roesch while ((opt = getopt(argc, argv, "dha:p:l:z:m:s:t:MUZNPCHD")) != -1) { 792baa489faSSeongJae Park switch (opt) { 793baa489faSSeongJae Park case 'a': 794baa489faSSeongJae Park prot = str_to_prot(optarg); 795baa489faSSeongJae Park break; 796baa489faSSeongJae Park case 'p': 797baa489faSSeongJae Park page_count = atol(optarg); 798baa489faSSeongJae Park if (page_count <= 0) { 799baa489faSSeongJae Park printf("The number of pages must be greater than 0\n"); 800baa489faSSeongJae Park return KSFT_FAIL; 801baa489faSSeongJae Park } 802baa489faSSeongJae Park break; 803baa489faSSeongJae Park case 'l': 804baa489faSSeongJae Park ksm_scan_limit_sec = atoi(optarg); 805baa489faSSeongJae Park if (ksm_scan_limit_sec <= 0) { 806baa489faSSeongJae Park printf("Timeout value must be greater than 0\n"); 807baa489faSSeongJae Park return KSFT_FAIL; 808baa489faSSeongJae Park } 809baa489faSSeongJae Park break; 810baa489faSSeongJae Park case 'h': 811baa489faSSeongJae Park print_help(); 812baa489faSSeongJae Park break; 813baa489faSSeongJae Park case 'z': 814baa489faSSeongJae Park if (strcmp(optarg, "0") == 0) 815baa489faSSeongJae Park use_zero_pages = 0; 816baa489faSSeongJae Park else 817baa489faSSeongJae Park use_zero_pages = 1; 818baa489faSSeongJae Park break; 819baa489faSSeongJae Park case 'm': 820baa489faSSeongJae Park if (strcmp(optarg, "0") == 0) 821baa489faSSeongJae Park merge_across_nodes = 0; 822baa489faSSeongJae Park else 823baa489faSSeongJae Park merge_across_nodes = 1; 824baa489faSSeongJae Park break; 82507115fccSStefan Roesch case 'd': 82607115fccSStefan Roesch debug = 1; 82707115fccSStefan Roesch break; 828baa489faSSeongJae Park case 's': 829baa489faSSeongJae Park size_MB = atoi(optarg); 830baa489faSSeongJae Park if (size_MB <= 0) { 831baa489faSSeongJae Park printf("Size must be greater than 0\n"); 832baa489faSSeongJae Park return KSFT_FAIL; 833baa489faSSeongJae Park } 83465294de3SAyush Jain break; 83507115fccSStefan Roesch case 't': 83607115fccSStefan Roesch { 83707115fccSStefan Roesch int tmp = atoi(optarg); 83807115fccSStefan Roesch 83907115fccSStefan Roesch if (tmp < 0 || tmp > KSM_MERGE_LAST) { 84007115fccSStefan Roesch printf("Invalid merge type\n"); 84107115fccSStefan Roesch return KSFT_FAIL; 84207115fccSStefan Roesch } 84307115fccSStefan Roesch merge_type = tmp; 84407115fccSStefan Roesch } 84507115fccSStefan Roesch break; 846baa489faSSeongJae Park case 'M': 847baa489faSSeongJae Park break; 848baa489faSSeongJae Park case 'U': 849baa489faSSeongJae Park test_name = CHECK_KSM_UNMERGE; 850baa489faSSeongJae Park break; 851baa489faSSeongJae Park case 'Z': 852baa489faSSeongJae Park test_name = CHECK_KSM_ZERO_PAGE_MERGE; 853baa489faSSeongJae Park break; 854baa489faSSeongJae Park case 'N': 855baa489faSSeongJae Park test_name = CHECK_KSM_NUMA_MERGE; 856baa489faSSeongJae Park break; 857baa489faSSeongJae Park case 'P': 858baa489faSSeongJae Park test_name = KSM_MERGE_TIME; 859baa489faSSeongJae Park break; 860baa489faSSeongJae Park case 'H': 861baa489faSSeongJae Park test_name = KSM_MERGE_TIME_HUGE_PAGES; 862baa489faSSeongJae Park break; 863baa489faSSeongJae Park case 'D': 864baa489faSSeongJae Park test_name = KSM_UNMERGE_TIME; 865baa489faSSeongJae Park break; 866baa489faSSeongJae Park case 'C': 867baa489faSSeongJae Park test_name = KSM_COW_TIME; 868baa489faSSeongJae Park break; 869baa489faSSeongJae Park default: 870baa489faSSeongJae Park return KSFT_FAIL; 871baa489faSSeongJae Park } 872baa489faSSeongJae Park } 873baa489faSSeongJae Park 874baa489faSSeongJae Park if (prot == 0) 875baa489faSSeongJae Park prot = str_to_prot(KSM_PROT_STR_DEFAULT); 876baa489faSSeongJae Park 877baa489faSSeongJae Park if (access(KSM_SYSFS_PATH, F_OK)) { 878baa489faSSeongJae Park printf("Config KSM not enabled\n"); 879baa489faSSeongJae Park return KSFT_SKIP; 880baa489faSSeongJae Park } 881baa489faSSeongJae Park 882baa489faSSeongJae Park if (ksm_save_def(&ksm_sysfs_old)) { 883baa489faSSeongJae Park printf("Cannot save default tunables\n"); 884baa489faSSeongJae Park return KSFT_FAIL; 885baa489faSSeongJae Park } 886baa489faSSeongJae Park 887baa489faSSeongJae Park if (ksm_write_sysfs(KSM_FP("run"), 2) || 888baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("sleep_millisecs"), 0) || 889baa489faSSeongJae Park numa_available() ? 0 : 890baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("merge_across_nodes"), 1) || 891baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("pages_to_scan"), page_count)) 892baa489faSSeongJae Park return KSFT_FAIL; 893baa489faSSeongJae Park 894baa489faSSeongJae Park switch (test_name) { 895baa489faSSeongJae Park case CHECK_KSM_MERGE: 89607115fccSStefan Roesch ret = check_ksm_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count, 897baa489faSSeongJae Park ksm_scan_limit_sec, page_size); 898baa489faSSeongJae Park break; 899baa489faSSeongJae Park case CHECK_KSM_UNMERGE: 90007115fccSStefan Roesch ret = check_ksm_unmerge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, 90107115fccSStefan Roesch ksm_scan_limit_sec, page_size); 902baa489faSSeongJae Park break; 903baa489faSSeongJae Park case CHECK_KSM_ZERO_PAGE_MERGE: 90407115fccSStefan Roesch ret = check_ksm_zero_page_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, 90507115fccSStefan Roesch page_count, ksm_scan_limit_sec, use_zero_pages, 90607115fccSStefan Roesch page_size); 907baa489faSSeongJae Park break; 908baa489faSSeongJae Park case CHECK_KSM_NUMA_MERGE: 90907115fccSStefan Roesch ret = check_ksm_numa_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, 91007115fccSStefan Roesch ksm_scan_limit_sec, merge_across_nodes, page_size); 911baa489faSSeongJae Park break; 912baa489faSSeongJae Park case KSM_MERGE_TIME: 913baa489faSSeongJae Park if (size_MB == 0) { 914baa489faSSeongJae Park printf("Option '-s' is required.\n"); 915baa489faSSeongJae Park return KSFT_FAIL; 916baa489faSSeongJae Park } 91707115fccSStefan Roesch ret = ksm_merge_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, 91807115fccSStefan Roesch ksm_scan_limit_sec, size_MB); 919baa489faSSeongJae Park break; 920baa489faSSeongJae Park case KSM_MERGE_TIME_HUGE_PAGES: 921baa489faSSeongJae Park if (size_MB == 0) { 922baa489faSSeongJae Park printf("Option '-s' is required.\n"); 923baa489faSSeongJae Park return KSFT_FAIL; 924baa489faSSeongJae Park } 92507115fccSStefan Roesch ret = ksm_merge_hugepages_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, 926baa489faSSeongJae Park ksm_scan_limit_sec, size_MB); 927baa489faSSeongJae Park break; 928baa489faSSeongJae Park case KSM_UNMERGE_TIME: 929baa489faSSeongJae Park if (size_MB == 0) { 930baa489faSSeongJae Park printf("Option '-s' is required.\n"); 931baa489faSSeongJae Park return KSFT_FAIL; 932baa489faSSeongJae Park } 93307115fccSStefan Roesch ret = ksm_unmerge_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, 934baa489faSSeongJae Park ksm_scan_limit_sec, size_MB); 935baa489faSSeongJae Park break; 936baa489faSSeongJae Park case KSM_COW_TIME: 93707115fccSStefan Roesch ret = ksm_cow_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, 93807115fccSStefan Roesch ksm_scan_limit_sec, page_size); 939baa489faSSeongJae Park break; 940baa489faSSeongJae Park } 941baa489faSSeongJae Park 942baa489faSSeongJae Park if (ksm_restore(&ksm_sysfs_old)) { 943baa489faSSeongJae Park printf("Cannot restore default tunables\n"); 944baa489faSSeongJae Park return KSFT_FAIL; 945baa489faSSeongJae Park } 946baa489faSSeongJae Park 947baa489faSSeongJae Park return ret; 948baa489faSSeongJae Park } 949