1baa489faSSeongJae Park // SPDX-License-Identifier: GPL-2.0 2baa489faSSeongJae Park 3baa489faSSeongJae Park #include <sys/mman.h> 4baa489faSSeongJae Park #include <stdbool.h> 5baa489faSSeongJae Park #include <time.h> 6baa489faSSeongJae Park #include <string.h> 7baa489faSSeongJae Park #include <numa.h> 8baa489faSSeongJae Park #include <unistd.h> 9baa489faSSeongJae Park #include <fcntl.h> 10baa489faSSeongJae Park #include <stdint.h> 11baa489faSSeongJae Park #include <err.h> 12baa489faSSeongJae Park 13baa489faSSeongJae Park #include "../kselftest.h" 14baa489faSSeongJae Park #include <include/vdso/time64.h> 15*af605d26SPeter Xu #include "vm_util.h" 16baa489faSSeongJae Park 17baa489faSSeongJae Park #define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/" 18baa489faSSeongJae Park #define KSM_FP(s) (KSM_SYSFS_PATH s) 19baa489faSSeongJae Park #define KSM_SCAN_LIMIT_SEC_DEFAULT 120 20baa489faSSeongJae Park #define KSM_PAGE_COUNT_DEFAULT 10l 21baa489faSSeongJae Park #define KSM_PROT_STR_DEFAULT "rw" 22baa489faSSeongJae Park #define KSM_USE_ZERO_PAGES_DEFAULT false 23baa489faSSeongJae Park #define KSM_MERGE_ACROSS_NODES_DEFAULT true 24baa489faSSeongJae Park #define MB (1ul << 20) 25baa489faSSeongJae Park 26baa489faSSeongJae Park struct ksm_sysfs { 27baa489faSSeongJae Park unsigned long max_page_sharing; 28baa489faSSeongJae Park unsigned long merge_across_nodes; 29baa489faSSeongJae Park unsigned long pages_to_scan; 30baa489faSSeongJae Park unsigned long run; 31baa489faSSeongJae Park unsigned long sleep_millisecs; 32baa489faSSeongJae Park unsigned long stable_node_chains_prune_millisecs; 33baa489faSSeongJae Park unsigned long use_zero_pages; 34baa489faSSeongJae Park }; 35baa489faSSeongJae Park 36baa489faSSeongJae Park enum ksm_test_name { 37baa489faSSeongJae Park CHECK_KSM_MERGE, 38baa489faSSeongJae Park CHECK_KSM_UNMERGE, 39baa489faSSeongJae Park CHECK_KSM_ZERO_PAGE_MERGE, 40baa489faSSeongJae Park CHECK_KSM_NUMA_MERGE, 41baa489faSSeongJae Park KSM_MERGE_TIME, 42baa489faSSeongJae Park KSM_MERGE_TIME_HUGE_PAGES, 43baa489faSSeongJae Park KSM_UNMERGE_TIME, 44baa489faSSeongJae Park KSM_COW_TIME 45baa489faSSeongJae Park }; 46baa489faSSeongJae Park 47baa489faSSeongJae Park static int ksm_write_sysfs(const char *file_path, unsigned long val) 48baa489faSSeongJae Park { 49baa489faSSeongJae Park FILE *f = fopen(file_path, "w"); 50baa489faSSeongJae Park 51baa489faSSeongJae Park if (!f) { 52baa489faSSeongJae Park fprintf(stderr, "f %s\n", file_path); 53baa489faSSeongJae Park perror("fopen"); 54baa489faSSeongJae Park return 1; 55baa489faSSeongJae Park } 56baa489faSSeongJae Park if (fprintf(f, "%lu", val) < 0) { 57baa489faSSeongJae Park perror("fprintf"); 58baa489faSSeongJae Park fclose(f); 59baa489faSSeongJae Park return 1; 60baa489faSSeongJae Park } 61baa489faSSeongJae Park fclose(f); 62baa489faSSeongJae Park 63baa489faSSeongJae Park return 0; 64baa489faSSeongJae Park } 65baa489faSSeongJae Park 66baa489faSSeongJae Park static int ksm_read_sysfs(const char *file_path, unsigned long *val) 67baa489faSSeongJae Park { 68baa489faSSeongJae Park FILE *f = fopen(file_path, "r"); 69baa489faSSeongJae Park 70baa489faSSeongJae Park if (!f) { 71baa489faSSeongJae Park fprintf(stderr, "f %s\n", file_path); 72baa489faSSeongJae Park perror("fopen"); 73baa489faSSeongJae Park return 1; 74baa489faSSeongJae Park } 75baa489faSSeongJae Park if (fscanf(f, "%lu", val) != 1) { 76baa489faSSeongJae Park perror("fscanf"); 77baa489faSSeongJae Park fclose(f); 78baa489faSSeongJae Park return 1; 79baa489faSSeongJae Park } 80baa489faSSeongJae Park fclose(f); 81baa489faSSeongJae Park 82baa489faSSeongJae Park return 0; 83baa489faSSeongJae Park } 84baa489faSSeongJae Park 85baa489faSSeongJae Park static int str_to_prot(char *prot_str) 86baa489faSSeongJae Park { 87baa489faSSeongJae Park int prot = 0; 88baa489faSSeongJae Park 89baa489faSSeongJae Park if ((strchr(prot_str, 'r')) != NULL) 90baa489faSSeongJae Park prot |= PROT_READ; 91baa489faSSeongJae Park if ((strchr(prot_str, 'w')) != NULL) 92baa489faSSeongJae Park prot |= PROT_WRITE; 93baa489faSSeongJae Park if ((strchr(prot_str, 'x')) != NULL) 94baa489faSSeongJae Park prot |= PROT_EXEC; 95baa489faSSeongJae Park 96baa489faSSeongJae Park return prot; 97baa489faSSeongJae Park } 98baa489faSSeongJae Park 99baa489faSSeongJae Park static void print_help(void) 100baa489faSSeongJae Park { 101baa489faSSeongJae Park printf("usage: ksm_tests [-h] <test type> [-a prot] [-p page_count] [-l timeout]\n" 102baa489faSSeongJae Park "[-z use_zero_pages] [-m merge_across_nodes] [-s size]\n"); 103baa489faSSeongJae Park 104baa489faSSeongJae Park printf("Supported <test type>:\n" 105baa489faSSeongJae Park " -M (page merging)\n" 106baa489faSSeongJae Park " -Z (zero pages merging)\n" 107baa489faSSeongJae Park " -N (merging of pages in different NUMA nodes)\n" 108baa489faSSeongJae Park " -U (page unmerging)\n" 109baa489faSSeongJae Park " -P evaluate merging time and speed.\n" 110baa489faSSeongJae Park " For this test, the size of duplicated memory area (in MiB)\n" 111baa489faSSeongJae Park " must be provided using -s option\n" 112baa489faSSeongJae Park " -H evaluate merging time and speed of area allocated mostly with huge pages\n" 113baa489faSSeongJae Park " For this test, the size of duplicated memory area (in MiB)\n" 114baa489faSSeongJae Park " must be provided using -s option\n" 115baa489faSSeongJae Park " -D evaluate unmerging time and speed when disabling KSM.\n" 116baa489faSSeongJae Park " For this test, the size of duplicated memory area (in MiB)\n" 117baa489faSSeongJae Park " must be provided using -s option\n" 118baa489faSSeongJae Park " -C evaluate the time required to break COW of merged pages.\n\n"); 119baa489faSSeongJae Park 120baa489faSSeongJae Park printf(" -a: specify the access protections of pages.\n" 121baa489faSSeongJae Park " <prot> must be of the form [rwx].\n" 122baa489faSSeongJae Park " Default: %s\n", KSM_PROT_STR_DEFAULT); 123baa489faSSeongJae Park printf(" -p: specify the number of pages to test.\n" 124baa489faSSeongJae Park " Default: %ld\n", KSM_PAGE_COUNT_DEFAULT); 125baa489faSSeongJae Park printf(" -l: limit the maximum running time (in seconds) for a test.\n" 126baa489faSSeongJae Park " Default: %d seconds\n", KSM_SCAN_LIMIT_SEC_DEFAULT); 127baa489faSSeongJae Park printf(" -z: change use_zero_pages tunable\n" 128baa489faSSeongJae Park " Default: %d\n", KSM_USE_ZERO_PAGES_DEFAULT); 129baa489faSSeongJae Park printf(" -m: change merge_across_nodes tunable\n" 130baa489faSSeongJae Park " Default: %d\n", KSM_MERGE_ACROSS_NODES_DEFAULT); 131baa489faSSeongJae Park printf(" -s: the size of duplicated memory area (in MiB)\n"); 132baa489faSSeongJae Park 133baa489faSSeongJae Park exit(0); 134baa489faSSeongJae Park } 135baa489faSSeongJae Park 136baa489faSSeongJae Park static void *allocate_memory(void *ptr, int prot, int mapping, char data, size_t map_size) 137baa489faSSeongJae Park { 138baa489faSSeongJae Park void *map_ptr = mmap(ptr, map_size, PROT_WRITE, mapping, -1, 0); 139baa489faSSeongJae Park 140baa489faSSeongJae Park if (!map_ptr) { 141baa489faSSeongJae Park perror("mmap"); 142baa489faSSeongJae Park return NULL; 143baa489faSSeongJae Park } 144baa489faSSeongJae Park memset(map_ptr, data, map_size); 145baa489faSSeongJae Park if (mprotect(map_ptr, map_size, prot)) { 146baa489faSSeongJae Park perror("mprotect"); 147baa489faSSeongJae Park munmap(map_ptr, map_size); 148baa489faSSeongJae Park return NULL; 149baa489faSSeongJae Park } 150baa489faSSeongJae Park 151baa489faSSeongJae Park return map_ptr; 152baa489faSSeongJae Park } 153baa489faSSeongJae Park 154baa489faSSeongJae Park static int ksm_do_scan(int scan_count, struct timespec start_time, int timeout) 155baa489faSSeongJae Park { 156baa489faSSeongJae Park struct timespec cur_time; 157baa489faSSeongJae Park unsigned long cur_scan, init_scan; 158baa489faSSeongJae Park 159baa489faSSeongJae Park if (ksm_read_sysfs(KSM_FP("full_scans"), &init_scan)) 160baa489faSSeongJae Park return 1; 161baa489faSSeongJae Park cur_scan = init_scan; 162baa489faSSeongJae Park 163baa489faSSeongJae Park while (cur_scan < init_scan + scan_count) { 164baa489faSSeongJae Park if (ksm_read_sysfs(KSM_FP("full_scans"), &cur_scan)) 165baa489faSSeongJae Park return 1; 166baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &cur_time)) { 167baa489faSSeongJae Park perror("clock_gettime"); 168baa489faSSeongJae Park return 1; 169baa489faSSeongJae Park } 170baa489faSSeongJae Park if ((cur_time.tv_sec - start_time.tv_sec) > timeout) { 171baa489faSSeongJae Park printf("Scan time limit exceeded\n"); 172baa489faSSeongJae Park return 1; 173baa489faSSeongJae Park } 174baa489faSSeongJae Park } 175baa489faSSeongJae Park 176baa489faSSeongJae Park return 0; 177baa489faSSeongJae Park } 178baa489faSSeongJae Park 179baa489faSSeongJae Park static int ksm_merge_pages(void *addr, size_t size, struct timespec start_time, int timeout) 180baa489faSSeongJae Park { 181baa489faSSeongJae Park if (madvise(addr, size, MADV_MERGEABLE)) { 182baa489faSSeongJae Park perror("madvise"); 183baa489faSSeongJae Park return 1; 184baa489faSSeongJae Park } 185baa489faSSeongJae Park if (ksm_write_sysfs(KSM_FP("run"), 1)) 186baa489faSSeongJae Park return 1; 187baa489faSSeongJae Park 188baa489faSSeongJae Park /* Since merging occurs only after 2 scans, make sure to get at least 2 full scans */ 189baa489faSSeongJae Park if (ksm_do_scan(2, start_time, timeout)) 190baa489faSSeongJae Park return 1; 191baa489faSSeongJae Park 192baa489faSSeongJae Park return 0; 193baa489faSSeongJae Park } 194baa489faSSeongJae Park 195baa489faSSeongJae Park static int ksm_unmerge_pages(void *addr, size_t size, 196baa489faSSeongJae Park struct timespec start_time, int timeout) 197baa489faSSeongJae Park { 198baa489faSSeongJae Park if (madvise(addr, size, MADV_UNMERGEABLE)) { 199baa489faSSeongJae Park perror("madvise"); 200baa489faSSeongJae Park return 1; 201baa489faSSeongJae Park } 202baa489faSSeongJae Park return 0; 203baa489faSSeongJae Park } 204baa489faSSeongJae Park 205baa489faSSeongJae Park static bool assert_ksm_pages_count(long dupl_page_count) 206baa489faSSeongJae Park { 207baa489faSSeongJae Park unsigned long max_page_sharing, pages_sharing, pages_shared; 208baa489faSSeongJae Park 209baa489faSSeongJae Park if (ksm_read_sysfs(KSM_FP("pages_shared"), &pages_shared) || 210baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("pages_sharing"), &pages_sharing) || 211baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("max_page_sharing"), &max_page_sharing)) 212baa489faSSeongJae Park return false; 213baa489faSSeongJae Park 214baa489faSSeongJae Park /* 215baa489faSSeongJae Park * Since there must be at least 2 pages for merging and 1 page can be 216baa489faSSeongJae Park * shared with the limited number of pages (max_page_sharing), sometimes 217baa489faSSeongJae Park * there are 'leftover' pages that cannot be merged. For example, if there 218baa489faSSeongJae Park * are 11 pages and max_page_sharing = 10, then only 10 pages will be 219baa489faSSeongJae Park * merged and the 11th page won't be affected. As a result, when the number 220baa489faSSeongJae Park * of duplicate pages is divided by max_page_sharing and the remainder is 1, 221baa489faSSeongJae Park * pages_shared and pages_sharing values will be equal between dupl_page_count 222baa489faSSeongJae Park * and dupl_page_count - 1. 223baa489faSSeongJae Park */ 224baa489faSSeongJae Park if (dupl_page_count % max_page_sharing == 1 || dupl_page_count % max_page_sharing == 0) { 225baa489faSSeongJae Park if (pages_shared == dupl_page_count / max_page_sharing && 226baa489faSSeongJae Park pages_sharing == pages_shared * (max_page_sharing - 1)) 227baa489faSSeongJae Park return true; 228baa489faSSeongJae Park } else { 229baa489faSSeongJae Park if (pages_shared == (dupl_page_count / max_page_sharing + 1) && 230baa489faSSeongJae Park pages_sharing == dupl_page_count - pages_shared) 231baa489faSSeongJae Park return true; 232baa489faSSeongJae Park } 233baa489faSSeongJae Park 234baa489faSSeongJae Park return false; 235baa489faSSeongJae Park } 236baa489faSSeongJae Park 237baa489faSSeongJae Park static int ksm_save_def(struct ksm_sysfs *ksm_sysfs) 238baa489faSSeongJae Park { 239baa489faSSeongJae Park if (ksm_read_sysfs(KSM_FP("max_page_sharing"), &ksm_sysfs->max_page_sharing) || 240baa489faSSeongJae Park numa_available() ? 0 : 241baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("merge_across_nodes"), &ksm_sysfs->merge_across_nodes) || 242baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("sleep_millisecs"), &ksm_sysfs->sleep_millisecs) || 243baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("pages_to_scan"), &ksm_sysfs->pages_to_scan) || 244baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("run"), &ksm_sysfs->run) || 245baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("stable_node_chains_prune_millisecs"), 246baa489faSSeongJae Park &ksm_sysfs->stable_node_chains_prune_millisecs) || 247baa489faSSeongJae Park ksm_read_sysfs(KSM_FP("use_zero_pages"), &ksm_sysfs->use_zero_pages)) 248baa489faSSeongJae Park return 1; 249baa489faSSeongJae Park 250baa489faSSeongJae Park return 0; 251baa489faSSeongJae Park } 252baa489faSSeongJae Park 253baa489faSSeongJae Park static int ksm_restore(struct ksm_sysfs *ksm_sysfs) 254baa489faSSeongJae Park { 255baa489faSSeongJae Park if (ksm_write_sysfs(KSM_FP("max_page_sharing"), ksm_sysfs->max_page_sharing) || 256baa489faSSeongJae Park numa_available() ? 0 : 257baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("merge_across_nodes"), ksm_sysfs->merge_across_nodes) || 258baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("pages_to_scan"), ksm_sysfs->pages_to_scan) || 259baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("run"), ksm_sysfs->run) || 260baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("sleep_millisecs"), ksm_sysfs->sleep_millisecs) || 261baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("stable_node_chains_prune_millisecs"), 262baa489faSSeongJae Park ksm_sysfs->stable_node_chains_prune_millisecs) || 263baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("use_zero_pages"), ksm_sysfs->use_zero_pages)) 264baa489faSSeongJae Park return 1; 265baa489faSSeongJae Park 266baa489faSSeongJae Park return 0; 267baa489faSSeongJae Park } 268baa489faSSeongJae Park 269baa489faSSeongJae Park static int check_ksm_merge(int mapping, int prot, long page_count, int timeout, size_t page_size) 270baa489faSSeongJae Park { 271baa489faSSeongJae Park void *map_ptr; 272baa489faSSeongJae Park struct timespec start_time; 273baa489faSSeongJae Park 274baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 275baa489faSSeongJae Park perror("clock_gettime"); 276baa489faSSeongJae Park return KSFT_FAIL; 277baa489faSSeongJae Park } 278baa489faSSeongJae Park 279baa489faSSeongJae Park /* fill pages with the same data and merge them */ 280baa489faSSeongJae Park map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count); 281baa489faSSeongJae Park if (!map_ptr) 282baa489faSSeongJae Park return KSFT_FAIL; 283baa489faSSeongJae Park 284baa489faSSeongJae Park if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout)) 285baa489faSSeongJae Park goto err_out; 286baa489faSSeongJae Park 287baa489faSSeongJae Park /* verify that the right number of pages are merged */ 288baa489faSSeongJae Park if (assert_ksm_pages_count(page_count)) { 289baa489faSSeongJae Park printf("OK\n"); 290baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 291baa489faSSeongJae Park return KSFT_PASS; 292baa489faSSeongJae Park } 293baa489faSSeongJae Park 294baa489faSSeongJae Park err_out: 295baa489faSSeongJae Park printf("Not OK\n"); 296baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 297baa489faSSeongJae Park return KSFT_FAIL; 298baa489faSSeongJae Park } 299baa489faSSeongJae Park 300baa489faSSeongJae Park static int check_ksm_unmerge(int mapping, int prot, int timeout, size_t page_size) 301baa489faSSeongJae Park { 302baa489faSSeongJae Park void *map_ptr; 303baa489faSSeongJae Park struct timespec start_time; 304baa489faSSeongJae Park int page_count = 2; 305baa489faSSeongJae Park 306baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 307baa489faSSeongJae Park perror("clock_gettime"); 308baa489faSSeongJae Park return KSFT_FAIL; 309baa489faSSeongJae Park } 310baa489faSSeongJae Park 311baa489faSSeongJae Park /* fill pages with the same data and merge them */ 312baa489faSSeongJae Park map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count); 313baa489faSSeongJae Park if (!map_ptr) 314baa489faSSeongJae Park return KSFT_FAIL; 315baa489faSSeongJae Park 316baa489faSSeongJae Park if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout)) 317baa489faSSeongJae Park goto err_out; 318baa489faSSeongJae Park 319baa489faSSeongJae Park /* change 1 byte in each of the 2 pages -- KSM must automatically unmerge them */ 320baa489faSSeongJae Park memset(map_ptr, '-', 1); 321baa489faSSeongJae Park memset(map_ptr + page_size, '+', 1); 322baa489faSSeongJae Park 323baa489faSSeongJae Park /* get at least 1 scan, so KSM can detect that the pages were modified */ 324baa489faSSeongJae Park if (ksm_do_scan(1, start_time, timeout)) 325baa489faSSeongJae Park goto err_out; 326baa489faSSeongJae Park 327baa489faSSeongJae Park /* check that unmerging was successful and 0 pages are currently merged */ 328baa489faSSeongJae Park if (assert_ksm_pages_count(0)) { 329baa489faSSeongJae Park printf("OK\n"); 330baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 331baa489faSSeongJae Park return KSFT_PASS; 332baa489faSSeongJae Park } 333baa489faSSeongJae Park 334baa489faSSeongJae Park err_out: 335baa489faSSeongJae Park printf("Not OK\n"); 336baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 337baa489faSSeongJae Park return KSFT_FAIL; 338baa489faSSeongJae Park } 339baa489faSSeongJae Park 340baa489faSSeongJae Park static int check_ksm_zero_page_merge(int mapping, int prot, long page_count, int timeout, 341baa489faSSeongJae Park bool use_zero_pages, size_t page_size) 342baa489faSSeongJae Park { 343baa489faSSeongJae Park void *map_ptr; 344baa489faSSeongJae Park struct timespec start_time; 345baa489faSSeongJae Park 346baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 347baa489faSSeongJae Park perror("clock_gettime"); 348baa489faSSeongJae Park return KSFT_FAIL; 349baa489faSSeongJae Park } 350baa489faSSeongJae Park 351baa489faSSeongJae Park if (ksm_write_sysfs(KSM_FP("use_zero_pages"), use_zero_pages)) 352baa489faSSeongJae Park return KSFT_FAIL; 353baa489faSSeongJae Park 354baa489faSSeongJae Park /* fill pages with zero and try to merge them */ 355baa489faSSeongJae Park map_ptr = allocate_memory(NULL, prot, mapping, 0, page_size * page_count); 356baa489faSSeongJae Park if (!map_ptr) 357baa489faSSeongJae Park return KSFT_FAIL; 358baa489faSSeongJae Park 359baa489faSSeongJae Park if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout)) 360baa489faSSeongJae Park goto err_out; 361baa489faSSeongJae Park 362baa489faSSeongJae Park /* 363baa489faSSeongJae Park * verify that the right number of pages are merged: 364baa489faSSeongJae Park * 1) if use_zero_pages is set to 1, empty pages are merged 365baa489faSSeongJae Park * with the kernel zero page instead of with each other; 366baa489faSSeongJae Park * 2) if use_zero_pages is set to 0, empty pages are not treated specially 367baa489faSSeongJae Park * and merged as usual. 368baa489faSSeongJae Park */ 369baa489faSSeongJae Park if (use_zero_pages && !assert_ksm_pages_count(0)) 370baa489faSSeongJae Park goto err_out; 371baa489faSSeongJae Park else if (!use_zero_pages && !assert_ksm_pages_count(page_count)) 372baa489faSSeongJae Park goto err_out; 373baa489faSSeongJae Park 374baa489faSSeongJae Park printf("OK\n"); 375baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 376baa489faSSeongJae Park return KSFT_PASS; 377baa489faSSeongJae Park 378baa489faSSeongJae Park err_out: 379baa489faSSeongJae Park printf("Not OK\n"); 380baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 381baa489faSSeongJae Park return KSFT_FAIL; 382baa489faSSeongJae Park } 383baa489faSSeongJae Park 384baa489faSSeongJae Park static int get_next_mem_node(int node) 385baa489faSSeongJae Park { 386baa489faSSeongJae Park 387baa489faSSeongJae Park long node_size; 388baa489faSSeongJae Park int mem_node = 0; 389baa489faSSeongJae Park int i, max_node = numa_max_node(); 390baa489faSSeongJae Park 391baa489faSSeongJae Park for (i = node + 1; i <= max_node + node; i++) { 392baa489faSSeongJae Park mem_node = i % (max_node + 1); 393baa489faSSeongJae Park node_size = numa_node_size(mem_node, NULL); 394baa489faSSeongJae Park if (node_size > 0) 395baa489faSSeongJae Park break; 396baa489faSSeongJae Park } 397baa489faSSeongJae Park return mem_node; 398baa489faSSeongJae Park } 399baa489faSSeongJae Park 400baa489faSSeongJae Park static int get_first_mem_node(void) 401baa489faSSeongJae Park { 402baa489faSSeongJae Park return get_next_mem_node(numa_max_node()); 403baa489faSSeongJae Park } 404baa489faSSeongJae Park 405baa489faSSeongJae Park static int check_ksm_numa_merge(int mapping, int prot, int timeout, bool merge_across_nodes, 406baa489faSSeongJae Park size_t page_size) 407baa489faSSeongJae Park { 408baa489faSSeongJae Park void *numa1_map_ptr, *numa2_map_ptr; 409baa489faSSeongJae Park struct timespec start_time; 410baa489faSSeongJae Park int page_count = 2; 411baa489faSSeongJae Park int first_node; 412baa489faSSeongJae Park 413baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 414baa489faSSeongJae Park perror("clock_gettime"); 415baa489faSSeongJae Park return KSFT_FAIL; 416baa489faSSeongJae Park } 417baa489faSSeongJae Park 418baa489faSSeongJae Park if (numa_available() < 0) { 419baa489faSSeongJae Park perror("NUMA support not enabled"); 420baa489faSSeongJae Park return KSFT_SKIP; 421baa489faSSeongJae Park } 422baa489faSSeongJae Park if (numa_num_configured_nodes() <= 1) { 423baa489faSSeongJae Park printf("At least 2 NUMA nodes must be available\n"); 424baa489faSSeongJae Park return KSFT_SKIP; 425baa489faSSeongJae Park } 426baa489faSSeongJae Park if (ksm_write_sysfs(KSM_FP("merge_across_nodes"), merge_across_nodes)) 427baa489faSSeongJae Park return KSFT_FAIL; 428baa489faSSeongJae Park 429baa489faSSeongJae Park /* allocate 2 pages in 2 different NUMA nodes and fill them with the same data */ 430baa489faSSeongJae Park first_node = get_first_mem_node(); 431baa489faSSeongJae Park numa1_map_ptr = numa_alloc_onnode(page_size, first_node); 432baa489faSSeongJae Park numa2_map_ptr = numa_alloc_onnode(page_size, get_next_mem_node(first_node)); 433baa489faSSeongJae Park if (!numa1_map_ptr || !numa2_map_ptr) { 434baa489faSSeongJae Park perror("numa_alloc_onnode"); 435baa489faSSeongJae Park return KSFT_FAIL; 436baa489faSSeongJae Park } 437baa489faSSeongJae Park 438baa489faSSeongJae Park memset(numa1_map_ptr, '*', page_size); 439baa489faSSeongJae Park memset(numa2_map_ptr, '*', page_size); 440baa489faSSeongJae Park 441baa489faSSeongJae Park /* try to merge the pages */ 442baa489faSSeongJae Park if (ksm_merge_pages(numa1_map_ptr, page_size, start_time, timeout) || 443baa489faSSeongJae Park ksm_merge_pages(numa2_map_ptr, page_size, start_time, timeout)) 444baa489faSSeongJae Park goto err_out; 445baa489faSSeongJae Park 446baa489faSSeongJae Park /* 447baa489faSSeongJae Park * verify that the right number of pages are merged: 448baa489faSSeongJae Park * 1) if merge_across_nodes was enabled, 2 duplicate pages will be merged; 449baa489faSSeongJae Park * 2) if merge_across_nodes = 0, there must be 0 merged pages, since there is 450baa489faSSeongJae Park * only 1 unique page in each node and they can't be shared. 451baa489faSSeongJae Park */ 452baa489faSSeongJae Park if (merge_across_nodes && !assert_ksm_pages_count(page_count)) 453baa489faSSeongJae Park goto err_out; 454baa489faSSeongJae Park else if (!merge_across_nodes && !assert_ksm_pages_count(0)) 455baa489faSSeongJae Park goto err_out; 456baa489faSSeongJae Park 457baa489faSSeongJae Park numa_free(numa1_map_ptr, page_size); 458baa489faSSeongJae Park numa_free(numa2_map_ptr, page_size); 459baa489faSSeongJae Park printf("OK\n"); 460baa489faSSeongJae Park return KSFT_PASS; 461baa489faSSeongJae Park 462baa489faSSeongJae Park err_out: 463baa489faSSeongJae Park numa_free(numa1_map_ptr, page_size); 464baa489faSSeongJae Park numa_free(numa2_map_ptr, page_size); 465baa489faSSeongJae Park printf("Not OK\n"); 466baa489faSSeongJae Park return KSFT_FAIL; 467baa489faSSeongJae Park } 468baa489faSSeongJae Park 469baa489faSSeongJae Park static int ksm_merge_hugepages_time(int mapping, int prot, int timeout, size_t map_size) 470baa489faSSeongJae Park { 471baa489faSSeongJae Park void *map_ptr, *map_ptr_orig; 472baa489faSSeongJae Park struct timespec start_time, end_time; 473baa489faSSeongJae Park unsigned long scan_time_ns; 474baa489faSSeongJae Park int pagemap_fd, n_normal_pages, n_huge_pages; 475baa489faSSeongJae Park 476baa489faSSeongJae Park map_size *= MB; 477baa489faSSeongJae Park size_t len = map_size; 478baa489faSSeongJae Park 479baa489faSSeongJae Park len -= len % HPAGE_SIZE; 480baa489faSSeongJae Park map_ptr_orig = mmap(NULL, len + HPAGE_SIZE, PROT_READ | PROT_WRITE, 481baa489faSSeongJae Park MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE, -1, 0); 482baa489faSSeongJae Park map_ptr = map_ptr_orig + HPAGE_SIZE - (uintptr_t)map_ptr_orig % HPAGE_SIZE; 483baa489faSSeongJae Park 484baa489faSSeongJae Park if (map_ptr_orig == MAP_FAILED) 485baa489faSSeongJae Park err(2, "initial mmap"); 486baa489faSSeongJae Park 487baa489faSSeongJae Park if (madvise(map_ptr, len + HPAGE_SIZE, MADV_HUGEPAGE)) 488baa489faSSeongJae Park err(2, "MADV_HUGEPAGE"); 489baa489faSSeongJae Park 490baa489faSSeongJae Park pagemap_fd = open("/proc/self/pagemap", O_RDONLY); 491baa489faSSeongJae Park if (pagemap_fd < 0) 492baa489faSSeongJae Park err(2, "open pagemap"); 493baa489faSSeongJae Park 494baa489faSSeongJae Park n_normal_pages = 0; 495baa489faSSeongJae Park n_huge_pages = 0; 496baa489faSSeongJae Park for (void *p = map_ptr; p < map_ptr + len; p += HPAGE_SIZE) { 497baa489faSSeongJae Park if (allocate_transhuge(p, pagemap_fd) < 0) 498baa489faSSeongJae Park n_normal_pages++; 499baa489faSSeongJae Park else 500baa489faSSeongJae Park n_huge_pages++; 501baa489faSSeongJae Park } 502baa489faSSeongJae Park printf("Number of normal pages: %d\n", n_normal_pages); 503baa489faSSeongJae Park printf("Number of huge pages: %d\n", n_huge_pages); 504baa489faSSeongJae Park 505baa489faSSeongJae Park memset(map_ptr, '*', len); 506baa489faSSeongJae Park 507baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 508baa489faSSeongJae Park perror("clock_gettime"); 509baa489faSSeongJae Park goto err_out; 510baa489faSSeongJae Park } 511baa489faSSeongJae Park if (ksm_merge_pages(map_ptr, map_size, start_time, timeout)) 512baa489faSSeongJae Park goto err_out; 513baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 514baa489faSSeongJae Park perror("clock_gettime"); 515baa489faSSeongJae Park goto err_out; 516baa489faSSeongJae Park } 517baa489faSSeongJae Park 518baa489faSSeongJae Park scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 519baa489faSSeongJae Park (end_time.tv_nsec - start_time.tv_nsec); 520baa489faSSeongJae Park 521baa489faSSeongJae Park printf("Total size: %lu MiB\n", map_size / MB); 522baa489faSSeongJae Park printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, 523baa489faSSeongJae Park scan_time_ns % NSEC_PER_SEC); 524baa489faSSeongJae Park printf("Average speed: %.3f MiB/s\n", (map_size / MB) / 525baa489faSSeongJae Park ((double)scan_time_ns / NSEC_PER_SEC)); 526baa489faSSeongJae Park 527baa489faSSeongJae Park munmap(map_ptr_orig, len + HPAGE_SIZE); 528baa489faSSeongJae Park return KSFT_PASS; 529baa489faSSeongJae Park 530baa489faSSeongJae Park err_out: 531baa489faSSeongJae Park printf("Not OK\n"); 532baa489faSSeongJae Park munmap(map_ptr_orig, len + HPAGE_SIZE); 533baa489faSSeongJae Park return KSFT_FAIL; 534baa489faSSeongJae Park } 535baa489faSSeongJae Park 536baa489faSSeongJae Park static int ksm_merge_time(int mapping, int prot, int timeout, size_t map_size) 537baa489faSSeongJae Park { 538baa489faSSeongJae Park void *map_ptr; 539baa489faSSeongJae Park struct timespec start_time, end_time; 540baa489faSSeongJae Park unsigned long scan_time_ns; 541baa489faSSeongJae Park 542baa489faSSeongJae Park map_size *= MB; 543baa489faSSeongJae Park 544baa489faSSeongJae Park map_ptr = allocate_memory(NULL, prot, mapping, '*', map_size); 545baa489faSSeongJae Park if (!map_ptr) 546baa489faSSeongJae Park return KSFT_FAIL; 547baa489faSSeongJae Park 548baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 549baa489faSSeongJae Park perror("clock_gettime"); 550baa489faSSeongJae Park goto err_out; 551baa489faSSeongJae Park } 552baa489faSSeongJae Park if (ksm_merge_pages(map_ptr, map_size, start_time, timeout)) 553baa489faSSeongJae Park goto err_out; 554baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 555baa489faSSeongJae Park perror("clock_gettime"); 556baa489faSSeongJae Park goto err_out; 557baa489faSSeongJae Park } 558baa489faSSeongJae Park 559baa489faSSeongJae Park scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 560baa489faSSeongJae Park (end_time.tv_nsec - start_time.tv_nsec); 561baa489faSSeongJae Park 562baa489faSSeongJae Park printf("Total size: %lu MiB\n", map_size / MB); 563baa489faSSeongJae Park printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, 564baa489faSSeongJae Park scan_time_ns % NSEC_PER_SEC); 565baa489faSSeongJae Park printf("Average speed: %.3f MiB/s\n", (map_size / MB) / 566baa489faSSeongJae Park ((double)scan_time_ns / NSEC_PER_SEC)); 567baa489faSSeongJae Park 568baa489faSSeongJae Park munmap(map_ptr, map_size); 569baa489faSSeongJae Park return KSFT_PASS; 570baa489faSSeongJae Park 571baa489faSSeongJae Park err_out: 572baa489faSSeongJae Park printf("Not OK\n"); 573baa489faSSeongJae Park munmap(map_ptr, map_size); 574baa489faSSeongJae Park return KSFT_FAIL; 575baa489faSSeongJae Park } 576baa489faSSeongJae Park 577baa489faSSeongJae Park static int ksm_unmerge_time(int mapping, int prot, int timeout, size_t map_size) 578baa489faSSeongJae Park { 579baa489faSSeongJae Park void *map_ptr; 580baa489faSSeongJae Park struct timespec start_time, end_time; 581baa489faSSeongJae Park unsigned long scan_time_ns; 582baa489faSSeongJae Park 583baa489faSSeongJae Park map_size *= MB; 584baa489faSSeongJae Park 585baa489faSSeongJae Park map_ptr = allocate_memory(NULL, prot, mapping, '*', map_size); 586baa489faSSeongJae Park if (!map_ptr) 587baa489faSSeongJae Park return KSFT_FAIL; 588baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 589baa489faSSeongJae Park perror("clock_gettime"); 590baa489faSSeongJae Park goto err_out; 591baa489faSSeongJae Park } 592baa489faSSeongJae Park if (ksm_merge_pages(map_ptr, map_size, start_time, timeout)) 593baa489faSSeongJae Park goto err_out; 594baa489faSSeongJae Park 595baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 596baa489faSSeongJae Park perror("clock_gettime"); 597baa489faSSeongJae Park goto err_out; 598baa489faSSeongJae Park } 599baa489faSSeongJae Park if (ksm_unmerge_pages(map_ptr, map_size, start_time, timeout)) 600baa489faSSeongJae Park goto err_out; 601baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 602baa489faSSeongJae Park perror("clock_gettime"); 603baa489faSSeongJae Park goto err_out; 604baa489faSSeongJae Park } 605baa489faSSeongJae Park 606baa489faSSeongJae Park scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 607baa489faSSeongJae Park (end_time.tv_nsec - start_time.tv_nsec); 608baa489faSSeongJae Park 609baa489faSSeongJae Park printf("Total size: %lu MiB\n", map_size / MB); 610baa489faSSeongJae Park printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, 611baa489faSSeongJae Park scan_time_ns % NSEC_PER_SEC); 612baa489faSSeongJae Park printf("Average speed: %.3f MiB/s\n", (map_size / MB) / 613baa489faSSeongJae Park ((double)scan_time_ns / NSEC_PER_SEC)); 614baa489faSSeongJae Park 615baa489faSSeongJae Park munmap(map_ptr, map_size); 616baa489faSSeongJae Park return KSFT_PASS; 617baa489faSSeongJae Park 618baa489faSSeongJae Park err_out: 619baa489faSSeongJae Park printf("Not OK\n"); 620baa489faSSeongJae Park munmap(map_ptr, map_size); 621baa489faSSeongJae Park return KSFT_FAIL; 622baa489faSSeongJae Park } 623baa489faSSeongJae Park 624baa489faSSeongJae Park static int ksm_cow_time(int mapping, int prot, int timeout, size_t page_size) 625baa489faSSeongJae Park { 626baa489faSSeongJae Park void *map_ptr; 627baa489faSSeongJae Park struct timespec start_time, end_time; 628baa489faSSeongJae Park unsigned long cow_time_ns; 629baa489faSSeongJae Park 630baa489faSSeongJae Park /* page_count must be less than 2*page_size */ 631baa489faSSeongJae Park size_t page_count = 4000; 632baa489faSSeongJae Park 633baa489faSSeongJae Park map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count); 634baa489faSSeongJae Park if (!map_ptr) 635baa489faSSeongJae Park return KSFT_FAIL; 636baa489faSSeongJae Park 637baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 638baa489faSSeongJae Park perror("clock_gettime"); 639baa489faSSeongJae Park return KSFT_FAIL; 640baa489faSSeongJae Park } 641baa489faSSeongJae Park for (size_t i = 0; i < page_count - 1; i = i + 2) 642baa489faSSeongJae Park memset(map_ptr + page_size * i, '-', 1); 643baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 644baa489faSSeongJae Park perror("clock_gettime"); 645baa489faSSeongJae Park return KSFT_FAIL; 646baa489faSSeongJae Park } 647baa489faSSeongJae Park 648baa489faSSeongJae Park cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 649baa489faSSeongJae Park (end_time.tv_nsec - start_time.tv_nsec); 650baa489faSSeongJae Park 651baa489faSSeongJae Park printf("Total size: %lu MiB\n\n", (page_size * page_count) / MB); 652baa489faSSeongJae Park printf("Not merged pages:\n"); 653baa489faSSeongJae Park printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC, 654baa489faSSeongJae Park cow_time_ns % NSEC_PER_SEC); 655baa489faSSeongJae Park printf("Average speed: %.3f MiB/s\n\n", ((page_size * (page_count / 2)) / MB) / 656baa489faSSeongJae Park ((double)cow_time_ns / NSEC_PER_SEC)); 657baa489faSSeongJae Park 658baa489faSSeongJae Park /* Create 2000 pairs of duplicate pages */ 659baa489faSSeongJae Park for (size_t i = 0; i < page_count - 1; i = i + 2) { 660baa489faSSeongJae Park memset(map_ptr + page_size * i, '+', i / 2 + 1); 661baa489faSSeongJae Park memset(map_ptr + page_size * (i + 1), '+', i / 2 + 1); 662baa489faSSeongJae Park } 663baa489faSSeongJae Park if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout)) 664baa489faSSeongJae Park goto err_out; 665baa489faSSeongJae Park 666baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 667baa489faSSeongJae Park perror("clock_gettime"); 668baa489faSSeongJae Park goto err_out; 669baa489faSSeongJae Park } 670baa489faSSeongJae Park for (size_t i = 0; i < page_count - 1; i = i + 2) 671baa489faSSeongJae Park memset(map_ptr + page_size * i, '-', 1); 672baa489faSSeongJae Park if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 673baa489faSSeongJae Park perror("clock_gettime"); 674baa489faSSeongJae Park goto err_out; 675baa489faSSeongJae Park } 676baa489faSSeongJae Park 677baa489faSSeongJae Park cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 678baa489faSSeongJae Park (end_time.tv_nsec - start_time.tv_nsec); 679baa489faSSeongJae Park 680baa489faSSeongJae Park printf("Merged pages:\n"); 681baa489faSSeongJae Park printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC, 682baa489faSSeongJae Park cow_time_ns % NSEC_PER_SEC); 683baa489faSSeongJae Park printf("Average speed: %.3f MiB/s\n", ((page_size * (page_count / 2)) / MB) / 684baa489faSSeongJae Park ((double)cow_time_ns / NSEC_PER_SEC)); 685baa489faSSeongJae Park 686baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 687baa489faSSeongJae Park return KSFT_PASS; 688baa489faSSeongJae Park 689baa489faSSeongJae Park err_out: 690baa489faSSeongJae Park printf("Not OK\n"); 691baa489faSSeongJae Park munmap(map_ptr, page_size * page_count); 692baa489faSSeongJae Park return KSFT_FAIL; 693baa489faSSeongJae Park } 694baa489faSSeongJae Park 695baa489faSSeongJae Park int main(int argc, char *argv[]) 696baa489faSSeongJae Park { 697baa489faSSeongJae Park int ret, opt; 698baa489faSSeongJae Park int prot = 0; 699baa489faSSeongJae Park int ksm_scan_limit_sec = KSM_SCAN_LIMIT_SEC_DEFAULT; 700baa489faSSeongJae Park long page_count = KSM_PAGE_COUNT_DEFAULT; 701baa489faSSeongJae Park size_t page_size = sysconf(_SC_PAGESIZE); 702baa489faSSeongJae Park struct ksm_sysfs ksm_sysfs_old; 703baa489faSSeongJae Park int test_name = CHECK_KSM_MERGE; 704baa489faSSeongJae Park bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT; 705baa489faSSeongJae Park bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT; 706baa489faSSeongJae Park long size_MB = 0; 707baa489faSSeongJae Park 708baa489faSSeongJae Park while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNPCHD")) != -1) { 709baa489faSSeongJae Park switch (opt) { 710baa489faSSeongJae Park case 'a': 711baa489faSSeongJae Park prot = str_to_prot(optarg); 712baa489faSSeongJae Park break; 713baa489faSSeongJae Park case 'p': 714baa489faSSeongJae Park page_count = atol(optarg); 715baa489faSSeongJae Park if (page_count <= 0) { 716baa489faSSeongJae Park printf("The number of pages must be greater than 0\n"); 717baa489faSSeongJae Park return KSFT_FAIL; 718baa489faSSeongJae Park } 719baa489faSSeongJae Park break; 720baa489faSSeongJae Park case 'l': 721baa489faSSeongJae Park ksm_scan_limit_sec = atoi(optarg); 722baa489faSSeongJae Park if (ksm_scan_limit_sec <= 0) { 723baa489faSSeongJae Park printf("Timeout value must be greater than 0\n"); 724baa489faSSeongJae Park return KSFT_FAIL; 725baa489faSSeongJae Park } 726baa489faSSeongJae Park break; 727baa489faSSeongJae Park case 'h': 728baa489faSSeongJae Park print_help(); 729baa489faSSeongJae Park break; 730baa489faSSeongJae Park case 'z': 731baa489faSSeongJae Park if (strcmp(optarg, "0") == 0) 732baa489faSSeongJae Park use_zero_pages = 0; 733baa489faSSeongJae Park else 734baa489faSSeongJae Park use_zero_pages = 1; 735baa489faSSeongJae Park break; 736baa489faSSeongJae Park case 'm': 737baa489faSSeongJae Park if (strcmp(optarg, "0") == 0) 738baa489faSSeongJae Park merge_across_nodes = 0; 739baa489faSSeongJae Park else 740baa489faSSeongJae Park merge_across_nodes = 1; 741baa489faSSeongJae Park break; 742baa489faSSeongJae Park case 's': 743baa489faSSeongJae Park size_MB = atoi(optarg); 744baa489faSSeongJae Park if (size_MB <= 0) { 745baa489faSSeongJae Park printf("Size must be greater than 0\n"); 746baa489faSSeongJae Park return KSFT_FAIL; 747baa489faSSeongJae Park } 748baa489faSSeongJae Park case 'M': 749baa489faSSeongJae Park break; 750baa489faSSeongJae Park case 'U': 751baa489faSSeongJae Park test_name = CHECK_KSM_UNMERGE; 752baa489faSSeongJae Park break; 753baa489faSSeongJae Park case 'Z': 754baa489faSSeongJae Park test_name = CHECK_KSM_ZERO_PAGE_MERGE; 755baa489faSSeongJae Park break; 756baa489faSSeongJae Park case 'N': 757baa489faSSeongJae Park test_name = CHECK_KSM_NUMA_MERGE; 758baa489faSSeongJae Park break; 759baa489faSSeongJae Park case 'P': 760baa489faSSeongJae Park test_name = KSM_MERGE_TIME; 761baa489faSSeongJae Park break; 762baa489faSSeongJae Park case 'H': 763baa489faSSeongJae Park test_name = KSM_MERGE_TIME_HUGE_PAGES; 764baa489faSSeongJae Park break; 765baa489faSSeongJae Park case 'D': 766baa489faSSeongJae Park test_name = KSM_UNMERGE_TIME; 767baa489faSSeongJae Park break; 768baa489faSSeongJae Park case 'C': 769baa489faSSeongJae Park test_name = KSM_COW_TIME; 770baa489faSSeongJae Park break; 771baa489faSSeongJae Park default: 772baa489faSSeongJae Park return KSFT_FAIL; 773baa489faSSeongJae Park } 774baa489faSSeongJae Park } 775baa489faSSeongJae Park 776baa489faSSeongJae Park if (prot == 0) 777baa489faSSeongJae Park prot = str_to_prot(KSM_PROT_STR_DEFAULT); 778baa489faSSeongJae Park 779baa489faSSeongJae Park if (access(KSM_SYSFS_PATH, F_OK)) { 780baa489faSSeongJae Park printf("Config KSM not enabled\n"); 781baa489faSSeongJae Park return KSFT_SKIP; 782baa489faSSeongJae Park } 783baa489faSSeongJae Park 784baa489faSSeongJae Park if (ksm_save_def(&ksm_sysfs_old)) { 785baa489faSSeongJae Park printf("Cannot save default tunables\n"); 786baa489faSSeongJae Park return KSFT_FAIL; 787baa489faSSeongJae Park } 788baa489faSSeongJae Park 789baa489faSSeongJae Park if (ksm_write_sysfs(KSM_FP("run"), 2) || 790baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("sleep_millisecs"), 0) || 791baa489faSSeongJae Park numa_available() ? 0 : 792baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("merge_across_nodes"), 1) || 793baa489faSSeongJae Park ksm_write_sysfs(KSM_FP("pages_to_scan"), page_count)) 794baa489faSSeongJae Park return KSFT_FAIL; 795baa489faSSeongJae Park 796baa489faSSeongJae Park switch (test_name) { 797baa489faSSeongJae Park case CHECK_KSM_MERGE: 798baa489faSSeongJae Park ret = check_ksm_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count, 799baa489faSSeongJae Park ksm_scan_limit_sec, page_size); 800baa489faSSeongJae Park break; 801baa489faSSeongJae Park case CHECK_KSM_UNMERGE: 802baa489faSSeongJae Park ret = check_ksm_unmerge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec, 803baa489faSSeongJae Park page_size); 804baa489faSSeongJae Park break; 805baa489faSSeongJae Park case CHECK_KSM_ZERO_PAGE_MERGE: 806baa489faSSeongJae Park ret = check_ksm_zero_page_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count, 807baa489faSSeongJae Park ksm_scan_limit_sec, use_zero_pages, page_size); 808baa489faSSeongJae Park break; 809baa489faSSeongJae Park case CHECK_KSM_NUMA_MERGE: 810baa489faSSeongJae Park ret = check_ksm_numa_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec, 811baa489faSSeongJae Park merge_across_nodes, page_size); 812baa489faSSeongJae Park break; 813baa489faSSeongJae Park case KSM_MERGE_TIME: 814baa489faSSeongJae Park if (size_MB == 0) { 815baa489faSSeongJae Park printf("Option '-s' is required.\n"); 816baa489faSSeongJae Park return KSFT_FAIL; 817baa489faSSeongJae Park } 818baa489faSSeongJae Park ret = ksm_merge_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec, 819baa489faSSeongJae Park size_MB); 820baa489faSSeongJae Park break; 821baa489faSSeongJae Park case KSM_MERGE_TIME_HUGE_PAGES: 822baa489faSSeongJae Park if (size_MB == 0) { 823baa489faSSeongJae Park printf("Option '-s' is required.\n"); 824baa489faSSeongJae Park return KSFT_FAIL; 825baa489faSSeongJae Park } 826baa489faSSeongJae Park ret = ksm_merge_hugepages_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, 827baa489faSSeongJae Park ksm_scan_limit_sec, size_MB); 828baa489faSSeongJae Park break; 829baa489faSSeongJae Park case KSM_UNMERGE_TIME: 830baa489faSSeongJae Park if (size_MB == 0) { 831baa489faSSeongJae Park printf("Option '-s' is required.\n"); 832baa489faSSeongJae Park return KSFT_FAIL; 833baa489faSSeongJae Park } 834baa489faSSeongJae Park ret = ksm_unmerge_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, 835baa489faSSeongJae Park ksm_scan_limit_sec, size_MB); 836baa489faSSeongJae Park break; 837baa489faSSeongJae Park case KSM_COW_TIME: 838baa489faSSeongJae Park ret = ksm_cow_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec, 839baa489faSSeongJae Park page_size); 840baa489faSSeongJae Park break; 841baa489faSSeongJae Park } 842baa489faSSeongJae Park 843baa489faSSeongJae Park if (ksm_restore(&ksm_sysfs_old)) { 844baa489faSSeongJae Park printf("Cannot restore default tunables\n"); 845baa489faSSeongJae Park return KSFT_FAIL; 846baa489faSSeongJae Park } 847baa489faSSeongJae Park 848baa489faSSeongJae Park return ret; 849baa489faSSeongJae Park } 850