Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <stdio.h>
0003 #include <string.h>
0004 #include <stdbool.h>
0005 #include <fcntl.h>
0006 #include <stdint.h>
0007 #include <malloc.h>
0008 #include <sys/mman.h>
0009 #include "../kselftest.h"
0010 #include "vm_util.h"
0011 
0012 #define PAGEMAP_FILE_PATH "/proc/self/pagemap"
0013 #define TEST_ITERATIONS 10000
0014 
0015 static void test_simple(int pagemap_fd, int pagesize)
0016 {
0017     int i;
0018     char *map;
0019 
0020     map = aligned_alloc(pagesize, pagesize);
0021     if (!map)
0022         ksft_exit_fail_msg("mmap failed\n");
0023 
0024     clear_softdirty();
0025 
0026     for (i = 0 ; i < TEST_ITERATIONS; i++) {
0027         if (pagemap_is_softdirty(pagemap_fd, map) == 1) {
0028             ksft_print_msg("dirty bit was 1, but should be 0 (i=%d)\n", i);
0029             break;
0030         }
0031 
0032         clear_softdirty();
0033         // Write something to the page to get the dirty bit enabled on the page
0034         map[0]++;
0035 
0036         if (pagemap_is_softdirty(pagemap_fd, map) == 0) {
0037             ksft_print_msg("dirty bit was 0, but should be 1 (i=%d)\n", i);
0038             break;
0039         }
0040 
0041         clear_softdirty();
0042     }
0043     free(map);
0044 
0045     ksft_test_result(i == TEST_ITERATIONS, "Test %s\n", __func__);
0046 }
0047 
0048 static void test_vma_reuse(int pagemap_fd, int pagesize)
0049 {
0050     char *map, *map2;
0051 
0052     map = mmap(NULL, pagesize, (PROT_READ | PROT_WRITE), (MAP_PRIVATE | MAP_ANON), -1, 0);
0053     if (map == MAP_FAILED)
0054         ksft_exit_fail_msg("mmap failed");
0055 
0056     // The kernel always marks new regions as soft dirty
0057     ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1,
0058              "Test %s dirty bit of allocated page\n", __func__);
0059 
0060     clear_softdirty();
0061     munmap(map, pagesize);
0062 
0063     map2 = mmap(NULL, pagesize, (PROT_READ | PROT_WRITE), (MAP_PRIVATE | MAP_ANON), -1, 0);
0064     if (map2 == MAP_FAILED)
0065         ksft_exit_fail_msg("mmap failed");
0066 
0067     // Dirty bit is set for new regions even if they are reused
0068     if (map == map2)
0069         ksft_test_result(pagemap_is_softdirty(pagemap_fd, map2) == 1,
0070                  "Test %s dirty bit of reused address page\n", __func__);
0071     else
0072         ksft_test_result_skip("Test %s dirty bit of reused address page\n", __func__);
0073 
0074     munmap(map2, pagesize);
0075 }
0076 
0077 static void test_hugepage(int pagemap_fd, int pagesize)
0078 {
0079     char *map;
0080     int i, ret;
0081     size_t hpage_len = read_pmd_pagesize();
0082 
0083     map = memalign(hpage_len, hpage_len);
0084     if (!map)
0085         ksft_exit_fail_msg("memalign failed\n");
0086 
0087     ret = madvise(map, hpage_len, MADV_HUGEPAGE);
0088     if (ret)
0089         ksft_exit_fail_msg("madvise failed %d\n", ret);
0090 
0091     for (i = 0; i < hpage_len; i++)
0092         map[i] = (char)i;
0093 
0094     if (check_huge(map)) {
0095         ksft_test_result_pass("Test %s huge page allocation\n", __func__);
0096 
0097         clear_softdirty();
0098         for (i = 0 ; i < TEST_ITERATIONS ; i++) {
0099             if (pagemap_is_softdirty(pagemap_fd, map) == 1) {
0100                 ksft_print_msg("dirty bit was 1, but should be 0 (i=%d)\n", i);
0101                 break;
0102             }
0103 
0104             clear_softdirty();
0105             // Write something to the page to get the dirty bit enabled on the page
0106             map[0]++;
0107 
0108             if (pagemap_is_softdirty(pagemap_fd, map) == 0) {
0109                 ksft_print_msg("dirty bit was 0, but should be 1 (i=%d)\n", i);
0110                 break;
0111             }
0112             clear_softdirty();
0113         }
0114 
0115         ksft_test_result(i == TEST_ITERATIONS, "Test %s huge page dirty bit\n", __func__);
0116     } else {
0117         // hugepage allocation failed. skip these tests
0118         ksft_test_result_skip("Test %s huge page allocation\n", __func__);
0119         ksft_test_result_skip("Test %s huge page dirty bit\n", __func__);
0120     }
0121     free(map);
0122 }
0123 
0124 static void test_mprotect(int pagemap_fd, int pagesize, bool anon)
0125 {
0126     const char *type[] = {"file", "anon"};
0127     const char *fname = "./soft-dirty-test-file";
0128     int test_fd;
0129     char *map;
0130 
0131     if (anon) {
0132         map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
0133                MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
0134         if (!map)
0135             ksft_exit_fail_msg("anon mmap failed\n");
0136     } else {
0137         test_fd = open(fname, O_RDWR | O_CREAT);
0138         if (test_fd < 0) {
0139             ksft_test_result_skip("Test %s open() file failed\n", __func__);
0140             return;
0141         }
0142         unlink(fname);
0143         ftruncate(test_fd, pagesize);
0144         map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
0145                MAP_SHARED, test_fd, 0);
0146         if (!map)
0147             ksft_exit_fail_msg("file mmap failed\n");
0148     }
0149 
0150     *map = 1;
0151     ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1,
0152              "Test %s-%s dirty bit of new written page\n",
0153              __func__, type[anon]);
0154     clear_softdirty();
0155     ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
0156              "Test %s-%s soft-dirty clear after clear_refs\n",
0157              __func__, type[anon]);
0158     mprotect(map, pagesize, PROT_READ);
0159     ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
0160              "Test %s-%s soft-dirty clear after marking RO\n",
0161              __func__, type[anon]);
0162     mprotect(map, pagesize, PROT_READ|PROT_WRITE);
0163     ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
0164              "Test %s-%s soft-dirty clear after marking RW\n",
0165              __func__, type[anon]);
0166     *map = 2;
0167     ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1,
0168              "Test %s-%s soft-dirty after rewritten\n",
0169              __func__, type[anon]);
0170 
0171     munmap(map, pagesize);
0172 
0173     if (!anon)
0174         close(test_fd);
0175 }
0176 
0177 static void test_mprotect_anon(int pagemap_fd, int pagesize)
0178 {
0179     test_mprotect(pagemap_fd, pagesize, true);
0180 }
0181 
0182 static void test_mprotect_file(int pagemap_fd, int pagesize)
0183 {
0184     test_mprotect(pagemap_fd, pagesize, false);
0185 }
0186 
0187 int main(int argc, char **argv)
0188 {
0189     int pagemap_fd;
0190     int pagesize;
0191 
0192     ksft_print_header();
0193     ksft_set_plan(15);
0194 
0195     pagemap_fd = open(PAGEMAP_FILE_PATH, O_RDONLY);
0196     if (pagemap_fd < 0)
0197         ksft_exit_fail_msg("Failed to open %s\n", PAGEMAP_FILE_PATH);
0198 
0199     pagesize = getpagesize();
0200 
0201     test_simple(pagemap_fd, pagesize);
0202     test_vma_reuse(pagemap_fd, pagesize);
0203     test_hugepage(pagemap_fd, pagesize);
0204     test_mprotect_anon(pagemap_fd, pagesize);
0205     test_mprotect_file(pagemap_fd, pagesize);
0206 
0207     close(pagemap_fd);
0208 
0209     return ksft_exit_pass();
0210 }