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