162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <sys/mman.h> 462306a36Sopenharmony_ci#include <sys/prctl.h> 562306a36Sopenharmony_ci#include <sys/wait.h> 662306a36Sopenharmony_ci#include <stdbool.h> 762306a36Sopenharmony_ci#include <time.h> 862306a36Sopenharmony_ci#include <string.h> 962306a36Sopenharmony_ci#include <numa.h> 1062306a36Sopenharmony_ci#include <unistd.h> 1162306a36Sopenharmony_ci#include <fcntl.h> 1262306a36Sopenharmony_ci#include <stdint.h> 1362306a36Sopenharmony_ci#include <err.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "../kselftest.h" 1662306a36Sopenharmony_ci#include <include/vdso/time64.h> 1762306a36Sopenharmony_ci#include "vm_util.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/" 2062306a36Sopenharmony_ci#define KSM_FP(s) (KSM_SYSFS_PATH s) 2162306a36Sopenharmony_ci#define KSM_SCAN_LIMIT_SEC_DEFAULT 120 2262306a36Sopenharmony_ci#define KSM_PAGE_COUNT_DEFAULT 10l 2362306a36Sopenharmony_ci#define KSM_PROT_STR_DEFAULT "rw" 2462306a36Sopenharmony_ci#define KSM_USE_ZERO_PAGES_DEFAULT false 2562306a36Sopenharmony_ci#define KSM_MERGE_ACROSS_NODES_DEFAULT true 2662306a36Sopenharmony_ci#define KSM_MERGE_TYPE_DEFAULT 0 2762306a36Sopenharmony_ci#define MB (1ul << 20) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct ksm_sysfs { 3062306a36Sopenharmony_ci unsigned long max_page_sharing; 3162306a36Sopenharmony_ci unsigned long merge_across_nodes; 3262306a36Sopenharmony_ci unsigned long pages_to_scan; 3362306a36Sopenharmony_ci unsigned long run; 3462306a36Sopenharmony_ci unsigned long sleep_millisecs; 3562306a36Sopenharmony_ci unsigned long stable_node_chains_prune_millisecs; 3662306a36Sopenharmony_ci unsigned long use_zero_pages; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cienum ksm_merge_type { 4062306a36Sopenharmony_ci KSM_MERGE_MADVISE, 4162306a36Sopenharmony_ci KSM_MERGE_PRCTL, 4262306a36Sopenharmony_ci KSM_MERGE_LAST = KSM_MERGE_PRCTL 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cienum ksm_test_name { 4662306a36Sopenharmony_ci CHECK_KSM_MERGE, 4762306a36Sopenharmony_ci CHECK_KSM_UNMERGE, 4862306a36Sopenharmony_ci CHECK_KSM_GET_MERGE_TYPE, 4962306a36Sopenharmony_ci CHECK_KSM_ZERO_PAGE_MERGE, 5062306a36Sopenharmony_ci CHECK_KSM_NUMA_MERGE, 5162306a36Sopenharmony_ci KSM_MERGE_TIME, 5262306a36Sopenharmony_ci KSM_MERGE_TIME_HUGE_PAGES, 5362306a36Sopenharmony_ci KSM_UNMERGE_TIME, 5462306a36Sopenharmony_ci KSM_COW_TIME 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ciint debug; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic int ksm_write_sysfs(const char *file_path, unsigned long val) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci FILE *f = fopen(file_path, "w"); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (!f) { 6462306a36Sopenharmony_ci fprintf(stderr, "f %s\n", file_path); 6562306a36Sopenharmony_ci perror("fopen"); 6662306a36Sopenharmony_ci return 1; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci if (fprintf(f, "%lu", val) < 0) { 6962306a36Sopenharmony_ci perror("fprintf"); 7062306a36Sopenharmony_ci fclose(f); 7162306a36Sopenharmony_ci return 1; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci fclose(f); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int ksm_read_sysfs(const char *file_path, unsigned long *val) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci FILE *f = fopen(file_path, "r"); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (!f) { 8362306a36Sopenharmony_ci fprintf(stderr, "f %s\n", file_path); 8462306a36Sopenharmony_ci perror("fopen"); 8562306a36Sopenharmony_ci return 1; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci if (fscanf(f, "%lu", val) != 1) { 8862306a36Sopenharmony_ci perror("fscanf"); 8962306a36Sopenharmony_ci fclose(f); 9062306a36Sopenharmony_ci return 1; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci fclose(f); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void ksm_print_sysfs(void) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci unsigned long max_page_sharing, pages_sharing, pages_shared; 10062306a36Sopenharmony_ci unsigned long full_scans, pages_unshared, pages_volatile; 10162306a36Sopenharmony_ci unsigned long stable_node_chains, stable_node_dups; 10262306a36Sopenharmony_ci long general_profit; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (ksm_read_sysfs(KSM_FP("pages_shared"), &pages_shared) || 10562306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("pages_sharing"), &pages_sharing) || 10662306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("max_page_sharing"), &max_page_sharing) || 10762306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("full_scans"), &full_scans) || 10862306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("pages_unshared"), &pages_unshared) || 10962306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("pages_volatile"), &pages_volatile) || 11062306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("stable_node_chains"), &stable_node_chains) || 11162306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("stable_node_dups"), &stable_node_dups) || 11262306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("general_profit"), (unsigned long *)&general_profit)) 11362306a36Sopenharmony_ci return; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci printf("pages_shared : %lu\n", pages_shared); 11662306a36Sopenharmony_ci printf("pages_sharing : %lu\n", pages_sharing); 11762306a36Sopenharmony_ci printf("max_page_sharing : %lu\n", max_page_sharing); 11862306a36Sopenharmony_ci printf("full_scans : %lu\n", full_scans); 11962306a36Sopenharmony_ci printf("pages_unshared : %lu\n", pages_unshared); 12062306a36Sopenharmony_ci printf("pages_volatile : %lu\n", pages_volatile); 12162306a36Sopenharmony_ci printf("stable_node_chains: %lu\n", stable_node_chains); 12262306a36Sopenharmony_ci printf("stable_node_dups : %lu\n", stable_node_dups); 12362306a36Sopenharmony_ci printf("general_profit : %ld\n", general_profit); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void ksm_print_procfs(void) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci const char *file_name = "/proc/self/ksm_stat"; 12962306a36Sopenharmony_ci char buffer[512]; 13062306a36Sopenharmony_ci FILE *f = fopen(file_name, "r"); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!f) { 13362306a36Sopenharmony_ci fprintf(stderr, "f %s\n", file_name); 13462306a36Sopenharmony_ci perror("fopen"); 13562306a36Sopenharmony_ci return; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci while (fgets(buffer, sizeof(buffer), f)) 13962306a36Sopenharmony_ci printf("%s", buffer); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci fclose(f); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int str_to_prot(char *prot_str) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci int prot = 0; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if ((strchr(prot_str, 'r')) != NULL) 14962306a36Sopenharmony_ci prot |= PROT_READ; 15062306a36Sopenharmony_ci if ((strchr(prot_str, 'w')) != NULL) 15162306a36Sopenharmony_ci prot |= PROT_WRITE; 15262306a36Sopenharmony_ci if ((strchr(prot_str, 'x')) != NULL) 15362306a36Sopenharmony_ci prot |= PROT_EXEC; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return prot; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void print_help(void) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci printf("usage: ksm_tests [-h] <test type> [-a prot] [-p page_count] [-l timeout]\n" 16162306a36Sopenharmony_ci "[-z use_zero_pages] [-m merge_across_nodes] [-s size]\n"); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci printf("Supported <test type>:\n" 16462306a36Sopenharmony_ci " -M (page merging)\n" 16562306a36Sopenharmony_ci " -Z (zero pages merging)\n" 16662306a36Sopenharmony_ci " -N (merging of pages in different NUMA nodes)\n" 16762306a36Sopenharmony_ci " -U (page unmerging)\n" 16862306a36Sopenharmony_ci " -P evaluate merging time and speed.\n" 16962306a36Sopenharmony_ci " For this test, the size of duplicated memory area (in MiB)\n" 17062306a36Sopenharmony_ci " must be provided using -s option\n" 17162306a36Sopenharmony_ci " -H evaluate merging time and speed of area allocated mostly with huge pages\n" 17262306a36Sopenharmony_ci " For this test, the size of duplicated memory area (in MiB)\n" 17362306a36Sopenharmony_ci " must be provided using -s option\n" 17462306a36Sopenharmony_ci " -D evaluate unmerging time and speed when disabling KSM.\n" 17562306a36Sopenharmony_ci " For this test, the size of duplicated memory area (in MiB)\n" 17662306a36Sopenharmony_ci " must be provided using -s option\n" 17762306a36Sopenharmony_ci " -C evaluate the time required to break COW of merged pages.\n\n"); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci printf(" -a: specify the access protections of pages.\n" 18062306a36Sopenharmony_ci " <prot> must be of the form [rwx].\n" 18162306a36Sopenharmony_ci " Default: %s\n", KSM_PROT_STR_DEFAULT); 18262306a36Sopenharmony_ci printf(" -p: specify the number of pages to test.\n" 18362306a36Sopenharmony_ci " Default: %ld\n", KSM_PAGE_COUNT_DEFAULT); 18462306a36Sopenharmony_ci printf(" -l: limit the maximum running time (in seconds) for a test.\n" 18562306a36Sopenharmony_ci " Default: %d seconds\n", KSM_SCAN_LIMIT_SEC_DEFAULT); 18662306a36Sopenharmony_ci printf(" -z: change use_zero_pages tunable\n" 18762306a36Sopenharmony_ci " Default: %d\n", KSM_USE_ZERO_PAGES_DEFAULT); 18862306a36Sopenharmony_ci printf(" -m: change merge_across_nodes tunable\n" 18962306a36Sopenharmony_ci " Default: %d\n", KSM_MERGE_ACROSS_NODES_DEFAULT); 19062306a36Sopenharmony_ci printf(" -d: turn debugging output on\n"); 19162306a36Sopenharmony_ci printf(" -s: the size of duplicated memory area (in MiB)\n"); 19262306a36Sopenharmony_ci printf(" -t: KSM merge type\n" 19362306a36Sopenharmony_ci " Default: 0\n" 19462306a36Sopenharmony_ci " 0: madvise merging\n" 19562306a36Sopenharmony_ci " 1: prctl merging\n"); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci exit(0); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void *allocate_memory(void *ptr, int prot, int mapping, char data, size_t map_size) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci void *map_ptr = mmap(ptr, map_size, PROT_WRITE, mapping, -1, 0); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!map_ptr) { 20562306a36Sopenharmony_ci perror("mmap"); 20662306a36Sopenharmony_ci return NULL; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci memset(map_ptr, data, map_size); 20962306a36Sopenharmony_ci if (mprotect(map_ptr, map_size, prot)) { 21062306a36Sopenharmony_ci perror("mprotect"); 21162306a36Sopenharmony_ci munmap(map_ptr, map_size); 21262306a36Sopenharmony_ci return NULL; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return map_ptr; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int ksm_do_scan(int scan_count, struct timespec start_time, int timeout) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct timespec cur_time; 22162306a36Sopenharmony_ci unsigned long cur_scan, init_scan; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (ksm_read_sysfs(KSM_FP("full_scans"), &init_scan)) 22462306a36Sopenharmony_ci return 1; 22562306a36Sopenharmony_ci cur_scan = init_scan; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci while (cur_scan < init_scan + scan_count) { 22862306a36Sopenharmony_ci if (ksm_read_sysfs(KSM_FP("full_scans"), &cur_scan)) 22962306a36Sopenharmony_ci return 1; 23062306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &cur_time)) { 23162306a36Sopenharmony_ci perror("clock_gettime"); 23262306a36Sopenharmony_ci return 1; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci if ((cur_time.tv_sec - start_time.tv_sec) > timeout) { 23562306a36Sopenharmony_ci printf("Scan time limit exceeded\n"); 23662306a36Sopenharmony_ci return 1; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int ksm_merge_pages(int merge_type, void *addr, size_t size, 24462306a36Sopenharmony_ci struct timespec start_time, int timeout) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci if (merge_type == KSM_MERGE_MADVISE) { 24762306a36Sopenharmony_ci if (madvise(addr, size, MADV_MERGEABLE)) { 24862306a36Sopenharmony_ci perror("madvise"); 24962306a36Sopenharmony_ci return 1; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci } else if (merge_type == KSM_MERGE_PRCTL) { 25262306a36Sopenharmony_ci if (prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0)) { 25362306a36Sopenharmony_ci perror("prctl"); 25462306a36Sopenharmony_ci return 1; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (ksm_write_sysfs(KSM_FP("run"), 1)) 25962306a36Sopenharmony_ci return 1; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* Since merging occurs only after 2 scans, make sure to get at least 2 full scans */ 26262306a36Sopenharmony_ci if (ksm_do_scan(2, start_time, timeout)) 26362306a36Sopenharmony_ci return 1; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic int ksm_unmerge_pages(void *addr, size_t size, 26962306a36Sopenharmony_ci struct timespec start_time, int timeout) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci if (madvise(addr, size, MADV_UNMERGEABLE)) { 27262306a36Sopenharmony_ci perror("madvise"); 27362306a36Sopenharmony_ci return 1; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci return 0; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic bool assert_ksm_pages_count(long dupl_page_count) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci unsigned long max_page_sharing, pages_sharing, pages_shared; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (ksm_read_sysfs(KSM_FP("pages_shared"), &pages_shared) || 28362306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("pages_sharing"), &pages_sharing) || 28462306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("max_page_sharing"), &max_page_sharing)) 28562306a36Sopenharmony_ci return false; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (debug) { 28862306a36Sopenharmony_ci ksm_print_sysfs(); 28962306a36Sopenharmony_ci ksm_print_procfs(); 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* 29362306a36Sopenharmony_ci * Since there must be at least 2 pages for merging and 1 page can be 29462306a36Sopenharmony_ci * shared with the limited number of pages (max_page_sharing), sometimes 29562306a36Sopenharmony_ci * there are 'leftover' pages that cannot be merged. For example, if there 29662306a36Sopenharmony_ci * are 11 pages and max_page_sharing = 10, then only 10 pages will be 29762306a36Sopenharmony_ci * merged and the 11th page won't be affected. As a result, when the number 29862306a36Sopenharmony_ci * of duplicate pages is divided by max_page_sharing and the remainder is 1, 29962306a36Sopenharmony_ci * pages_shared and pages_sharing values will be equal between dupl_page_count 30062306a36Sopenharmony_ci * and dupl_page_count - 1. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci if (dupl_page_count % max_page_sharing == 1 || dupl_page_count % max_page_sharing == 0) { 30362306a36Sopenharmony_ci if (pages_shared == dupl_page_count / max_page_sharing && 30462306a36Sopenharmony_ci pages_sharing == pages_shared * (max_page_sharing - 1)) 30562306a36Sopenharmony_ci return true; 30662306a36Sopenharmony_ci } else { 30762306a36Sopenharmony_ci if (pages_shared == (dupl_page_count / max_page_sharing + 1) && 30862306a36Sopenharmony_ci pages_sharing == dupl_page_count - pages_shared) 30962306a36Sopenharmony_ci return true; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return false; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int ksm_save_def(struct ksm_sysfs *ksm_sysfs) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci if (ksm_read_sysfs(KSM_FP("max_page_sharing"), &ksm_sysfs->max_page_sharing) || 31862306a36Sopenharmony_ci numa_available() ? 0 : 31962306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("merge_across_nodes"), &ksm_sysfs->merge_across_nodes) || 32062306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("sleep_millisecs"), &ksm_sysfs->sleep_millisecs) || 32162306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("pages_to_scan"), &ksm_sysfs->pages_to_scan) || 32262306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("run"), &ksm_sysfs->run) || 32362306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("stable_node_chains_prune_millisecs"), 32462306a36Sopenharmony_ci &ksm_sysfs->stable_node_chains_prune_millisecs) || 32562306a36Sopenharmony_ci ksm_read_sysfs(KSM_FP("use_zero_pages"), &ksm_sysfs->use_zero_pages)) 32662306a36Sopenharmony_ci return 1; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int ksm_restore(struct ksm_sysfs *ksm_sysfs) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci if (ksm_write_sysfs(KSM_FP("max_page_sharing"), ksm_sysfs->max_page_sharing) || 33462306a36Sopenharmony_ci numa_available() ? 0 : 33562306a36Sopenharmony_ci ksm_write_sysfs(KSM_FP("merge_across_nodes"), ksm_sysfs->merge_across_nodes) || 33662306a36Sopenharmony_ci ksm_write_sysfs(KSM_FP("pages_to_scan"), ksm_sysfs->pages_to_scan) || 33762306a36Sopenharmony_ci ksm_write_sysfs(KSM_FP("run"), ksm_sysfs->run) || 33862306a36Sopenharmony_ci ksm_write_sysfs(KSM_FP("sleep_millisecs"), ksm_sysfs->sleep_millisecs) || 33962306a36Sopenharmony_ci ksm_write_sysfs(KSM_FP("stable_node_chains_prune_millisecs"), 34062306a36Sopenharmony_ci ksm_sysfs->stable_node_chains_prune_millisecs) || 34162306a36Sopenharmony_ci ksm_write_sysfs(KSM_FP("use_zero_pages"), ksm_sysfs->use_zero_pages)) 34262306a36Sopenharmony_ci return 1; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int check_ksm_merge(int merge_type, int mapping, int prot, 34862306a36Sopenharmony_ci long page_count, int timeout, size_t page_size) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci void *map_ptr; 35162306a36Sopenharmony_ci struct timespec start_time; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 35462306a36Sopenharmony_ci perror("clock_gettime"); 35562306a36Sopenharmony_ci return KSFT_FAIL; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* fill pages with the same data and merge them */ 35962306a36Sopenharmony_ci map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count); 36062306a36Sopenharmony_ci if (!map_ptr) 36162306a36Sopenharmony_ci return KSFT_FAIL; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (ksm_merge_pages(merge_type, map_ptr, page_size * page_count, start_time, timeout)) 36462306a36Sopenharmony_ci goto err_out; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* verify that the right number of pages are merged */ 36762306a36Sopenharmony_ci if (assert_ksm_pages_count(page_count)) { 36862306a36Sopenharmony_ci printf("OK\n"); 36962306a36Sopenharmony_ci munmap(map_ptr, page_size * page_count); 37062306a36Sopenharmony_ci if (merge_type == KSM_MERGE_PRCTL) 37162306a36Sopenharmony_ci prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0); 37262306a36Sopenharmony_ci return KSFT_PASS; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cierr_out: 37662306a36Sopenharmony_ci printf("Not OK\n"); 37762306a36Sopenharmony_ci munmap(map_ptr, page_size * page_count); 37862306a36Sopenharmony_ci return KSFT_FAIL; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int check_ksm_unmerge(int merge_type, int mapping, int prot, int timeout, size_t page_size) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci void *map_ptr; 38462306a36Sopenharmony_ci struct timespec start_time; 38562306a36Sopenharmony_ci int page_count = 2; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 38862306a36Sopenharmony_ci perror("clock_gettime"); 38962306a36Sopenharmony_ci return KSFT_FAIL; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* fill pages with the same data and merge them */ 39362306a36Sopenharmony_ci map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count); 39462306a36Sopenharmony_ci if (!map_ptr) 39562306a36Sopenharmony_ci return KSFT_FAIL; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (ksm_merge_pages(merge_type, map_ptr, page_size * page_count, start_time, timeout)) 39862306a36Sopenharmony_ci goto err_out; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* change 1 byte in each of the 2 pages -- KSM must automatically unmerge them */ 40162306a36Sopenharmony_ci memset(map_ptr, '-', 1); 40262306a36Sopenharmony_ci memset(map_ptr + page_size, '+', 1); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* get at least 1 scan, so KSM can detect that the pages were modified */ 40562306a36Sopenharmony_ci if (ksm_do_scan(1, start_time, timeout)) 40662306a36Sopenharmony_ci goto err_out; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* check that unmerging was successful and 0 pages are currently merged */ 40962306a36Sopenharmony_ci if (assert_ksm_pages_count(0)) { 41062306a36Sopenharmony_ci printf("OK\n"); 41162306a36Sopenharmony_ci munmap(map_ptr, page_size * page_count); 41262306a36Sopenharmony_ci return KSFT_PASS; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cierr_out: 41662306a36Sopenharmony_ci printf("Not OK\n"); 41762306a36Sopenharmony_ci munmap(map_ptr, page_size * page_count); 41862306a36Sopenharmony_ci return KSFT_FAIL; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic int check_ksm_zero_page_merge(int merge_type, int mapping, int prot, long page_count, 42262306a36Sopenharmony_ci int timeout, bool use_zero_pages, size_t page_size) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci void *map_ptr; 42562306a36Sopenharmony_ci struct timespec start_time; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 42862306a36Sopenharmony_ci perror("clock_gettime"); 42962306a36Sopenharmony_ci return KSFT_FAIL; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (ksm_write_sysfs(KSM_FP("use_zero_pages"), use_zero_pages)) 43362306a36Sopenharmony_ci return KSFT_FAIL; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* fill pages with zero and try to merge them */ 43662306a36Sopenharmony_ci map_ptr = allocate_memory(NULL, prot, mapping, 0, page_size * page_count); 43762306a36Sopenharmony_ci if (!map_ptr) 43862306a36Sopenharmony_ci return KSFT_FAIL; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (ksm_merge_pages(merge_type, map_ptr, page_size * page_count, start_time, timeout)) 44162306a36Sopenharmony_ci goto err_out; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * verify that the right number of pages are merged: 44562306a36Sopenharmony_ci * 1) if use_zero_pages is set to 1, empty pages are merged 44662306a36Sopenharmony_ci * with the kernel zero page instead of with each other; 44762306a36Sopenharmony_ci * 2) if use_zero_pages is set to 0, empty pages are not treated specially 44862306a36Sopenharmony_ci * and merged as usual. 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_ci if (use_zero_pages && !assert_ksm_pages_count(0)) 45162306a36Sopenharmony_ci goto err_out; 45262306a36Sopenharmony_ci else if (!use_zero_pages && !assert_ksm_pages_count(page_count)) 45362306a36Sopenharmony_ci goto err_out; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci printf("OK\n"); 45662306a36Sopenharmony_ci munmap(map_ptr, page_size * page_count); 45762306a36Sopenharmony_ci return KSFT_PASS; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cierr_out: 46062306a36Sopenharmony_ci printf("Not OK\n"); 46162306a36Sopenharmony_ci munmap(map_ptr, page_size * page_count); 46262306a36Sopenharmony_ci return KSFT_FAIL; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic int get_next_mem_node(int node) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci long node_size; 46962306a36Sopenharmony_ci int mem_node = 0; 47062306a36Sopenharmony_ci int i, max_node = numa_max_node(); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci for (i = node + 1; i <= max_node + node; i++) { 47362306a36Sopenharmony_ci mem_node = i % (max_node + 1); 47462306a36Sopenharmony_ci node_size = numa_node_size(mem_node, NULL); 47562306a36Sopenharmony_ci if (node_size > 0) 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci return mem_node; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic int get_first_mem_node(void) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci return get_next_mem_node(numa_max_node()); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeout, 48762306a36Sopenharmony_ci bool merge_across_nodes, size_t page_size) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci void *numa1_map_ptr, *numa2_map_ptr; 49062306a36Sopenharmony_ci struct timespec start_time; 49162306a36Sopenharmony_ci int page_count = 2; 49262306a36Sopenharmony_ci int first_node; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 49562306a36Sopenharmony_ci perror("clock_gettime"); 49662306a36Sopenharmony_ci return KSFT_FAIL; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (numa_available() < 0) { 50062306a36Sopenharmony_ci perror("NUMA support not enabled"); 50162306a36Sopenharmony_ci return KSFT_SKIP; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci if (numa_num_configured_nodes() <= 1) { 50462306a36Sopenharmony_ci printf("At least 2 NUMA nodes must be available\n"); 50562306a36Sopenharmony_ci return KSFT_SKIP; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci if (ksm_write_sysfs(KSM_FP("merge_across_nodes"), merge_across_nodes)) 50862306a36Sopenharmony_ci return KSFT_FAIL; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* allocate 2 pages in 2 different NUMA nodes and fill them with the same data */ 51162306a36Sopenharmony_ci first_node = get_first_mem_node(); 51262306a36Sopenharmony_ci numa1_map_ptr = numa_alloc_onnode(page_size, first_node); 51362306a36Sopenharmony_ci numa2_map_ptr = numa_alloc_onnode(page_size, get_next_mem_node(first_node)); 51462306a36Sopenharmony_ci if (!numa1_map_ptr || !numa2_map_ptr) { 51562306a36Sopenharmony_ci perror("numa_alloc_onnode"); 51662306a36Sopenharmony_ci return KSFT_FAIL; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci memset(numa1_map_ptr, '*', page_size); 52062306a36Sopenharmony_ci memset(numa2_map_ptr, '*', page_size); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* try to merge the pages */ 52362306a36Sopenharmony_ci if (ksm_merge_pages(merge_type, numa1_map_ptr, page_size, start_time, timeout) || 52462306a36Sopenharmony_ci ksm_merge_pages(merge_type, numa2_map_ptr, page_size, start_time, timeout)) 52562306a36Sopenharmony_ci goto err_out; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* 52862306a36Sopenharmony_ci * verify that the right number of pages are merged: 52962306a36Sopenharmony_ci * 1) if merge_across_nodes was enabled, 2 duplicate pages will be merged; 53062306a36Sopenharmony_ci * 2) if merge_across_nodes = 0, there must be 0 merged pages, since there is 53162306a36Sopenharmony_ci * only 1 unique page in each node and they can't be shared. 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_ci if (merge_across_nodes && !assert_ksm_pages_count(page_count)) 53462306a36Sopenharmony_ci goto err_out; 53562306a36Sopenharmony_ci else if (!merge_across_nodes && !assert_ksm_pages_count(0)) 53662306a36Sopenharmony_ci goto err_out; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci numa_free(numa1_map_ptr, page_size); 53962306a36Sopenharmony_ci numa_free(numa2_map_ptr, page_size); 54062306a36Sopenharmony_ci printf("OK\n"); 54162306a36Sopenharmony_ci return KSFT_PASS; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cierr_out: 54462306a36Sopenharmony_ci numa_free(numa1_map_ptr, page_size); 54562306a36Sopenharmony_ci numa_free(numa2_map_ptr, page_size); 54662306a36Sopenharmony_ci printf("Not OK\n"); 54762306a36Sopenharmony_ci return KSFT_FAIL; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic int ksm_merge_hugepages_time(int merge_type, int mapping, int prot, 55162306a36Sopenharmony_ci int timeout, size_t map_size) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci void *map_ptr, *map_ptr_orig; 55462306a36Sopenharmony_ci struct timespec start_time, end_time; 55562306a36Sopenharmony_ci unsigned long scan_time_ns; 55662306a36Sopenharmony_ci int pagemap_fd, n_normal_pages, n_huge_pages; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci map_size *= MB; 55962306a36Sopenharmony_ci size_t len = map_size; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci len -= len % HPAGE_SIZE; 56262306a36Sopenharmony_ci map_ptr_orig = mmap(NULL, len + HPAGE_SIZE, PROT_READ | PROT_WRITE, 56362306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE, -1, 0); 56462306a36Sopenharmony_ci map_ptr = map_ptr_orig + HPAGE_SIZE - (uintptr_t)map_ptr_orig % HPAGE_SIZE; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (map_ptr_orig == MAP_FAILED) 56762306a36Sopenharmony_ci err(2, "initial mmap"); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (madvise(map_ptr, len, MADV_HUGEPAGE)) 57062306a36Sopenharmony_ci err(2, "MADV_HUGEPAGE"); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci pagemap_fd = open("/proc/self/pagemap", O_RDONLY); 57362306a36Sopenharmony_ci if (pagemap_fd < 0) 57462306a36Sopenharmony_ci err(2, "open pagemap"); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci n_normal_pages = 0; 57762306a36Sopenharmony_ci n_huge_pages = 0; 57862306a36Sopenharmony_ci for (void *p = map_ptr; p < map_ptr + len; p += HPAGE_SIZE) { 57962306a36Sopenharmony_ci if (allocate_transhuge(p, pagemap_fd) < 0) 58062306a36Sopenharmony_ci n_normal_pages++; 58162306a36Sopenharmony_ci else 58262306a36Sopenharmony_ci n_huge_pages++; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci printf("Number of normal pages: %d\n", n_normal_pages); 58562306a36Sopenharmony_ci printf("Number of huge pages: %d\n", n_huge_pages); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci memset(map_ptr, '*', len); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 59062306a36Sopenharmony_ci perror("clock_gettime"); 59162306a36Sopenharmony_ci goto err_out; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout)) 59462306a36Sopenharmony_ci goto err_out; 59562306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 59662306a36Sopenharmony_ci perror("clock_gettime"); 59762306a36Sopenharmony_ci goto err_out; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 60162306a36Sopenharmony_ci (end_time.tv_nsec - start_time.tv_nsec); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci printf("Total size: %lu MiB\n", map_size / MB); 60462306a36Sopenharmony_ci printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, 60562306a36Sopenharmony_ci scan_time_ns % NSEC_PER_SEC); 60662306a36Sopenharmony_ci printf("Average speed: %.3f MiB/s\n", (map_size / MB) / 60762306a36Sopenharmony_ci ((double)scan_time_ns / NSEC_PER_SEC)); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci munmap(map_ptr_orig, len + HPAGE_SIZE); 61062306a36Sopenharmony_ci return KSFT_PASS; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cierr_out: 61362306a36Sopenharmony_ci printf("Not OK\n"); 61462306a36Sopenharmony_ci munmap(map_ptr_orig, len + HPAGE_SIZE); 61562306a36Sopenharmony_ci return KSFT_FAIL; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic int ksm_merge_time(int merge_type, int mapping, int prot, int timeout, size_t map_size) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci void *map_ptr; 62162306a36Sopenharmony_ci struct timespec start_time, end_time; 62262306a36Sopenharmony_ci unsigned long scan_time_ns; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci map_size *= MB; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci map_ptr = allocate_memory(NULL, prot, mapping, '*', map_size); 62762306a36Sopenharmony_ci if (!map_ptr) 62862306a36Sopenharmony_ci return KSFT_FAIL; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 63162306a36Sopenharmony_ci perror("clock_gettime"); 63262306a36Sopenharmony_ci goto err_out; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout)) 63562306a36Sopenharmony_ci goto err_out; 63662306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 63762306a36Sopenharmony_ci perror("clock_gettime"); 63862306a36Sopenharmony_ci goto err_out; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 64262306a36Sopenharmony_ci (end_time.tv_nsec - start_time.tv_nsec); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci printf("Total size: %lu MiB\n", map_size / MB); 64562306a36Sopenharmony_ci printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, 64662306a36Sopenharmony_ci scan_time_ns % NSEC_PER_SEC); 64762306a36Sopenharmony_ci printf("Average speed: %.3f MiB/s\n", (map_size / MB) / 64862306a36Sopenharmony_ci ((double)scan_time_ns / NSEC_PER_SEC)); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci munmap(map_ptr, map_size); 65162306a36Sopenharmony_ci return KSFT_PASS; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cierr_out: 65462306a36Sopenharmony_ci printf("Not OK\n"); 65562306a36Sopenharmony_ci munmap(map_ptr, map_size); 65662306a36Sopenharmony_ci return KSFT_FAIL; 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic int ksm_unmerge_time(int merge_type, int mapping, int prot, int timeout, size_t map_size) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci void *map_ptr; 66262306a36Sopenharmony_ci struct timespec start_time, end_time; 66362306a36Sopenharmony_ci unsigned long scan_time_ns; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci map_size *= MB; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci map_ptr = allocate_memory(NULL, prot, mapping, '*', map_size); 66862306a36Sopenharmony_ci if (!map_ptr) 66962306a36Sopenharmony_ci return KSFT_FAIL; 67062306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 67162306a36Sopenharmony_ci perror("clock_gettime"); 67262306a36Sopenharmony_ci goto err_out; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci if (ksm_merge_pages(merge_type, map_ptr, map_size, start_time, timeout)) 67562306a36Sopenharmony_ci goto err_out; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 67862306a36Sopenharmony_ci perror("clock_gettime"); 67962306a36Sopenharmony_ci goto err_out; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci if (ksm_unmerge_pages(map_ptr, map_size, start_time, timeout)) 68262306a36Sopenharmony_ci goto err_out; 68362306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 68462306a36Sopenharmony_ci perror("clock_gettime"); 68562306a36Sopenharmony_ci goto err_out; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 68962306a36Sopenharmony_ci (end_time.tv_nsec - start_time.tv_nsec); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci printf("Total size: %lu MiB\n", map_size / MB); 69262306a36Sopenharmony_ci printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, 69362306a36Sopenharmony_ci scan_time_ns % NSEC_PER_SEC); 69462306a36Sopenharmony_ci printf("Average speed: %.3f MiB/s\n", (map_size / MB) / 69562306a36Sopenharmony_ci ((double)scan_time_ns / NSEC_PER_SEC)); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci munmap(map_ptr, map_size); 69862306a36Sopenharmony_ci return KSFT_PASS; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cierr_out: 70162306a36Sopenharmony_ci printf("Not OK\n"); 70262306a36Sopenharmony_ci munmap(map_ptr, map_size); 70362306a36Sopenharmony_ci return KSFT_FAIL; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic int ksm_cow_time(int merge_type, int mapping, int prot, int timeout, size_t page_size) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci void *map_ptr; 70962306a36Sopenharmony_ci struct timespec start_time, end_time; 71062306a36Sopenharmony_ci unsigned long cow_time_ns; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* page_count must be less than 2*page_size */ 71362306a36Sopenharmony_ci size_t page_count = 4000; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count); 71662306a36Sopenharmony_ci if (!map_ptr) 71762306a36Sopenharmony_ci return KSFT_FAIL; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 72062306a36Sopenharmony_ci perror("clock_gettime"); 72162306a36Sopenharmony_ci return KSFT_FAIL; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci for (size_t i = 0; i < page_count - 1; i = i + 2) 72462306a36Sopenharmony_ci memset(map_ptr + page_size * i, '-', 1); 72562306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 72662306a36Sopenharmony_ci perror("clock_gettime"); 72762306a36Sopenharmony_ci return KSFT_FAIL; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 73162306a36Sopenharmony_ci (end_time.tv_nsec - start_time.tv_nsec); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci printf("Total size: %lu MiB\n\n", (page_size * page_count) / MB); 73462306a36Sopenharmony_ci printf("Not merged pages:\n"); 73562306a36Sopenharmony_ci printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC, 73662306a36Sopenharmony_ci cow_time_ns % NSEC_PER_SEC); 73762306a36Sopenharmony_ci printf("Average speed: %.3f MiB/s\n\n", ((page_size * (page_count / 2)) / MB) / 73862306a36Sopenharmony_ci ((double)cow_time_ns / NSEC_PER_SEC)); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* Create 2000 pairs of duplicate pages */ 74162306a36Sopenharmony_ci for (size_t i = 0; i < page_count - 1; i = i + 2) { 74262306a36Sopenharmony_ci memset(map_ptr + page_size * i, '+', i / 2 + 1); 74362306a36Sopenharmony_ci memset(map_ptr + page_size * (i + 1), '+', i / 2 + 1); 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci if (ksm_merge_pages(merge_type, map_ptr, page_size * page_count, start_time, timeout)) 74662306a36Sopenharmony_ci goto err_out; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 74962306a36Sopenharmony_ci perror("clock_gettime"); 75062306a36Sopenharmony_ci goto err_out; 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci for (size_t i = 0; i < page_count - 1; i = i + 2) 75362306a36Sopenharmony_ci memset(map_ptr + page_size * i, '-', 1); 75462306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 75562306a36Sopenharmony_ci perror("clock_gettime"); 75662306a36Sopenharmony_ci goto err_out; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 76062306a36Sopenharmony_ci (end_time.tv_nsec - start_time.tv_nsec); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci printf("Merged pages:\n"); 76362306a36Sopenharmony_ci printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC, 76462306a36Sopenharmony_ci cow_time_ns % NSEC_PER_SEC); 76562306a36Sopenharmony_ci printf("Average speed: %.3f MiB/s\n", ((page_size * (page_count / 2)) / MB) / 76662306a36Sopenharmony_ci ((double)cow_time_ns / NSEC_PER_SEC)); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci munmap(map_ptr, page_size * page_count); 76962306a36Sopenharmony_ci return KSFT_PASS; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cierr_out: 77262306a36Sopenharmony_ci printf("Not OK\n"); 77362306a36Sopenharmony_ci munmap(map_ptr, page_size * page_count); 77462306a36Sopenharmony_ci return KSFT_FAIL; 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ciint main(int argc, char *argv[]) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci int ret, opt; 78062306a36Sopenharmony_ci int prot = 0; 78162306a36Sopenharmony_ci int ksm_scan_limit_sec = KSM_SCAN_LIMIT_SEC_DEFAULT; 78262306a36Sopenharmony_ci int merge_type = KSM_MERGE_TYPE_DEFAULT; 78362306a36Sopenharmony_ci long page_count = KSM_PAGE_COUNT_DEFAULT; 78462306a36Sopenharmony_ci size_t page_size = sysconf(_SC_PAGESIZE); 78562306a36Sopenharmony_ci struct ksm_sysfs ksm_sysfs_old; 78662306a36Sopenharmony_ci int test_name = CHECK_KSM_MERGE; 78762306a36Sopenharmony_ci bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT; 78862306a36Sopenharmony_ci bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT; 78962306a36Sopenharmony_ci long size_MB = 0; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci while ((opt = getopt(argc, argv, "dha:p:l:z:m:s:t:MUZNPCHD")) != -1) { 79262306a36Sopenharmony_ci switch (opt) { 79362306a36Sopenharmony_ci case 'a': 79462306a36Sopenharmony_ci prot = str_to_prot(optarg); 79562306a36Sopenharmony_ci break; 79662306a36Sopenharmony_ci case 'p': 79762306a36Sopenharmony_ci page_count = atol(optarg); 79862306a36Sopenharmony_ci if (page_count <= 0) { 79962306a36Sopenharmony_ci printf("The number of pages must be greater than 0\n"); 80062306a36Sopenharmony_ci return KSFT_FAIL; 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci break; 80362306a36Sopenharmony_ci case 'l': 80462306a36Sopenharmony_ci ksm_scan_limit_sec = atoi(optarg); 80562306a36Sopenharmony_ci if (ksm_scan_limit_sec <= 0) { 80662306a36Sopenharmony_ci printf("Timeout value must be greater than 0\n"); 80762306a36Sopenharmony_ci return KSFT_FAIL; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci break; 81062306a36Sopenharmony_ci case 'h': 81162306a36Sopenharmony_ci print_help(); 81262306a36Sopenharmony_ci break; 81362306a36Sopenharmony_ci case 'z': 81462306a36Sopenharmony_ci if (strcmp(optarg, "0") == 0) 81562306a36Sopenharmony_ci use_zero_pages = 0; 81662306a36Sopenharmony_ci else 81762306a36Sopenharmony_ci use_zero_pages = 1; 81862306a36Sopenharmony_ci break; 81962306a36Sopenharmony_ci case 'm': 82062306a36Sopenharmony_ci if (strcmp(optarg, "0") == 0) 82162306a36Sopenharmony_ci merge_across_nodes = 0; 82262306a36Sopenharmony_ci else 82362306a36Sopenharmony_ci merge_across_nodes = 1; 82462306a36Sopenharmony_ci break; 82562306a36Sopenharmony_ci case 'd': 82662306a36Sopenharmony_ci debug = 1; 82762306a36Sopenharmony_ci break; 82862306a36Sopenharmony_ci case 's': 82962306a36Sopenharmony_ci size_MB = atoi(optarg); 83062306a36Sopenharmony_ci if (size_MB <= 0) { 83162306a36Sopenharmony_ci printf("Size must be greater than 0\n"); 83262306a36Sopenharmony_ci return KSFT_FAIL; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci break; 83562306a36Sopenharmony_ci case 't': 83662306a36Sopenharmony_ci { 83762306a36Sopenharmony_ci int tmp = atoi(optarg); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (tmp < 0 || tmp > KSM_MERGE_LAST) { 84062306a36Sopenharmony_ci printf("Invalid merge type\n"); 84162306a36Sopenharmony_ci return KSFT_FAIL; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci merge_type = tmp; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci break; 84662306a36Sopenharmony_ci case 'M': 84762306a36Sopenharmony_ci break; 84862306a36Sopenharmony_ci case 'U': 84962306a36Sopenharmony_ci test_name = CHECK_KSM_UNMERGE; 85062306a36Sopenharmony_ci break; 85162306a36Sopenharmony_ci case 'Z': 85262306a36Sopenharmony_ci test_name = CHECK_KSM_ZERO_PAGE_MERGE; 85362306a36Sopenharmony_ci break; 85462306a36Sopenharmony_ci case 'N': 85562306a36Sopenharmony_ci test_name = CHECK_KSM_NUMA_MERGE; 85662306a36Sopenharmony_ci break; 85762306a36Sopenharmony_ci case 'P': 85862306a36Sopenharmony_ci test_name = KSM_MERGE_TIME; 85962306a36Sopenharmony_ci break; 86062306a36Sopenharmony_ci case 'H': 86162306a36Sopenharmony_ci test_name = KSM_MERGE_TIME_HUGE_PAGES; 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci case 'D': 86462306a36Sopenharmony_ci test_name = KSM_UNMERGE_TIME; 86562306a36Sopenharmony_ci break; 86662306a36Sopenharmony_ci case 'C': 86762306a36Sopenharmony_ci test_name = KSM_COW_TIME; 86862306a36Sopenharmony_ci break; 86962306a36Sopenharmony_ci default: 87062306a36Sopenharmony_ci return KSFT_FAIL; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (prot == 0) 87562306a36Sopenharmony_ci prot = str_to_prot(KSM_PROT_STR_DEFAULT); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (access(KSM_SYSFS_PATH, F_OK)) { 87862306a36Sopenharmony_ci printf("Config KSM not enabled\n"); 87962306a36Sopenharmony_ci return KSFT_SKIP; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (ksm_save_def(&ksm_sysfs_old)) { 88362306a36Sopenharmony_ci printf("Cannot save default tunables\n"); 88462306a36Sopenharmony_ci return KSFT_FAIL; 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (ksm_write_sysfs(KSM_FP("run"), 2) || 88862306a36Sopenharmony_ci ksm_write_sysfs(KSM_FP("sleep_millisecs"), 0) || 88962306a36Sopenharmony_ci numa_available() ? 0 : 89062306a36Sopenharmony_ci ksm_write_sysfs(KSM_FP("merge_across_nodes"), 1) || 89162306a36Sopenharmony_ci ksm_write_sysfs(KSM_FP("pages_to_scan"), page_count)) 89262306a36Sopenharmony_ci return KSFT_FAIL; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci switch (test_name) { 89562306a36Sopenharmony_ci case CHECK_KSM_MERGE: 89662306a36Sopenharmony_ci ret = check_ksm_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count, 89762306a36Sopenharmony_ci ksm_scan_limit_sec, page_size); 89862306a36Sopenharmony_ci break; 89962306a36Sopenharmony_ci case CHECK_KSM_UNMERGE: 90062306a36Sopenharmony_ci ret = check_ksm_unmerge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, 90162306a36Sopenharmony_ci ksm_scan_limit_sec, page_size); 90262306a36Sopenharmony_ci break; 90362306a36Sopenharmony_ci case CHECK_KSM_ZERO_PAGE_MERGE: 90462306a36Sopenharmony_ci ret = check_ksm_zero_page_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, 90562306a36Sopenharmony_ci page_count, ksm_scan_limit_sec, use_zero_pages, 90662306a36Sopenharmony_ci page_size); 90762306a36Sopenharmony_ci break; 90862306a36Sopenharmony_ci case CHECK_KSM_NUMA_MERGE: 90962306a36Sopenharmony_ci ret = check_ksm_numa_merge(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, 91062306a36Sopenharmony_ci ksm_scan_limit_sec, merge_across_nodes, page_size); 91162306a36Sopenharmony_ci break; 91262306a36Sopenharmony_ci case KSM_MERGE_TIME: 91362306a36Sopenharmony_ci if (size_MB == 0) { 91462306a36Sopenharmony_ci printf("Option '-s' is required.\n"); 91562306a36Sopenharmony_ci return KSFT_FAIL; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci ret = ksm_merge_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, 91862306a36Sopenharmony_ci ksm_scan_limit_sec, size_MB); 91962306a36Sopenharmony_ci break; 92062306a36Sopenharmony_ci case KSM_MERGE_TIME_HUGE_PAGES: 92162306a36Sopenharmony_ci if (size_MB == 0) { 92262306a36Sopenharmony_ci printf("Option '-s' is required.\n"); 92362306a36Sopenharmony_ci return KSFT_FAIL; 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci ret = ksm_merge_hugepages_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, 92662306a36Sopenharmony_ci ksm_scan_limit_sec, size_MB); 92762306a36Sopenharmony_ci break; 92862306a36Sopenharmony_ci case KSM_UNMERGE_TIME: 92962306a36Sopenharmony_ci if (size_MB == 0) { 93062306a36Sopenharmony_ci printf("Option '-s' is required.\n"); 93162306a36Sopenharmony_ci return KSFT_FAIL; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci ret = ksm_unmerge_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, 93462306a36Sopenharmony_ci ksm_scan_limit_sec, size_MB); 93562306a36Sopenharmony_ci break; 93662306a36Sopenharmony_ci case KSM_COW_TIME: 93762306a36Sopenharmony_ci ret = ksm_cow_time(merge_type, MAP_PRIVATE | MAP_ANONYMOUS, prot, 93862306a36Sopenharmony_ci ksm_scan_limit_sec, page_size); 93962306a36Sopenharmony_ci break; 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci if (ksm_restore(&ksm_sysfs_old)) { 94362306a36Sopenharmony_ci printf("Cannot restore default tunables\n"); 94462306a36Sopenharmony_ci return KSFT_FAIL; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci return ret; 94862306a36Sopenharmony_ci} 949