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

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: 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
Loading
Loading
Loading
Loading
+83 −3
Original line number Diff line number Diff line
@@ -33,7 +33,8 @@ enum ksm_test_name {
	CHECK_KSM_UNMERGE,
	CHECK_KSM_ZERO_PAGE_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)
@@ -98,7 +99,8 @@ static void print_help(void)
	       " -U (page unmerging)\n"
	       " -P evaluate merging time and speed.\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"
	       "     <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)
	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 ret, opt;
@@ -468,7 +541,7 @@ int main(int argc, char *argv[])
	bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT;
	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) {
		case 'a':
			prot = str_to_prot(optarg);
@@ -522,6 +595,9 @@ int main(int argc, char *argv[])
		case 'P':
			test_name = KSM_MERGE_TIME;
			break;
		case 'C':
			test_name = KSM_COW_TIME;
			break;
		default:
			return KSFT_FAIL;
		}
@@ -571,6 +647,10 @@ int main(int argc, char *argv[])
		ret = ksm_merge_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
				     size_MB);
		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)) {