0001
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
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
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
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
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
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 }