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

selftests/vm: add test to measure MADV_UNMERGEABLE performance

Let's add a test to measure performance of KSM breaking not triggered via
COW, but triggered by disabling KSM on an area filled with KSM pages via
MADV_UNMERGEABLE.

Link: https://lkml.kernel.org/r/20221021101141.84170-2-david@redhat.com
Signed-off-by: David Hildenbrand <david@redhat.com>
Acked-by: Peter Xu <peterx@redhat.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Jason Gunthorpe <jgg@nvidia.com>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

David Hildenbrand and committed by
Andrew Morton
5036880e c31783ee

+74 -2
+74 -2
tools/testing/selftests/vm/ksm_tests.c
··· 40 40 CHECK_KSM_NUMA_MERGE, 41 41 KSM_MERGE_TIME, 42 42 KSM_MERGE_TIME_HUGE_PAGES, 43 + KSM_UNMERGE_TIME, 43 44 KSM_COW_TIME 44 45 }; 45 46 ··· 109 108 " -P evaluate merging time and speed.\n" 110 109 " For this test, the size of duplicated memory area (in MiB)\n" 111 110 " must be provided using -s option\n" 112 - " -H evaluate merging time and speed of area allocated mostly with huge pages\n" 111 + " -H evaluate merging time and speed of area allocated mostly with huge pages\n" 112 + " For this test, the size of duplicated memory area (in MiB)\n" 113 + " must be provided using -s option\n" 114 + " -D evaluate unmerging time and speed when disabling KSM.\n" 113 115 " For this test, the size of duplicated memory area (in MiB)\n" 114 116 " must be provided using -s option\n" 115 117 " -C evaluate the time required to break COW of merged pages.\n\n"); ··· 189 185 if (ksm_do_scan(2, start_time, timeout)) 190 186 return 1; 191 187 188 + return 0; 189 + } 190 + 191 + static int ksm_unmerge_pages(void *addr, size_t size, 192 + struct timespec start_time, int timeout) 193 + { 194 + if (madvise(addr, size, MADV_UNMERGEABLE)) { 195 + perror("madvise"); 196 + return 1; 197 + } 192 198 return 0; 193 199 } 194 200 ··· 574 560 return KSFT_FAIL; 575 561 } 576 562 563 + static int ksm_unmerge_time(int mapping, int prot, int timeout, size_t map_size) 564 + { 565 + void *map_ptr; 566 + struct timespec start_time, end_time; 567 + unsigned long scan_time_ns; 568 + 569 + map_size *= MB; 570 + 571 + map_ptr = allocate_memory(NULL, prot, mapping, '*', map_size); 572 + if (!map_ptr) 573 + return KSFT_FAIL; 574 + if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 575 + perror("clock_gettime"); 576 + goto err_out; 577 + } 578 + if (ksm_merge_pages(map_ptr, map_size, start_time, timeout)) 579 + goto err_out; 580 + 581 + if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) { 582 + perror("clock_gettime"); 583 + goto err_out; 584 + } 585 + if (ksm_unmerge_pages(map_ptr, map_size, start_time, timeout)) 586 + goto err_out; 587 + if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) { 588 + perror("clock_gettime"); 589 + goto err_out; 590 + } 591 + 592 + scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC + 593 + (end_time.tv_nsec - start_time.tv_nsec); 594 + 595 + printf("Total size: %lu MiB\n", map_size / MB); 596 + printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC, 597 + scan_time_ns % NSEC_PER_SEC); 598 + printf("Average speed: %.3f MiB/s\n", (map_size / MB) / 599 + ((double)scan_time_ns / NSEC_PER_SEC)); 600 + 601 + munmap(map_ptr, map_size); 602 + return KSFT_PASS; 603 + 604 + err_out: 605 + printf("Not OK\n"); 606 + munmap(map_ptr, map_size); 607 + return KSFT_FAIL; 608 + } 609 + 577 610 static int ksm_cow_time(int mapping, int prot, int timeout, size_t page_size) 578 611 { 579 612 void *map_ptr; ··· 705 644 bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT; 706 645 long size_MB = 0; 707 646 708 - while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNPCH")) != -1) { 647 + while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNPCHD")) != -1) { 709 648 switch (opt) { 710 649 case 'a': 711 650 prot = str_to_prot(optarg); ··· 761 700 break; 762 701 case 'H': 763 702 test_name = KSM_MERGE_TIME_HUGE_PAGES; 703 + break; 704 + case 'D': 705 + test_name = KSM_UNMERGE_TIME; 764 706 break; 765 707 case 'C': 766 708 test_name = KSM_COW_TIME; ··· 825 761 } 826 762 ret = ksm_merge_hugepages_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, 827 763 ksm_scan_limit_sec, size_MB); 764 + break; 765 + case KSM_UNMERGE_TIME: 766 + if (size_MB == 0) { 767 + printf("Option '-s' is required.\n"); 768 + return KSFT_FAIL; 769 + } 770 + ret = ksm_unmerge_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, 771 + ksm_scan_limit_sec, size_MB); 828 772 break; 829 773 case KSM_COW_TIME: 830 774 ret = ksm_cow_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,