Commit 924a11bd authored by Zhansaya Bagdauletkyzy's avatar Zhansaya Bagdauletkyzy Committed by Linus Torvalds

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.comSigned-off-by: default avatarZhansaya Bagdauletkyzy <zhansayabagdaulet@gmail.com>
Reviewed-by: default avatarTyler Hicks <tyhicks@linux.microsoft.com>
Reviewed-by: default avatarPavel Tatashin <pasha.tatashin@soleen.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 9e7cb94c
...@@ -33,7 +33,8 @@ enum ksm_test_name { ...@@ -33,7 +33,8 @@ enum ksm_test_name {
CHECK_KSM_UNMERGE, CHECK_KSM_UNMERGE,
CHECK_KSM_ZERO_PAGE_MERGE, CHECK_KSM_ZERO_PAGE_MERGE,
CHECK_KSM_NUMA_MERGE, CHECK_KSM_NUMA_MERGE,
KSM_MERGE_TIME KSM_MERGE_TIME,
KSM_COW_TIME
}; };
static int ksm_write_sysfs(const char *file_path, unsigned long val) static int ksm_write_sysfs(const char *file_path, unsigned long val)
...@@ -98,7 +99,8 @@ static void print_help(void) ...@@ -98,7 +99,8 @@ static void print_help(void)
" -U (page unmerging)\n" " -U (page unmerging)\n"
" -P evaluate merging time and speed.\n" " -P evaluate merging time and speed.\n"
" For this test, the size of duplicated memory area (in MiB)\n" " For this test, the size of duplicated memory area (in MiB)\n"
" must be provided using -s option\n\n"); " must be provided using -s option\n"
" -C evaluate the time required to break COW of merged pages.\n\n");
printf(" -a: specify the access protections of pages.\n" printf(" -a: specify the access protections of pages.\n"
" <prot> must be of the form [rwx].\n" " <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) ...@@ -455,6 +457,77 @@ static int ksm_merge_time(int mapping, int prot, int timeout, size_t map_size)
return KSFT_FAIL; return KSFT_FAIL;
} }
static int ksm_cow_time(int mapping, int prot, int timeout, size_t page_size)
{
void *map_ptr;
struct timespec start_time, end_time;
unsigned long cow_time_ns;
/* page_count must be less than 2*page_size */
size_t page_count = 4000;
map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count);
if (!map_ptr)
return KSFT_FAIL;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
perror("clock_gettime");
return KSFT_FAIL;
}
for (size_t i = 0; i < page_count - 1; i = i + 2)
memset(map_ptr + page_size * i, '-', 1);
if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
perror("clock_gettime");
return KSFT_FAIL;
}
cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
(end_time.tv_nsec - start_time.tv_nsec);
printf("Total size: %lu MiB\n\n", (page_size * page_count) / MB);
printf("Not merged pages:\n");
printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
cow_time_ns % NSEC_PER_SEC);
printf("Average speed: %.3f MiB/s\n\n", ((page_size * (page_count / 2)) / MB) /
((double)cow_time_ns / NSEC_PER_SEC));
/* Create 2000 pairs of duplicate pages */
for (size_t i = 0; i < page_count - 1; i = i + 2) {
memset(map_ptr + page_size * i, '+', i / 2 + 1);
memset(map_ptr + page_size * (i + 1), '+', i / 2 + 1);
}
if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout))
goto err_out;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
perror("clock_gettime");
goto err_out;
}
for (size_t i = 0; i < page_count - 1; i = i + 2)
memset(map_ptr + page_size * i, '-', 1);
if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
perror("clock_gettime");
goto err_out;
}
cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
(end_time.tv_nsec - start_time.tv_nsec);
printf("Merged pages:\n");
printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
cow_time_ns % NSEC_PER_SEC);
printf("Average speed: %.3f MiB/s\n", ((page_size * (page_count / 2)) / MB) /
((double)cow_time_ns / NSEC_PER_SEC));
munmap(map_ptr, page_size * page_count);
return KSFT_PASS;
err_out:
printf("Not OK\n");
munmap(map_ptr, page_size * page_count);
return KSFT_FAIL;
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int ret, opt; int ret, opt;
...@@ -468,7 +541,7 @@ int main(int argc, char *argv[]) ...@@ -468,7 +541,7 @@ int main(int argc, char *argv[])
bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT; bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT;
long size_MB = 0; long size_MB = 0;
while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNP")) != -1) { while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNPC")) != -1) {
switch (opt) { switch (opt) {
case 'a': case 'a':
prot = str_to_prot(optarg); prot = str_to_prot(optarg);
...@@ -522,6 +595,9 @@ int main(int argc, char *argv[]) ...@@ -522,6 +595,9 @@ int main(int argc, char *argv[])
case 'P': case 'P':
test_name = KSM_MERGE_TIME; test_name = KSM_MERGE_TIME;
break; break;
case 'C':
test_name = KSM_COW_TIME;
break;
default: default:
return KSFT_FAIL; return KSFT_FAIL;
} }
...@@ -571,6 +647,10 @@ int main(int argc, char *argv[]) ...@@ -571,6 +647,10 @@ int main(int argc, char *argv[])
ret = ksm_merge_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec, ret = ksm_merge_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
size_MB); size_MB);
break; break;
case KSM_COW_TIME:
ret = ksm_cow_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
page_size);
break;
} }
if (ksm_restore(&ksm_sysfs_old)) { if (ksm_restore(&ksm_sysfs_old)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment