Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
fork

Configure Feed

Select the types of activity you want to include in your feed.

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