Skip to content

Commit 924a11b

Browse files
zhans00torvalds
authored andcommitted
selftests: vm: add COW time test for KSM pages
Since merged pages are copied every time they need to be modified, the write access time is different between shared and non-shared pages. Add ksm_cow_time() function which evaluates latency of these COW breaks. First, 4000 pages are allocated and the time, required to modify 1 byte in every other page, is measured. After this, the pages are merged into 2000 pairs and in each pair, 1 page is modified (i.e. they are decoupled) to detect COW breaks. The time needed to break COW of merged pages is then compared with performance of non-shared pages. The test is run as follows: ./ksm_tests -C The output: Total size: 15 MiB Not merged pages: Total time: 0.002185489 s Average speed: 3202.945 MiB/s Merged pages: Total time: 0.004386872 s Average speed: 1595.670 MiB/s Link: https://lkml.kernel.org/r/1d03ee0d1b341959d4b61672c6401d498bff5652.1629386192.git.zhansayabagdaulet@gmail.com Signed-off-by: Zhansaya Bagdauletkyzy <[email protected]> Reviewed-by: Tyler Hicks <[email protected]> Reviewed-by: Pavel Tatashin <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 9e7cb94 commit 924a11b

File tree

1 file changed

+83
-3
lines changed

1 file changed

+83
-3
lines changed

tools/testing/selftests/vm/ksm_tests.c

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ enum ksm_test_name {
3333
CHECK_KSM_UNMERGE,
3434
CHECK_KSM_ZERO_PAGE_MERGE,
3535
CHECK_KSM_NUMA_MERGE,
36-
KSM_MERGE_TIME
36+
KSM_MERGE_TIME,
37+
KSM_COW_TIME
3738
};
3839

3940
static int ksm_write_sysfs(const char *file_path, unsigned long val)
@@ -98,7 +99,8 @@ static void print_help(void)
9899
" -U (page unmerging)\n"
99100
" -P evaluate merging time and speed.\n"
100101
" For this test, the size of duplicated memory area (in MiB)\n"
101-
" must be provided using -s option\n\n");
102+
" must be provided using -s option\n"
103+
" -C evaluate the time required to break COW of merged pages.\n\n");
102104

103105
printf(" -a: specify the access protections of pages.\n"
104106
" <prot> must be of the form [rwx].\n"
@@ -455,6 +457,77 @@ static int ksm_merge_time(int mapping, int prot, int timeout, size_t map_size)
455457
return KSFT_FAIL;
456458
}
457459

460+
static int ksm_cow_time(int mapping, int prot, int timeout, size_t page_size)
461+
{
462+
void *map_ptr;
463+
struct timespec start_time, end_time;
464+
unsigned long cow_time_ns;
465+
466+
/* page_count must be less than 2*page_size */
467+
size_t page_count = 4000;
468+
469+
map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count);
470+
if (!map_ptr)
471+
return KSFT_FAIL;
472+
473+
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
474+
perror("clock_gettime");
475+
return KSFT_FAIL;
476+
}
477+
for (size_t i = 0; i < page_count - 1; i = i + 2)
478+
memset(map_ptr + page_size * i, '-', 1);
479+
if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
480+
perror("clock_gettime");
481+
return KSFT_FAIL;
482+
}
483+
484+
cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
485+
(end_time.tv_nsec - start_time.tv_nsec);
486+
487+
printf("Total size: %lu MiB\n\n", (page_size * page_count) / MB);
488+
printf("Not merged pages:\n");
489+
printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
490+
cow_time_ns % NSEC_PER_SEC);
491+
printf("Average speed: %.3f MiB/s\n\n", ((page_size * (page_count / 2)) / MB) /
492+
((double)cow_time_ns / NSEC_PER_SEC));
493+
494+
/* Create 2000 pairs of duplicate pages */
495+
for (size_t i = 0; i < page_count - 1; i = i + 2) {
496+
memset(map_ptr + page_size * i, '+', i / 2 + 1);
497+
memset(map_ptr + page_size * (i + 1), '+', i / 2 + 1);
498+
}
499+
if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout))
500+
goto err_out;
501+
502+
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
503+
perror("clock_gettime");
504+
goto err_out;
505+
}
506+
for (size_t i = 0; i < page_count - 1; i = i + 2)
507+
memset(map_ptr + page_size * i, '-', 1);
508+
if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
509+
perror("clock_gettime");
510+
goto err_out;
511+
}
512+
513+
cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
514+
(end_time.tv_nsec - start_time.tv_nsec);
515+
516+
printf("Merged pages:\n");
517+
printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
518+
cow_time_ns % NSEC_PER_SEC);
519+
printf("Average speed: %.3f MiB/s\n", ((page_size * (page_count / 2)) / MB) /
520+
((double)cow_time_ns / NSEC_PER_SEC));
521+
522+
munmap(map_ptr, page_size * page_count);
523+
return KSFT_PASS;
524+
525+
err_out:
526+
printf("Not OK\n");
527+
munmap(map_ptr, page_size * page_count);
528+
return KSFT_FAIL;
529+
}
530+
458531
int main(int argc, char *argv[])
459532
{
460533
int ret, opt;
@@ -468,7 +541,7 @@ int main(int argc, char *argv[])
468541
bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT;
469542
long size_MB = 0;
470543

471-
while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNP")) != -1) {
544+
while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNPC")) != -1) {
472545
switch (opt) {
473546
case 'a':
474547
prot = str_to_prot(optarg);
@@ -522,6 +595,9 @@ int main(int argc, char *argv[])
522595
case 'P':
523596
test_name = KSM_MERGE_TIME;
524597
break;
598+
case 'C':
599+
test_name = KSM_COW_TIME;
600+
break;
525601
default:
526602
return KSFT_FAIL;
527603
}
@@ -571,6 +647,10 @@ int main(int argc, char *argv[])
571647
ret = ksm_merge_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
572648
size_MB);
573649
break;
650+
case KSM_COW_TIME:
651+
ret = ksm_cow_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
652+
page_size);
653+
break;
574654
}
575655

576656
if (ksm_restore(&ksm_sysfs_old)) {

0 commit comments

Comments
 (0)