From: xu xin <xu.xin16@xxxxxxxxxx> Add tests to verify the effectiveness and cpu consumption of auto mode of KSM. The tests requires no other MERGEABLE-madvised vm areas in system than the areas our testing process allocs. Signed-off-by: xu xin <xu.xin16@xxxxxxxxxx> Signed-off-by: CGEL <cgel.zte@xxxxxxxxx> --- tools/testing/selftests/vm/.gitignore | 1 + tools/testing/selftests/vm/Makefile | 1 + tools/testing/selftests/vm/test-ksm-auto.c | 273 +++++++++++++++++++++ 3 files changed, 275 insertions(+) create mode 100644 tools/testing/selftests/vm/test-ksm-auto.c diff --git a/tools/testing/selftests/vm/.gitignore b/tools/testing/selftests/vm/.gitignore index 31e5eea2a9b9..1cd8816c055d 100644 --- a/tools/testing/selftests/vm/.gitignore +++ b/tools/testing/selftests/vm/.gitignore @@ -34,3 +34,4 @@ local_config.* soft-dirty split_huge_page_test ksm_tests +test-ksm-auto diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile index d9fa6a9ea584..002d9482c37f 100644 --- a/tools/testing/selftests/vm/Makefile +++ b/tools/testing/selftests/vm/Makefile @@ -54,6 +54,7 @@ TEST_GEN_FILES += userfaultfd TEST_GEN_PROGS += soft-dirty TEST_GEN_PROGS += split_huge_page_test TEST_GEN_FILES += ksm_tests +TEST_GEN_FILES += test-ksm-auto ifeq ($(MACHINE),x86_64) CAN_BUILD_I386 := $(shell ./../x86/check_cc.sh "$(CC)" ../x86/trivial_32bit_program.c -m32) diff --git a/tools/testing/selftests/vm/test-ksm-auto.c b/tools/testing/selftests/vm/test-ksm-auto.c new file mode 100644 index 000000000000..0d71593e008e --- /dev/null +++ b/tools/testing/selftests/vm/test-ksm-auto.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ucontext.h> +#include <sys/time.h> +#include <sys/mman.h> + +#define KSM_CLEAR_MODE "2\n" +#define KSM_NORMAL_MODE "1\n" +#define KSM_AUTO_MODE "8\n" + +#define PAGESIZE (4*1024) +/* Don't change the value, it will afffect the result */ +#define TOTAL_MADVISE_SIZE (300*1024*1024) + +char *ksm_run_file = "/sys/kernel/mm/ksm/run"; +char *ksm_auto_threshold_file = "/sys/kernel/mm/ksm/auto_threshold"; +char *ksm_pages_volatile_file = "/sys/kernel/mm/ksm/pages_volatile"; +char *ksm_pages_sharing_file = "/sys/kernel/mm/ksm/pages_sharing"; + +#define SHAPE_FULL 1 +#define SHAPE_SPARSE 2 +/* They are related to the shape of memory */ +int final_pages[3] = {0, 76500, 42}; + +static char *mmap_and_madvise(long long size, int advise) +{ + char *ptr; + int err; + + err = 0; + + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (!ptr) + return NULL; + + err = madvise(ptr, size, advise); + if (err) { + perror("Madvise failed\n"); + free(ptr); + return NULL; + } + + return ptr; +} + +void make_samepage_ares(char *ptr, int size, int shape_type) +{ + int i, j; + char rnd_num; + + switch (shape_type) { + case SHAPE_FULL: + for (i = 0; i < (size / PAGESIZE); i++) + memset(ptr + (i * PAGESIZE), 0x1, PAGESIZE); + break; + case SHAPE_SPARSE: + /* Make pages different */ + j = 0; + for (i = 1; i < (size / PAGESIZE); i++) { + ptr[i * PAGESIZE + (j%PAGESIZE)] = j%127 + 1; + j++; + } + for (i = 0; i < (size / PAGESIZE); i += 1800) + memset(ptr + (i * PAGESIZE), -1, PAGESIZE); + } + + return; +} + +int read_file(char *file, char *buffer, int buf_len) +{ + FILE *fp; + size_t result; + long lSize; + + fp = fopen(file, "r"); + if (!fp) + return -1; + + fseek(fp, 0, SEEK_END); + lSize = ftell(fp); + rewind(fp); + + memset(buffer, 0, buf_len); + result = fread(buffer, 1, buf_len, fp); + if (result == 0) + return -1; + + fclose(fp); + + return 0; +} + +int write_file(char *file, const char *buffer, int len) +{ + FILE *fp; + size_t result; + + fp = fopen(file, "w+"); + if (!fp) + return -1; + + result = fwrite(buffer, len, 1, fp); + if (result == 0) + return -1; + + fclose(fp); + + return 0; +} + +static inline void get_orig_info(int *run, int *auto_threshold) +{ + char buffer[50]; + + /* Read the original state of ksm/run */ + if (read_file(ksm_run_file, buffer, sizeof(buffer))) { + printf("read file %s failed\n", ksm_run_file); + exit(1); + } + *run = atoi(buffer); + + if (read_file(ksm_auto_threshold_file, buffer, sizeof(buffer))) { + printf("read file: %s failed\n", ksm_auto_threshold_file); + exit(1); + } + *auto_threshold = atoi(buffer); +} + +static inline void restore_orig_state(int run, int auto_threshold) +{ + char buffer[50]; + + /* restore the original state */ + memset(buffer, 0, sizeof(buffer)); + snprintf(buffer, sizeof(buffer) - 1, "%d\n", run); + if (write_file(ksm_run_file, buffer, sizeof(buffer))) { + printf("write file %s failed\n", ksm_run_file); + exit(1); + } + + memset(buffer, 0, sizeof(buffer)); + snprintf(buffer, sizeof(buffer) - 1, "%d\n", auto_threshold); + if (write_file(ksm_auto_threshold_file, buffer, sizeof(buffer))) { + printf("write file %s failed\n", ksm_run_file); + exit(1); + } +} + +void set_ksmd_run_mode(char *mode) +{ + if (write_file(ksm_run_file, mode, 2)) { + printf("Failed: write 1 to %s\n", ksm_auto_threshold_file); + exit(1); + } +} + +static inline void wait_ksmpages_converged(int final_pages) +{ + int pages_sharing; + char buffer[50]; + + for (;;) { + if (read_file(ksm_pages_sharing_file, buffer, sizeof(buffer))) { + printf("read file %s failed\n", ksm_pages_sharing_file); + exit(1); + } + + pages_sharing = atoi(buffer); + if (pages_sharing >= final_pages) + break; + } +} + +void print_shape(int shape_type) +{ + switch (shape_type) { + case SHAPE_FULL: + printf("Now the shape of memory area is full-samepages:\n"); + printf("[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]\n\n"); + break; + case SHAPE_SPARSE: + printf("Now the shape of memory area is sparse-samepages:\n"); + printf("[xx] [xx] [xx] \n\n"); + break; + } +} + +void print_ksmd_cpu_comsuption(void) +{ + system("(ps x| grep \"ksmd\" | grep -v grep | awk \'{print $1}\' |" + " xargs -i cat /proc/{}/stat) | awk \'{print \"ksm current " + "cpu total slice: \" $14+$15+$16+$17}\'"); +} + +void test_ksmd_performance(char *madvise_area, int shape_type) +{ + struct timeval tv_start, tv_end; + + make_samepage_ares(madvise_area, TOTAL_MADVISE_SIZE, shape_type); + print_shape(shape_type); + + /********* Start to time ksmd's normal-run mode **********/ + printf("Start to test normal-run ksmd...\n"); + + print_ksmd_cpu_comsuption(); + + set_ksmd_run_mode(KSM_CLEAR_MODE); + set_ksmd_run_mode(KSM_NORMAL_MODE); + + gettimeofday(&tv_start, NULL); + + wait_ksmpages_converged(final_pages[shape_type]); + + gettimeofday(&tv_end, NULL); + printf("ksm normal-run's merging time: %lf seconds\n", + ((tv_end.tv_sec * 1000000 + tv_end.tv_usec) - + (tv_start.tv_sec * 1000000 + tv_start.tv_usec))/1000000.0); + + /******* Start to time ksmd's auto-run mode **********/ + print_ksmd_cpu_comsuption(); + + printf("Start to test auto-run ksmd...\n"); + set_ksmd_run_mode(KSM_CLEAR_MODE); + set_ksmd_run_mode(KSM_AUTO_MODE); + if (write_file(ksm_auto_threshold_file, "99\n", 2)) + printf("Failed: write 1 to %s\n", ksm_auto_threshold_file); + + gettimeofday(&tv_start, NULL); + + wait_ksmpages_converged(shape_type); + + gettimeofday(&tv_end, NULL); + printf("ksm auto-run's merging time: %lf seconds\n", + ((tv_end.tv_sec * 1000000 + tv_end.tv_usec) - + (tv_start.tv_sec * 1000000 + tv_start.tv_usec))/1000000.0); + + print_ksmd_cpu_comsuption(); +} + +int main(int argc, char **argv) +{ + char *madvise_area; + int orig_run, orig_auto_threshold; + + /* Get the original state of ksm */ + get_orig_info(&orig_run, &orig_auto_threshold); + printf("Now we mmap 300MB anouymous memory for testing.\n" + "There are two type of TEST which have different shape of\n" + "samepage areas.\n" + "Note: the test requires no other MERGEABLE-madvised vm areas\n" + "in system than the areas our testing process allocs.\n"); + madvise_area = mmap_and_madvise(TOTAL_MADVISE_SIZE, MADV_MERGEABLE); + if (!madvise_area) { + printf("madvise failed\n"); + exit(1); + } + + printf("\n****************** TEST 1 ******************\n"); + test_ksmd_performance(madvise_area, SHAPE_FULL); + printf("\n****************** TEST 2 ******************\n"); + test_ksmd_performance(madvise_area, SHAPE_SPARSE); + + /* Restore the original state */ + restore_orig_state(orig_run, orig_auto_threshold); + + return 0; +} -- 2.25.1