]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
selftests/mm: put general ksm operation into vm_util
authorWei Yang <richard.weiyang@gmail.com>
Tue, 19 Aug 2025 08:00:46 +0000 (08:00 +0000)
committerAndrew Morton <akpm@linux-foundation.org>
Fri, 12 Sep 2025 00:25:12 +0000 (17:25 -0700)
Patch series "test that rmap behaves as expected", v4.

As David suggested, currently we don't have a high level test case to
verify the behavior of rmap. This patch set introduce the verification
on rmap by migration.

Patch 1 is a preparation to move ksm related operations into vm_util.
Patch 2 is the new test case for rmap.

Currently it covers following four scenarios:

  * anonymous page
  * shmem page
  * pagecache page
  * ksm page

This patch (of 2):

There are some general ksm operations could be used by other related
test cases. Put them into vm_util for common use.

This is a preparation patch for later use.

Link: https://lkml.kernel.org/r/20250819080047.10063-1-richard.weiyang@gmail.com
Link: https://lkml.kernel.org/r/20250819080047.10063-2-richard.weiyang@gmail.com
Signed-off-by: Wei Yang <richard.weiyang@gmail.com>
Suggested-by: David Hildenbrand <david@redhat.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Rik van Riel <riel@surriel.com>
Cc: Liam R. Howlett <Liam.Howlett@oracle.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Harry Yoo <harry.yoo@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
tools/testing/selftests/mm/ksm_functional_tests.c
tools/testing/selftests/mm/vm_util.c
tools/testing/selftests/mm/vm_util.h

index 534aa405cac704723f2f981625a40aac91d5d337..712f43c877369ebfae01b26ce7e90b29098d858e 100644 (file)
@@ -38,11 +38,6 @@ enum ksm_merge_mode {
 };
 
 static int mem_fd;
-static int ksm_fd;
-static int ksm_full_scans_fd;
-static int proc_self_ksm_stat_fd;
-static int proc_self_ksm_merging_pages_fd;
-static int ksm_use_zero_pages_fd;
 static int pagemap_fd;
 static size_t pagesize;
 
@@ -75,88 +70,6 @@ static bool range_maps_duplicates(char *addr, unsigned long size)
        return false;
 }
 
-static long get_my_ksm_zero_pages(void)
-{
-       char buf[200];
-       char *substr_ksm_zero;
-       size_t value_pos;
-       ssize_t read_size;
-       unsigned long my_ksm_zero_pages;
-
-       if (!proc_self_ksm_stat_fd)
-               return 0;
-
-       read_size = pread(proc_self_ksm_stat_fd, buf, sizeof(buf) - 1, 0);
-       if (read_size < 0)
-               return -errno;
-
-       buf[read_size] = 0;
-
-       substr_ksm_zero = strstr(buf, "ksm_zero_pages");
-       if (!substr_ksm_zero)
-               return 0;
-
-       value_pos = strcspn(substr_ksm_zero, "0123456789");
-       my_ksm_zero_pages = strtol(substr_ksm_zero + value_pos, NULL, 10);
-
-       return my_ksm_zero_pages;
-}
-
-static long get_my_merging_pages(void)
-{
-       char buf[10];
-       ssize_t ret;
-
-       if (proc_self_ksm_merging_pages_fd < 0)
-               return proc_self_ksm_merging_pages_fd;
-
-       ret = pread(proc_self_ksm_merging_pages_fd, buf, sizeof(buf) - 1, 0);
-       if (ret <= 0)
-               return -errno;
-       buf[ret] = 0;
-
-       return strtol(buf, NULL, 10);
-}
-
-static long ksm_get_full_scans(void)
-{
-       char buf[10];
-       ssize_t ret;
-
-       ret = pread(ksm_full_scans_fd, buf, sizeof(buf) - 1, 0);
-       if (ret <= 0)
-               return -errno;
-       buf[ret] = 0;
-
-       return strtol(buf, NULL, 10);
-}
-
-static int ksm_merge(void)
-{
-       long start_scans, end_scans;
-
-       /* Wait for two full scans such that any possible merging happened. */
-       start_scans = ksm_get_full_scans();
-       if (start_scans < 0)
-               return start_scans;
-       if (write(ksm_fd, "1", 1) != 1)
-               return -errno;
-       do {
-               end_scans = ksm_get_full_scans();
-               if (end_scans < 0)
-                       return end_scans;
-       } while (end_scans < start_scans + 2);
-
-       return 0;
-}
-
-static int ksm_unmerge(void)
-{
-       if (write(ksm_fd, "2", 1) != 1)
-               return -errno;
-       return 0;
-}
-
 static char *__mmap_and_merge_range(char val, unsigned long size, int prot,
                                  enum ksm_merge_mode mode)
 {
@@ -165,12 +78,12 @@ static char *__mmap_and_merge_range(char val, unsigned long size, int prot,
        int ret;
 
        /* Stabilize accounting by disabling KSM completely. */
-       if (ksm_unmerge()) {
+       if (ksm_stop() < 0) {
                ksft_print_msg("Disabling (unmerging) KSM failed\n");
                return err_map;
        }
 
-       if (get_my_merging_pages() > 0) {
+       if (ksm_get_self_merging_pages() > 0) {
                ksft_print_msg("Still pages merged\n");
                return err_map;
        }
@@ -220,7 +133,7 @@ static char *__mmap_and_merge_range(char val, unsigned long size, int prot,
        }
 
        /* Run KSM to trigger merging and wait. */
-       if (ksm_merge()) {
+       if (ksm_start() < 0) {
                ksft_print_msg("Running KSM failed\n");
                goto unmap;
        }
@@ -229,7 +142,7 @@ static char *__mmap_and_merge_range(char val, unsigned long size, int prot,
         * Check if anything was merged at all. Ignore the zero page that is
         * accounted differently (depending on kernel support).
         */
-       if (val && !get_my_merging_pages()) {
+       if (val && !ksm_get_self_merging_pages()) {
                ksft_print_msg("No pages got merged\n");
                goto unmap;
        }
@@ -276,7 +189,7 @@ static void test_unmerge(void)
        ksft_test_result(!range_maps_duplicates(map, size),
                         "Pages were unmerged\n");
 unmap:
-       ksm_unmerge();
+       ksm_stop();
        munmap(map, size);
 }
 
@@ -289,15 +202,12 @@ static void test_unmerge_zero_pages(void)
 
        ksft_print_msg("[RUN] %s\n", __func__);
 
-       if (proc_self_ksm_stat_fd < 0) {
-               ksft_test_result_skip("open(\"/proc/self/ksm_stat\") failed\n");
-               return;
-       }
-       if (ksm_use_zero_pages_fd < 0) {
-               ksft_test_result_skip("open \"/sys/kernel/mm/ksm/use_zero_pages\" failed\n");
+       if (ksm_get_self_zero_pages() < 0) {
+               ksft_test_result_skip("accessing \"/proc/self/ksm_stat\" failed\n");
                return;
        }
-       if (write(ksm_use_zero_pages_fd, "1", 1) != 1) {
+
+       if (ksm_use_zero_pages() < 0) {
                ksft_test_result_skip("write \"/sys/kernel/mm/ksm/use_zero_pages\" failed\n");
                return;
        }
@@ -309,7 +219,7 @@ static void test_unmerge_zero_pages(void)
 
        /* Check if ksm_zero_pages is updated correctly after KSM merging */
        pages_expected = size / pagesize;
-       if (pages_expected != get_my_ksm_zero_pages()) {
+       if (pages_expected != ksm_get_self_zero_pages()) {
                ksft_test_result_fail("'ksm_zero_pages' updated after merging\n");
                goto unmap;
        }
@@ -322,7 +232,7 @@ static void test_unmerge_zero_pages(void)
 
        /* Check if ksm_zero_pages is updated correctly after unmerging */
        pages_expected /= 2;
-       if (pages_expected != get_my_ksm_zero_pages()) {
+       if (pages_expected != ksm_get_self_zero_pages()) {
                ksft_test_result_fail("'ksm_zero_pages' updated after unmerging\n");
                goto unmap;
        }
@@ -332,7 +242,7 @@ static void test_unmerge_zero_pages(void)
                *((unsigned int *)&map[offs]) = offs;
 
        /* Now we should have no zeropages remaining. */
-       if (get_my_ksm_zero_pages()) {
+       if (ksm_get_self_zero_pages()) {
                ksft_test_result_fail("'ksm_zero_pages' updated after write fault\n");
                goto unmap;
        }
@@ -341,7 +251,7 @@ static void test_unmerge_zero_pages(void)
        ksft_test_result(!range_maps_duplicates(map, size),
                        "KSM zero pages were unmerged\n");
 unmap:
-       ksm_unmerge();
+       ksm_stop();
        munmap(map, size);
 }
 
@@ -370,7 +280,7 @@ static void test_unmerge_discarded(void)
        ksft_test_result(!range_maps_duplicates(map, size),
                         "Pages were unmerged\n");
 unmap:
-       ksm_unmerge();
+       ksm_stop();
        munmap(map, size);
 }
 
@@ -457,7 +367,7 @@ static void test_unmerge_uffd_wp(void)
 close_uffd:
        close(uffd);
 unmap:
-       ksm_unmerge();
+       ksm_stop();
        munmap(map, size);
 }
 #endif
@@ -521,7 +431,7 @@ static int test_child_ksm(void)
        else if (map == MAP_MERGE_SKIP)
                return 3;
 
-       ksm_unmerge();
+       ksm_stop();
        munmap(map, size);
        return 0;
 }
@@ -654,7 +564,7 @@ static void test_prctl_unmerge(void)
        ksft_test_result(!range_maps_duplicates(map, size),
                         "Pages were unmerged\n");
 unmap:
-       ksm_unmerge();
+       ksm_stop();
        munmap(map, size);
 }
 
@@ -688,7 +598,7 @@ static void test_prot_none(void)
        ksft_test_result(!range_maps_duplicates(map, size),
                         "Pages were unmerged\n");
 unmap:
-       ksm_unmerge();
+       ksm_stop();
        munmap(map, size);
 }
 
@@ -697,19 +607,15 @@ static void init_global_file_handles(void)
        mem_fd = open("/proc/self/mem", O_RDWR);
        if (mem_fd < 0)
                ksft_exit_fail_msg("opening /proc/self/mem failed\n");
-       ksm_fd = open("/sys/kernel/mm/ksm/run", O_RDWR);
-       if (ksm_fd < 0)
-               ksft_exit_skip("open(\"/sys/kernel/mm/ksm/run\") failed\n");
-       ksm_full_scans_fd = open("/sys/kernel/mm/ksm/full_scans", O_RDONLY);
-       if (ksm_full_scans_fd < 0)
-               ksft_exit_skip("open(\"/sys/kernel/mm/ksm/full_scans\") failed\n");
+       if (ksm_stop() < 0)
+               ksft_exit_skip("accessing \"/sys/kernel/mm/ksm/run\") failed\n");
+       if (ksm_get_full_scans() < 0)
+               ksft_exit_skip("accessing \"/sys/kernel/mm/ksm/full_scans\") failed\n");
        pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
        if (pagemap_fd < 0)
                ksft_exit_skip("open(\"/proc/self/pagemap\") failed\n");
-       proc_self_ksm_stat_fd = open("/proc/self/ksm_stat", O_RDONLY);
-       proc_self_ksm_merging_pages_fd = open("/proc/self/ksm_merging_pages",
-                                               O_RDONLY);
-       ksm_use_zero_pages_fd = open("/sys/kernel/mm/ksm/use_zero_pages", O_RDWR);
+       if (ksm_get_self_merging_pages() < 0)
+               ksft_exit_skip("accessing \"/proc/self/ksm_merging_pages\") failed\n");
 }
 
 int main(int argc, char **argv)
index 741fc129313dcc52521056b6ec3b15a647b5b669..56e9bd541edd556f6b4a64e48b0c7dbfe684f211 100644 (file)
@@ -578,3 +578,126 @@ bool detect_huge_zeropage(void)
        close(fd);
        return enabled;
 }
+
+long ksm_get_self_zero_pages(void)
+{
+       int proc_self_ksm_stat_fd;
+       char buf[200];
+       char *substr_ksm_zero;
+       size_t value_pos;
+       ssize_t read_size;
+
+       proc_self_ksm_stat_fd = open("/proc/self/ksm_stat", O_RDONLY);
+       if (proc_self_ksm_stat_fd < 0)
+               return -errno;
+
+       read_size = pread(proc_self_ksm_stat_fd, buf, sizeof(buf) - 1, 0);
+       close(proc_self_ksm_stat_fd);
+       if (read_size < 0)
+               return -errno;
+
+       buf[read_size] = 0;
+
+       substr_ksm_zero = strstr(buf, "ksm_zero_pages");
+       if (!substr_ksm_zero)
+               return 0;
+
+       value_pos = strcspn(substr_ksm_zero, "0123456789");
+       return strtol(substr_ksm_zero + value_pos, NULL, 10);
+}
+
+long ksm_get_self_merging_pages(void)
+{
+       int proc_self_ksm_merging_pages_fd;
+       char buf[10];
+       ssize_t ret;
+
+       proc_self_ksm_merging_pages_fd = open("/proc/self/ksm_merging_pages",
+                                               O_RDONLY);
+       if (proc_self_ksm_merging_pages_fd < 0)
+               return -errno;
+
+       ret = pread(proc_self_ksm_merging_pages_fd, buf, sizeof(buf) - 1, 0);
+       close(proc_self_ksm_merging_pages_fd);
+       if (ret <= 0)
+               return -errno;
+       buf[ret] = 0;
+
+       return strtol(buf, NULL, 10);
+}
+
+long ksm_get_full_scans(void)
+{
+       int ksm_full_scans_fd;
+       char buf[10];
+       ssize_t ret;
+
+       ksm_full_scans_fd = open("/sys/kernel/mm/ksm/full_scans", O_RDONLY);
+       if (ksm_full_scans_fd < 0)
+               return -errno;
+
+       ret = pread(ksm_full_scans_fd, buf, sizeof(buf) - 1, 0);
+       close(ksm_full_scans_fd);
+       if (ret <= 0)
+               return -errno;
+       buf[ret] = 0;
+
+       return strtol(buf, NULL, 10);
+}
+
+int ksm_use_zero_pages(void)
+{
+       int ksm_use_zero_pages_fd;
+       ssize_t ret;
+
+       ksm_use_zero_pages_fd = open("/sys/kernel/mm/ksm/use_zero_pages", O_RDWR);
+       if (ksm_use_zero_pages_fd < 0)
+               return -errno;
+
+       ret = write(ksm_use_zero_pages_fd, "1", 1);
+       close(ksm_use_zero_pages_fd);
+       return ret == 1 ? 0 : -errno;
+}
+
+int ksm_start(void)
+{
+       int ksm_fd;
+       ssize_t ret;
+       long start_scans, end_scans;
+
+       ksm_fd = open("/sys/kernel/mm/ksm/run", O_RDWR);
+       if (ksm_fd < 0)
+               return -errno;
+
+       /* Wait for two full scans such that any possible merging happened. */
+       start_scans = ksm_get_full_scans();
+       if (start_scans < 0) {
+               close(ksm_fd);
+               return start_scans;
+       }
+       ret = write(ksm_fd, "1", 1);
+       close(ksm_fd);
+       if (ret != 1)
+               return -errno;
+       do {
+               end_scans = ksm_get_full_scans();
+               if (end_scans < 0)
+                       return end_scans;
+       } while (end_scans < start_scans + 2);
+
+       return 0;
+}
+
+int ksm_stop(void)
+{
+       int ksm_fd;
+       ssize_t ret;
+
+       ksm_fd = open("/sys/kernel/mm/ksm/run", O_RDWR);
+       if (ksm_fd < 0)
+               return -errno;
+
+       ret = write(ksm_fd, "2", 1);
+       close(ksm_fd);
+       return ret == 1 ? 0 : -errno;
+}
index ab8722f482aedb11156a69e16b66611474079809..07c4acfd84b62d571bbef23e5666d17225c3dccc 100644 (file)
@@ -139,6 +139,13 @@ static inline int sz2ord(size_t size, size_t pagesize)
 void *sys_mremap(void *old_address, unsigned long old_size,
                 unsigned long new_size, int flags, void *new_address);
 
+long ksm_get_self_zero_pages(void);
+long ksm_get_self_merging_pages(void);
+long ksm_get_full_scans(void);
+int ksm_use_zero_pages(void);
+int ksm_start(void);
+int ksm_stop(void);
+
 /*
  * On ppc64 this will only work with radix 2M hugepage size
  */