@@ -34,3 +34,4 @@ local_config.*
soft-dirty
split_huge_page_test
ksm_tests
+test-ksm-auto
@@ -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)
new file mode 100644
@@ -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;
+}