Commit 9e7cb94c authored by Zhansaya Bagdauletkyzy's avatar Zhansaya Bagdauletkyzy Committed by Linus Torvalds

selftests: vm: add KSM merging time test

Patch series "add KSM performance tests", v3.

Extend KSM self tests with a performance benchmark.  These tests are not
part of regular regression testing, as they are mainly intended to be used
by developers making changes to the memory management subsystem.

This patch (of 2):

Add ksm_merge_time() function to determine speed and time needed for
merging.  The total spent time is shown in seconds while speed is in
MiB/s.  User must specify the size of duplicated memory area (in MiB)
before running the test.

The test is run as follows: ./ksm_tests -P -s 100
The output:
	Total size:    100 MiB
	Total time:    0.201106786 s
	Average speed:  497.248 MiB/s

Link: https://lkml.kernel.org/r/cover.1629386192.git.zhansayabagdaulet@gmail.com
Link: https://lkml.kernel.org/r/318b946ac80cc9205c89d0962048378f7ce0705b.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 584ff0df
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <numa.h> #include <numa.h>
#include "../kselftest.h" #include "../kselftest.h"
#include "../../../../include/vdso/time64.h"
#define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/" #define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/"
#define KSM_FP(s) (KSM_SYSFS_PATH s) #define KSM_FP(s) (KSM_SYSFS_PATH s)
...@@ -15,6 +16,7 @@ ...@@ -15,6 +16,7 @@
#define KSM_PROT_STR_DEFAULT "rw" #define KSM_PROT_STR_DEFAULT "rw"
#define KSM_USE_ZERO_PAGES_DEFAULT false #define KSM_USE_ZERO_PAGES_DEFAULT false
#define KSM_MERGE_ACROSS_NODES_DEFAULT true #define KSM_MERGE_ACROSS_NODES_DEFAULT true
#define MB (1ul << 20)
struct ksm_sysfs { struct ksm_sysfs {
unsigned long max_page_sharing; unsigned long max_page_sharing;
...@@ -30,7 +32,8 @@ enum ksm_test_name { ...@@ -30,7 +32,8 @@ enum ksm_test_name {
CHECK_KSM_MERGE, CHECK_KSM_MERGE,
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
}; };
static int ksm_write_sysfs(const char *file_path, unsigned long val) static int ksm_write_sysfs(const char *file_path, unsigned long val)
...@@ -86,13 +89,16 @@ static int str_to_prot(char *prot_str) ...@@ -86,13 +89,16 @@ static int str_to_prot(char *prot_str)
static void print_help(void) static void print_help(void)
{ {
printf("usage: ksm_tests [-h] <test type> [-a prot] [-p page_count] [-l timeout]\n" printf("usage: ksm_tests [-h] <test type> [-a prot] [-p page_count] [-l timeout]\n"
"[-z use_zero_pages] [-m merge_across_nodes]\n"); "[-z use_zero_pages] [-m merge_across_nodes] [-s size]\n");
printf("Supported <test type>:\n" printf("Supported <test type>:\n"
" -M (page merging)\n" " -M (page merging)\n"
" -Z (zero pages merging)\n" " -Z (zero pages merging)\n"
" -N (merging of pages in different NUMA nodes)\n" " -N (merging of pages in different NUMA nodes)\n"
" -U (page unmerging)\n\n"); " -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");
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"
...@@ -105,6 +111,7 @@ static void print_help(void) ...@@ -105,6 +111,7 @@ static void print_help(void)
" Default: %d\n", KSM_USE_ZERO_PAGES_DEFAULT); " Default: %d\n", KSM_USE_ZERO_PAGES_DEFAULT);
printf(" -m: change merge_across_nodes tunable\n" printf(" -m: change merge_across_nodes tunable\n"
" Default: %d\n", KSM_MERGE_ACROSS_NODES_DEFAULT); " Default: %d\n", KSM_MERGE_ACROSS_NODES_DEFAULT);
printf(" -s: the size of duplicated memory area (in MiB)\n");
exit(0); exit(0);
} }
...@@ -407,6 +414,47 @@ static int check_ksm_numa_merge(int mapping, int prot, int timeout, bool merge_a ...@@ -407,6 +414,47 @@ static int check_ksm_numa_merge(int mapping, int prot, int timeout, bool merge_a
return KSFT_FAIL; return KSFT_FAIL;
} }
static int ksm_merge_time(int mapping, int prot, int timeout, size_t map_size)
{
void *map_ptr;
struct timespec start_time, end_time;
unsigned long scan_time_ns;
map_size *= MB;
map_ptr = allocate_memory(NULL, prot, mapping, '*', map_size);
if (!map_ptr)
return KSFT_FAIL;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
perror("clock_gettime");
goto err_out;
}
if (ksm_merge_pages(map_ptr, map_size, start_time, timeout))
goto err_out;
if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
perror("clock_gettime");
goto err_out;
}
scan_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", map_size / MB);
printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
scan_time_ns % NSEC_PER_SEC);
printf("Average speed: %.3f MiB/s\n", (map_size / MB) /
((double)scan_time_ns / NSEC_PER_SEC));
munmap(map_ptr, map_size);
return KSFT_PASS;
err_out:
printf("Not OK\n");
munmap(map_ptr, map_size);
return KSFT_FAIL;
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int ret, opt; int ret, opt;
...@@ -418,8 +466,9 @@ int main(int argc, char *argv[]) ...@@ -418,8 +466,9 @@ int main(int argc, char *argv[])
int test_name = CHECK_KSM_MERGE; int test_name = CHECK_KSM_MERGE;
bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT; bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT;
bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT; bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT;
long size_MB = 0;
while ((opt = getopt(argc, argv, "ha:p:l:z:m:MUZN")) != -1) { while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNP")) != -1) {
switch (opt) { switch (opt) {
case 'a': case 'a':
prot = str_to_prot(optarg); prot = str_to_prot(optarg);
...@@ -453,6 +502,12 @@ int main(int argc, char *argv[]) ...@@ -453,6 +502,12 @@ int main(int argc, char *argv[])
else else
merge_across_nodes = 1; merge_across_nodes = 1;
break; break;
case 's':
size_MB = atoi(optarg);
if (size_MB <= 0) {
printf("Size must be greater than 0\n");
return KSFT_FAIL;
}
case 'M': case 'M':
break; break;
case 'U': case 'U':
...@@ -464,6 +519,9 @@ int main(int argc, char *argv[]) ...@@ -464,6 +519,9 @@ int main(int argc, char *argv[])
case 'N': case 'N':
test_name = CHECK_KSM_NUMA_MERGE; test_name = CHECK_KSM_NUMA_MERGE;
break; break;
case 'P':
test_name = KSM_MERGE_TIME;
break;
default: default:
return KSFT_FAIL; return KSFT_FAIL;
} }
...@@ -505,6 +563,14 @@ int main(int argc, char *argv[]) ...@@ -505,6 +563,14 @@ int main(int argc, char *argv[])
ret = check_ksm_numa_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec, ret = check_ksm_numa_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
merge_across_nodes, page_size); merge_across_nodes, page_size);
break; break;
case KSM_MERGE_TIME:
if (size_MB == 0) {
printf("Option '-s' is required.\n");
return KSFT_FAIL;
}
ret = ksm_merge_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
size_MB);
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