selftests: vm: add KSM merging time test
[linux-2.6-microblaze.git] / tools / testing / selftests / vm / ksm_tests.c
index cdeb4a0..432dfe6 100644 (file)
@@ -7,6 +7,7 @@
 #include <numa.h>
 
 #include "../kselftest.h"
+#include "../../../../include/vdso/time64.h"
 
 #define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/"
 #define KSM_FP(s) (KSM_SYSFS_PATH s)
@@ -15,6 +16,7 @@
 #define KSM_PROT_STR_DEFAULT "rw"
 #define KSM_USE_ZERO_PAGES_DEFAULT false
 #define KSM_MERGE_ACROSS_NODES_DEFAULT true
+#define MB (1ul << 20)
 
 struct ksm_sysfs {
        unsigned long max_page_sharing;
@@ -30,7 +32,8 @@ enum ksm_test_name {
        CHECK_KSM_MERGE,
        CHECK_KSM_UNMERGE,
        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)
@@ -86,13 +89,16 @@ static int str_to_prot(char *prot_str)
 static void print_help(void)
 {
        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"
               " -M (page merging)\n"
               " -Z (zero pages merging)\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"
               "     <prot> must be of the form [rwx].\n"
@@ -105,6 +111,7 @@ static void print_help(void)
               "     Default: %d\n", KSM_USE_ZERO_PAGES_DEFAULT);
        printf(" -m: change merge_across_nodes tunable\n"
               "     Default: %d\n", KSM_MERGE_ACROSS_NODES_DEFAULT);
+       printf(" -s: the size of duplicated memory area (in MiB)\n");
 
        exit(0);
 }
@@ -407,6 +414,47 @@ err_out:
        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 ret, opt;
@@ -418,8 +466,9 @@ int main(int argc, char *argv[])
        int test_name = CHECK_KSM_MERGE;
        bool use_zero_pages = KSM_USE_ZERO_PAGES_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) {
                case 'a':
                        prot = str_to_prot(optarg);
@@ -453,6 +502,12 @@ int main(int argc, char *argv[])
                        else
                                merge_across_nodes = 1;
                        break;
+               case 's':
+                       size_MB = atoi(optarg);
+                       if (size_MB <= 0) {
+                               printf("Size must be greater than 0\n");
+                               return KSFT_FAIL;
+                       }
                case 'M':
                        break;
                case 'U':
@@ -464,6 +519,9 @@ int main(int argc, char *argv[])
                case 'N':
                        test_name = CHECK_KSM_NUMA_MERGE;
                        break;
+               case 'P':
+                       test_name = KSM_MERGE_TIME;
+                       break;
                default:
                        return KSFT_FAIL;
                }
@@ -505,6 +563,14 @@ int main(int argc, char *argv[])
                ret = check_ksm_numa_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
                                           merge_across_nodes, page_size);
                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)) {