Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 
0003 #include <sys/mman.h>
0004 #include <stdbool.h>
0005 #include <time.h>
0006 #include <string.h>
0007 #include <numa.h>
0008 #include <unistd.h>
0009 #include <fcntl.h>
0010 #include <stdint.h>
0011 #include <err.h>
0012 
0013 #include "../kselftest.h"
0014 #include "../../../../include/vdso/time64.h"
0015 #include "util.h"
0016 
0017 #define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/"
0018 #define KSM_FP(s) (KSM_SYSFS_PATH s)
0019 #define KSM_SCAN_LIMIT_SEC_DEFAULT 120
0020 #define KSM_PAGE_COUNT_DEFAULT 10l
0021 #define KSM_PROT_STR_DEFAULT "rw"
0022 #define KSM_USE_ZERO_PAGES_DEFAULT false
0023 #define KSM_MERGE_ACROSS_NODES_DEFAULT true
0024 #define MB (1ul << 20)
0025 
0026 struct ksm_sysfs {
0027     unsigned long max_page_sharing;
0028     unsigned long merge_across_nodes;
0029     unsigned long pages_to_scan;
0030     unsigned long run;
0031     unsigned long sleep_millisecs;
0032     unsigned long stable_node_chains_prune_millisecs;
0033     unsigned long use_zero_pages;
0034 };
0035 
0036 enum ksm_test_name {
0037     CHECK_KSM_MERGE,
0038     CHECK_KSM_UNMERGE,
0039     CHECK_KSM_ZERO_PAGE_MERGE,
0040     CHECK_KSM_NUMA_MERGE,
0041     KSM_MERGE_TIME,
0042     KSM_MERGE_TIME_HUGE_PAGES,
0043     KSM_COW_TIME
0044 };
0045 
0046 static int ksm_write_sysfs(const char *file_path, unsigned long val)
0047 {
0048     FILE *f = fopen(file_path, "w");
0049 
0050     if (!f) {
0051         fprintf(stderr, "f %s\n", file_path);
0052         perror("fopen");
0053         return 1;
0054     }
0055     if (fprintf(f, "%lu", val) < 0) {
0056         perror("fprintf");
0057         fclose(f);
0058         return 1;
0059     }
0060     fclose(f);
0061 
0062     return 0;
0063 }
0064 
0065 static int ksm_read_sysfs(const char *file_path, unsigned long *val)
0066 {
0067     FILE *f = fopen(file_path, "r");
0068 
0069     if (!f) {
0070         fprintf(stderr, "f %s\n", file_path);
0071         perror("fopen");
0072         return 1;
0073     }
0074     if (fscanf(f, "%lu", val) != 1) {
0075         perror("fscanf");
0076         fclose(f);
0077         return 1;
0078     }
0079     fclose(f);
0080 
0081     return 0;
0082 }
0083 
0084 static int str_to_prot(char *prot_str)
0085 {
0086     int prot = 0;
0087 
0088     if ((strchr(prot_str, 'r')) != NULL)
0089         prot |= PROT_READ;
0090     if ((strchr(prot_str, 'w')) != NULL)
0091         prot |= PROT_WRITE;
0092     if ((strchr(prot_str, 'x')) != NULL)
0093         prot |= PROT_EXEC;
0094 
0095     return prot;
0096 }
0097 
0098 static void print_help(void)
0099 {
0100     printf("usage: ksm_tests [-h] <test type> [-a prot] [-p page_count] [-l timeout]\n"
0101            "[-z use_zero_pages] [-m merge_across_nodes] [-s size]\n");
0102 
0103     printf("Supported <test type>:\n"
0104            " -M (page merging)\n"
0105            " -Z (zero pages merging)\n"
0106            " -N (merging of pages in different NUMA nodes)\n"
0107            " -U (page unmerging)\n"
0108            " -P evaluate merging time and speed.\n"
0109            "    For this test, the size of duplicated memory area (in MiB)\n"
0110            "    must be provided using -s option\n"
0111                  " -H evaluate merging time and speed of area allocated mostly with huge pages\n"
0112            "    For this test, the size of duplicated memory area (in MiB)\n"
0113            "    must be provided using -s option\n"
0114            " -C evaluate the time required to break COW of merged pages.\n\n");
0115 
0116     printf(" -a: specify the access protections of pages.\n"
0117            "     <prot> must be of the form [rwx].\n"
0118            "     Default: %s\n", KSM_PROT_STR_DEFAULT);
0119     printf(" -p: specify the number of pages to test.\n"
0120            "     Default: %ld\n", KSM_PAGE_COUNT_DEFAULT);
0121     printf(" -l: limit the maximum running time (in seconds) for a test.\n"
0122            "     Default: %d seconds\n", KSM_SCAN_LIMIT_SEC_DEFAULT);
0123     printf(" -z: change use_zero_pages tunable\n"
0124            "     Default: %d\n", KSM_USE_ZERO_PAGES_DEFAULT);
0125     printf(" -m: change merge_across_nodes tunable\n"
0126            "     Default: %d\n", KSM_MERGE_ACROSS_NODES_DEFAULT);
0127     printf(" -s: the size of duplicated memory area (in MiB)\n");
0128 
0129     exit(0);
0130 }
0131 
0132 static void  *allocate_memory(void *ptr, int prot, int mapping, char data, size_t map_size)
0133 {
0134     void *map_ptr = mmap(ptr, map_size, PROT_WRITE, mapping, -1, 0);
0135 
0136     if (!map_ptr) {
0137         perror("mmap");
0138         return NULL;
0139     }
0140     memset(map_ptr, data, map_size);
0141     if (mprotect(map_ptr, map_size, prot)) {
0142         perror("mprotect");
0143         munmap(map_ptr, map_size);
0144         return NULL;
0145     }
0146 
0147     return map_ptr;
0148 }
0149 
0150 static int ksm_do_scan(int scan_count, struct timespec start_time, int timeout)
0151 {
0152     struct timespec cur_time;
0153     unsigned long cur_scan, init_scan;
0154 
0155     if (ksm_read_sysfs(KSM_FP("full_scans"), &init_scan))
0156         return 1;
0157     cur_scan = init_scan;
0158 
0159     while (cur_scan < init_scan + scan_count) {
0160         if (ksm_read_sysfs(KSM_FP("full_scans"), &cur_scan))
0161             return 1;
0162         if (clock_gettime(CLOCK_MONOTONIC_RAW, &cur_time)) {
0163             perror("clock_gettime");
0164             return 1;
0165         }
0166         if ((cur_time.tv_sec - start_time.tv_sec) > timeout) {
0167             printf("Scan time limit exceeded\n");
0168             return 1;
0169         }
0170     }
0171 
0172     return 0;
0173 }
0174 
0175 static int ksm_merge_pages(void *addr, size_t size, struct timespec start_time, int timeout)
0176 {
0177     if (madvise(addr, size, MADV_MERGEABLE)) {
0178         perror("madvise");
0179         return 1;
0180     }
0181     if (ksm_write_sysfs(KSM_FP("run"), 1))
0182         return 1;
0183 
0184     /* Since merging occurs only after 2 scans, make sure to get at least 2 full scans */
0185     if (ksm_do_scan(2, start_time, timeout))
0186         return 1;
0187 
0188     return 0;
0189 }
0190 
0191 static bool assert_ksm_pages_count(long dupl_page_count)
0192 {
0193     unsigned long max_page_sharing, pages_sharing, pages_shared;
0194 
0195     if (ksm_read_sysfs(KSM_FP("pages_shared"), &pages_shared) ||
0196         ksm_read_sysfs(KSM_FP("pages_sharing"), &pages_sharing) ||
0197         ksm_read_sysfs(KSM_FP("max_page_sharing"), &max_page_sharing))
0198         return false;
0199 
0200     /*
0201      * Since there must be at least 2 pages for merging and 1 page can be
0202      * shared with the limited number of pages (max_page_sharing), sometimes
0203      * there are 'leftover' pages that cannot be merged. For example, if there
0204      * are 11 pages and max_page_sharing = 10, then only 10 pages will be
0205      * merged and the 11th page won't be affected. As a result, when the number
0206      * of duplicate pages is divided by max_page_sharing and the remainder is 1,
0207      * pages_shared and pages_sharing values will be equal between dupl_page_count
0208      * and dupl_page_count - 1.
0209      */
0210     if (dupl_page_count % max_page_sharing == 1 || dupl_page_count % max_page_sharing == 0) {
0211         if (pages_shared == dupl_page_count / max_page_sharing &&
0212             pages_sharing == pages_shared * (max_page_sharing - 1))
0213             return true;
0214     } else {
0215         if (pages_shared == (dupl_page_count / max_page_sharing + 1) &&
0216             pages_sharing == dupl_page_count - pages_shared)
0217             return true;
0218     }
0219 
0220     return false;
0221 }
0222 
0223 static int ksm_save_def(struct ksm_sysfs *ksm_sysfs)
0224 {
0225     if (ksm_read_sysfs(KSM_FP("max_page_sharing"), &ksm_sysfs->max_page_sharing) ||
0226         numa_available() ? 0 :
0227         ksm_read_sysfs(KSM_FP("merge_across_nodes"), &ksm_sysfs->merge_across_nodes) ||
0228         ksm_read_sysfs(KSM_FP("sleep_millisecs"), &ksm_sysfs->sleep_millisecs) ||
0229         ksm_read_sysfs(KSM_FP("pages_to_scan"), &ksm_sysfs->pages_to_scan) ||
0230         ksm_read_sysfs(KSM_FP("run"), &ksm_sysfs->run) ||
0231         ksm_read_sysfs(KSM_FP("stable_node_chains_prune_millisecs"),
0232                &ksm_sysfs->stable_node_chains_prune_millisecs) ||
0233         ksm_read_sysfs(KSM_FP("use_zero_pages"), &ksm_sysfs->use_zero_pages))
0234         return 1;
0235 
0236     return 0;
0237 }
0238 
0239 static int ksm_restore(struct ksm_sysfs *ksm_sysfs)
0240 {
0241     if (ksm_write_sysfs(KSM_FP("max_page_sharing"), ksm_sysfs->max_page_sharing) ||
0242         numa_available() ? 0 :
0243         ksm_write_sysfs(KSM_FP("merge_across_nodes"), ksm_sysfs->merge_across_nodes) ||
0244         ksm_write_sysfs(KSM_FP("pages_to_scan"), ksm_sysfs->pages_to_scan) ||
0245         ksm_write_sysfs(KSM_FP("run"), ksm_sysfs->run) ||
0246         ksm_write_sysfs(KSM_FP("sleep_millisecs"), ksm_sysfs->sleep_millisecs) ||
0247         ksm_write_sysfs(KSM_FP("stable_node_chains_prune_millisecs"),
0248                 ksm_sysfs->stable_node_chains_prune_millisecs) ||
0249         ksm_write_sysfs(KSM_FP("use_zero_pages"), ksm_sysfs->use_zero_pages))
0250         return 1;
0251 
0252     return 0;
0253 }
0254 
0255 static int check_ksm_merge(int mapping, int prot, long page_count, int timeout, size_t page_size)
0256 {
0257     void *map_ptr;
0258     struct timespec start_time;
0259 
0260     if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
0261         perror("clock_gettime");
0262         return KSFT_FAIL;
0263     }
0264 
0265     /* fill pages with the same data and merge them */
0266     map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count);
0267     if (!map_ptr)
0268         return KSFT_FAIL;
0269 
0270     if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout))
0271         goto err_out;
0272 
0273     /* verify that the right number of pages are merged */
0274     if (assert_ksm_pages_count(page_count)) {
0275         printf("OK\n");
0276         munmap(map_ptr, page_size * page_count);
0277         return KSFT_PASS;
0278     }
0279 
0280 err_out:
0281     printf("Not OK\n");
0282     munmap(map_ptr, page_size * page_count);
0283     return KSFT_FAIL;
0284 }
0285 
0286 static int check_ksm_unmerge(int mapping, int prot, int timeout, size_t page_size)
0287 {
0288     void *map_ptr;
0289     struct timespec start_time;
0290     int page_count = 2;
0291 
0292     if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
0293         perror("clock_gettime");
0294         return KSFT_FAIL;
0295     }
0296 
0297     /* fill pages with the same data and merge them */
0298     map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count);
0299     if (!map_ptr)
0300         return KSFT_FAIL;
0301 
0302     if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout))
0303         goto err_out;
0304 
0305     /* change 1 byte in each of the 2 pages -- KSM must automatically unmerge them */
0306     memset(map_ptr, '-', 1);
0307     memset(map_ptr + page_size, '+', 1);
0308 
0309     /* get at least 1 scan, so KSM can detect that the pages were modified */
0310     if (ksm_do_scan(1, start_time, timeout))
0311         goto err_out;
0312 
0313     /* check that unmerging was successful and 0 pages are currently merged */
0314     if (assert_ksm_pages_count(0)) {
0315         printf("OK\n");
0316         munmap(map_ptr, page_size * page_count);
0317         return KSFT_PASS;
0318     }
0319 
0320 err_out:
0321     printf("Not OK\n");
0322     munmap(map_ptr, page_size * page_count);
0323     return KSFT_FAIL;
0324 }
0325 
0326 static int check_ksm_zero_page_merge(int mapping, int prot, long page_count, int timeout,
0327                      bool use_zero_pages, size_t page_size)
0328 {
0329     void *map_ptr;
0330     struct timespec start_time;
0331 
0332     if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
0333         perror("clock_gettime");
0334         return KSFT_FAIL;
0335     }
0336 
0337     if (ksm_write_sysfs(KSM_FP("use_zero_pages"), use_zero_pages))
0338         return KSFT_FAIL;
0339 
0340     /* fill pages with zero and try to merge them */
0341     map_ptr = allocate_memory(NULL, prot, mapping, 0, page_size * page_count);
0342     if (!map_ptr)
0343         return KSFT_FAIL;
0344 
0345     if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout))
0346         goto err_out;
0347 
0348        /*
0349     * verify that the right number of pages are merged:
0350     * 1) if use_zero_pages is set to 1, empty pages are merged
0351     *    with the kernel zero page instead of with each other;
0352     * 2) if use_zero_pages is set to 0, empty pages are not treated specially
0353     *    and merged as usual.
0354     */
0355     if (use_zero_pages && !assert_ksm_pages_count(0))
0356         goto err_out;
0357     else if (!use_zero_pages && !assert_ksm_pages_count(page_count))
0358         goto err_out;
0359 
0360     printf("OK\n");
0361     munmap(map_ptr, page_size * page_count);
0362     return KSFT_PASS;
0363 
0364 err_out:
0365     printf("Not OK\n");
0366     munmap(map_ptr, page_size * page_count);
0367     return KSFT_FAIL;
0368 }
0369 
0370 static int get_next_mem_node(int node)
0371 {
0372 
0373     long node_size;
0374     int mem_node = 0;
0375     int i, max_node = numa_max_node();
0376 
0377     for (i = node + 1; i <= max_node + node; i++) {
0378         mem_node = i % (max_node + 1);
0379         node_size = numa_node_size(mem_node, NULL);
0380         if (node_size > 0)
0381             break;
0382     }
0383     return mem_node;
0384 }
0385 
0386 static int get_first_mem_node(void)
0387 {
0388     return get_next_mem_node(numa_max_node());
0389 }
0390 
0391 static int check_ksm_numa_merge(int mapping, int prot, int timeout, bool merge_across_nodes,
0392                 size_t page_size)
0393 {
0394     void *numa1_map_ptr, *numa2_map_ptr;
0395     struct timespec start_time;
0396     int page_count = 2;
0397     int first_node;
0398 
0399     if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
0400         perror("clock_gettime");
0401         return KSFT_FAIL;
0402     }
0403 
0404     if (numa_available() < 0) {
0405         perror("NUMA support not enabled");
0406         return KSFT_SKIP;
0407     }
0408     if (numa_num_configured_nodes() <= 1) {
0409         printf("At least 2 NUMA nodes must be available\n");
0410         return KSFT_SKIP;
0411     }
0412     if (ksm_write_sysfs(KSM_FP("merge_across_nodes"), merge_across_nodes))
0413         return KSFT_FAIL;
0414 
0415     /* allocate 2 pages in 2 different NUMA nodes and fill them with the same data */
0416     first_node = get_first_mem_node();
0417     numa1_map_ptr = numa_alloc_onnode(page_size, first_node);
0418     numa2_map_ptr = numa_alloc_onnode(page_size, get_next_mem_node(first_node));
0419     if (!numa1_map_ptr || !numa2_map_ptr) {
0420         perror("numa_alloc_onnode");
0421         return KSFT_FAIL;
0422     }
0423 
0424     memset(numa1_map_ptr, '*', page_size);
0425     memset(numa2_map_ptr, '*', page_size);
0426 
0427     /* try to merge the pages */
0428     if (ksm_merge_pages(numa1_map_ptr, page_size, start_time, timeout) ||
0429         ksm_merge_pages(numa2_map_ptr, page_size, start_time, timeout))
0430         goto err_out;
0431 
0432        /*
0433     * verify that the right number of pages are merged:
0434     * 1) if merge_across_nodes was enabled, 2 duplicate pages will be merged;
0435     * 2) if merge_across_nodes = 0, there must be 0 merged pages, since there is
0436     *    only 1 unique page in each node and they can't be shared.
0437     */
0438     if (merge_across_nodes && !assert_ksm_pages_count(page_count))
0439         goto err_out;
0440     else if (!merge_across_nodes && !assert_ksm_pages_count(0))
0441         goto err_out;
0442 
0443     numa_free(numa1_map_ptr, page_size);
0444     numa_free(numa2_map_ptr, page_size);
0445     printf("OK\n");
0446     return KSFT_PASS;
0447 
0448 err_out:
0449     numa_free(numa1_map_ptr, page_size);
0450     numa_free(numa2_map_ptr, page_size);
0451     printf("Not OK\n");
0452     return KSFT_FAIL;
0453 }
0454 
0455 static int ksm_merge_hugepages_time(int mapping, int prot, int timeout, size_t map_size)
0456 {
0457     void *map_ptr, *map_ptr_orig;
0458     struct timespec start_time, end_time;
0459     unsigned long scan_time_ns;
0460     int pagemap_fd, n_normal_pages, n_huge_pages;
0461 
0462     map_size *= MB;
0463     size_t len = map_size;
0464 
0465     len -= len % HPAGE_SIZE;
0466     map_ptr_orig = mmap(NULL, len + HPAGE_SIZE, PROT_READ | PROT_WRITE,
0467             MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE, -1, 0);
0468     map_ptr = map_ptr_orig + HPAGE_SIZE - (uintptr_t)map_ptr_orig % HPAGE_SIZE;
0469 
0470     if (map_ptr_orig == MAP_FAILED)
0471         err(2, "initial mmap");
0472 
0473     if (madvise(map_ptr, len + HPAGE_SIZE, MADV_HUGEPAGE))
0474         err(2, "MADV_HUGEPAGE");
0475 
0476     pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
0477     if (pagemap_fd < 0)
0478         err(2, "open pagemap");
0479 
0480     n_normal_pages = 0;
0481     n_huge_pages = 0;
0482     for (void *p = map_ptr; p < map_ptr + len; p += HPAGE_SIZE) {
0483         if (allocate_transhuge(p, pagemap_fd) < 0)
0484             n_normal_pages++;
0485         else
0486             n_huge_pages++;
0487     }
0488     printf("Number of normal pages:    %d\n", n_normal_pages);
0489     printf("Number of huge pages:    %d\n", n_huge_pages);
0490 
0491     memset(map_ptr, '*', len);
0492 
0493     if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
0494         perror("clock_gettime");
0495         goto err_out;
0496     }
0497     if (ksm_merge_pages(map_ptr, map_size, start_time, timeout))
0498         goto err_out;
0499     if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
0500         perror("clock_gettime");
0501         goto err_out;
0502     }
0503 
0504     scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
0505                (end_time.tv_nsec - start_time.tv_nsec);
0506 
0507     printf("Total size:    %lu MiB\n", map_size / MB);
0508     printf("Total time:    %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
0509            scan_time_ns % NSEC_PER_SEC);
0510     printf("Average speed:  %.3f MiB/s\n", (map_size / MB) /
0511                            ((double)scan_time_ns / NSEC_PER_SEC));
0512 
0513     munmap(map_ptr_orig, len + HPAGE_SIZE);
0514     return KSFT_PASS;
0515 
0516 err_out:
0517     printf("Not OK\n");
0518     munmap(map_ptr_orig, len + HPAGE_SIZE);
0519     return KSFT_FAIL;
0520 }
0521 
0522 static int ksm_merge_time(int mapping, int prot, int timeout, size_t map_size)
0523 {
0524     void *map_ptr;
0525     struct timespec start_time, end_time;
0526     unsigned long scan_time_ns;
0527 
0528     map_size *= MB;
0529 
0530     map_ptr = allocate_memory(NULL, prot, mapping, '*', map_size);
0531     if (!map_ptr)
0532         return KSFT_FAIL;
0533 
0534     if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
0535         perror("clock_gettime");
0536         goto err_out;
0537     }
0538     if (ksm_merge_pages(map_ptr, map_size, start_time, timeout))
0539         goto err_out;
0540     if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
0541         perror("clock_gettime");
0542         goto err_out;
0543     }
0544 
0545     scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
0546                (end_time.tv_nsec - start_time.tv_nsec);
0547 
0548     printf("Total size:    %lu MiB\n", map_size / MB);
0549     printf("Total time:    %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
0550            scan_time_ns % NSEC_PER_SEC);
0551     printf("Average speed:  %.3f MiB/s\n", (map_size / MB) /
0552                            ((double)scan_time_ns / NSEC_PER_SEC));
0553 
0554     munmap(map_ptr, map_size);
0555     return KSFT_PASS;
0556 
0557 err_out:
0558     printf("Not OK\n");
0559     munmap(map_ptr, map_size);
0560     return KSFT_FAIL;
0561 }
0562 
0563 static int ksm_cow_time(int mapping, int prot, int timeout, size_t page_size)
0564 {
0565     void *map_ptr;
0566     struct timespec start_time, end_time;
0567     unsigned long cow_time_ns;
0568 
0569     /* page_count must be less than 2*page_size */
0570     size_t page_count = 4000;
0571 
0572     map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count);
0573     if (!map_ptr)
0574         return KSFT_FAIL;
0575 
0576     if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
0577         perror("clock_gettime");
0578         return KSFT_FAIL;
0579     }
0580     for (size_t i = 0; i < page_count - 1; i = i + 2)
0581         memset(map_ptr + page_size * i, '-', 1);
0582     if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
0583         perror("clock_gettime");
0584         return KSFT_FAIL;
0585     }
0586 
0587     cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
0588                (end_time.tv_nsec - start_time.tv_nsec);
0589 
0590     printf("Total size:    %lu MiB\n\n", (page_size * page_count) / MB);
0591     printf("Not merged pages:\n");
0592     printf("Total time:     %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
0593            cow_time_ns % NSEC_PER_SEC);
0594     printf("Average speed:  %.3f MiB/s\n\n", ((page_size * (page_count / 2)) / MB) /
0595                            ((double)cow_time_ns / NSEC_PER_SEC));
0596 
0597     /* Create 2000 pairs of duplicate pages */
0598     for (size_t i = 0; i < page_count - 1; i = i + 2) {
0599         memset(map_ptr + page_size * i, '+', i / 2 + 1);
0600         memset(map_ptr + page_size * (i + 1), '+', i / 2 + 1);
0601     }
0602     if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout))
0603         goto err_out;
0604 
0605     if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
0606         perror("clock_gettime");
0607         goto err_out;
0608     }
0609     for (size_t i = 0; i < page_count - 1; i = i + 2)
0610         memset(map_ptr + page_size * i, '-', 1);
0611     if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
0612         perror("clock_gettime");
0613         goto err_out;
0614     }
0615 
0616     cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
0617                (end_time.tv_nsec - start_time.tv_nsec);
0618 
0619     printf("Merged pages:\n");
0620     printf("Total time:     %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
0621            cow_time_ns % NSEC_PER_SEC);
0622     printf("Average speed:  %.3f MiB/s\n", ((page_size * (page_count / 2)) / MB) /
0623                            ((double)cow_time_ns / NSEC_PER_SEC));
0624 
0625     munmap(map_ptr, page_size * page_count);
0626     return KSFT_PASS;
0627 
0628 err_out:
0629     printf("Not OK\n");
0630     munmap(map_ptr, page_size * page_count);
0631     return KSFT_FAIL;
0632 }
0633 
0634 int main(int argc, char *argv[])
0635 {
0636     int ret, opt;
0637     int prot = 0;
0638     int ksm_scan_limit_sec = KSM_SCAN_LIMIT_SEC_DEFAULT;
0639     long page_count = KSM_PAGE_COUNT_DEFAULT;
0640     size_t page_size = sysconf(_SC_PAGESIZE);
0641     struct ksm_sysfs ksm_sysfs_old;
0642     int test_name = CHECK_KSM_MERGE;
0643     bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT;
0644     bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT;
0645     long size_MB = 0;
0646 
0647     while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNPCH")) != -1) {
0648         switch (opt) {
0649         case 'a':
0650             prot = str_to_prot(optarg);
0651             break;
0652         case 'p':
0653             page_count = atol(optarg);
0654             if (page_count <= 0) {
0655                 printf("The number of pages must be greater than 0\n");
0656                 return KSFT_FAIL;
0657             }
0658             break;
0659         case 'l':
0660             ksm_scan_limit_sec = atoi(optarg);
0661             if (ksm_scan_limit_sec <= 0) {
0662                 printf("Timeout value must be greater than 0\n");
0663                 return KSFT_FAIL;
0664             }
0665             break;
0666         case 'h':
0667             print_help();
0668             break;
0669         case 'z':
0670             if (strcmp(optarg, "0") == 0)
0671                 use_zero_pages = 0;
0672             else
0673                 use_zero_pages = 1;
0674             break;
0675         case 'm':
0676             if (strcmp(optarg, "0") == 0)
0677                 merge_across_nodes = 0;
0678             else
0679                 merge_across_nodes = 1;
0680             break;
0681         case 's':
0682             size_MB = atoi(optarg);
0683             if (size_MB <= 0) {
0684                 printf("Size must be greater than 0\n");
0685                 return KSFT_FAIL;
0686             }
0687         case 'M':
0688             break;
0689         case 'U':
0690             test_name = CHECK_KSM_UNMERGE;
0691             break;
0692         case 'Z':
0693             test_name = CHECK_KSM_ZERO_PAGE_MERGE;
0694             break;
0695         case 'N':
0696             test_name = CHECK_KSM_NUMA_MERGE;
0697             break;
0698         case 'P':
0699             test_name = KSM_MERGE_TIME;
0700             break;
0701         case 'H':
0702             test_name = KSM_MERGE_TIME_HUGE_PAGES;
0703             break;
0704         case 'C':
0705             test_name = KSM_COW_TIME;
0706             break;
0707         default:
0708             return KSFT_FAIL;
0709         }
0710     }
0711 
0712     if (prot == 0)
0713         prot = str_to_prot(KSM_PROT_STR_DEFAULT);
0714 
0715     if (access(KSM_SYSFS_PATH, F_OK)) {
0716         printf("Config KSM not enabled\n");
0717         return KSFT_SKIP;
0718     }
0719 
0720     if (ksm_save_def(&ksm_sysfs_old)) {
0721         printf("Cannot save default tunables\n");
0722         return KSFT_FAIL;
0723     }
0724 
0725     if (ksm_write_sysfs(KSM_FP("run"), 2) ||
0726         ksm_write_sysfs(KSM_FP("sleep_millisecs"), 0) ||
0727         numa_available() ? 0 :
0728         ksm_write_sysfs(KSM_FP("merge_across_nodes"), 1) ||
0729         ksm_write_sysfs(KSM_FP("pages_to_scan"), page_count))
0730         return KSFT_FAIL;
0731 
0732     switch (test_name) {
0733     case CHECK_KSM_MERGE:
0734         ret = check_ksm_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count,
0735                       ksm_scan_limit_sec, page_size);
0736         break;
0737     case CHECK_KSM_UNMERGE:
0738         ret = check_ksm_unmerge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
0739                     page_size);
0740         break;
0741     case CHECK_KSM_ZERO_PAGE_MERGE:
0742         ret = check_ksm_zero_page_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count,
0743                         ksm_scan_limit_sec, use_zero_pages, page_size);
0744         break;
0745     case CHECK_KSM_NUMA_MERGE:
0746         ret = check_ksm_numa_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
0747                        merge_across_nodes, page_size);
0748         break;
0749     case KSM_MERGE_TIME:
0750         if (size_MB == 0) {
0751             printf("Option '-s' is required.\n");
0752             return KSFT_FAIL;
0753         }
0754         ret = ksm_merge_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
0755                      size_MB);
0756         break;
0757     case KSM_MERGE_TIME_HUGE_PAGES:
0758         if (size_MB == 0) {
0759             printf("Option '-s' is required.\n");
0760             return KSFT_FAIL;
0761         }
0762         ret = ksm_merge_hugepages_time(MAP_PRIVATE | MAP_ANONYMOUS, prot,
0763                 ksm_scan_limit_sec, size_MB);
0764         break;
0765     case KSM_COW_TIME:
0766         ret = ksm_cow_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
0767                    page_size);
0768         break;
0769     }
0770 
0771     if (ksm_restore(&ksm_sysfs_old)) {
0772         printf("Cannot restore default tunables\n");
0773         return KSFT_FAIL;
0774     }
0775 
0776     return ret;
0777 }