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