ACPI: APEI: Fix integer overflow in ghes_estatus_pool_init()
[sfrench/cifs-2.6.git] / tools / testing / selftests / vm / soft-dirty.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdbool.h>
5 #include <fcntl.h>
6 #include <stdint.h>
7 #include <malloc.h>
8 #include <sys/mman.h>
9 #include "../kselftest.h"
10 #include "vm_util.h"
11
12 #define PAGEMAP_FILE_PATH "/proc/self/pagemap"
13 #define TEST_ITERATIONS 10000
14
15 static void test_simple(int pagemap_fd, int pagesize)
16 {
17         int i;
18         char *map;
19
20         map = aligned_alloc(pagesize, pagesize);
21         if (!map)
22                 ksft_exit_fail_msg("mmap failed\n");
23
24         clear_softdirty();
25
26         for (i = 0 ; i < TEST_ITERATIONS; i++) {
27                 if (pagemap_is_softdirty(pagemap_fd, map) == 1) {
28                         ksft_print_msg("dirty bit was 1, but should be 0 (i=%d)\n", i);
29                         break;
30                 }
31
32                 clear_softdirty();
33                 // Write something to the page to get the dirty bit enabled on the page
34                 map[0]++;
35
36                 if (pagemap_is_softdirty(pagemap_fd, map) == 0) {
37                         ksft_print_msg("dirty bit was 0, but should be 1 (i=%d)\n", i);
38                         break;
39                 }
40
41                 clear_softdirty();
42         }
43         free(map);
44
45         ksft_test_result(i == TEST_ITERATIONS, "Test %s\n", __func__);
46 }
47
48 static void test_vma_reuse(int pagemap_fd, int pagesize)
49 {
50         char *map, *map2;
51
52         map = mmap(NULL, pagesize, (PROT_READ | PROT_WRITE), (MAP_PRIVATE | MAP_ANON), -1, 0);
53         if (map == MAP_FAILED)
54                 ksft_exit_fail_msg("mmap failed");
55
56         // The kernel always marks new regions as soft dirty
57         ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1,
58                          "Test %s dirty bit of allocated page\n", __func__);
59
60         clear_softdirty();
61         munmap(map, pagesize);
62
63         map2 = mmap(NULL, pagesize, (PROT_READ | PROT_WRITE), (MAP_PRIVATE | MAP_ANON), -1, 0);
64         if (map2 == MAP_FAILED)
65                 ksft_exit_fail_msg("mmap failed");
66
67         // Dirty bit is set for new regions even if they are reused
68         if (map == map2)
69                 ksft_test_result(pagemap_is_softdirty(pagemap_fd, map2) == 1,
70                                  "Test %s dirty bit of reused address page\n", __func__);
71         else
72                 ksft_test_result_skip("Test %s dirty bit of reused address page\n", __func__);
73
74         munmap(map2, pagesize);
75 }
76
77 static void test_hugepage(int pagemap_fd, int pagesize)
78 {
79         char *map;
80         int i, ret;
81         size_t hpage_len = read_pmd_pagesize();
82
83         map = memalign(hpage_len, hpage_len);
84         if (!map)
85                 ksft_exit_fail_msg("memalign failed\n");
86
87         ret = madvise(map, hpage_len, MADV_HUGEPAGE);
88         if (ret)
89                 ksft_exit_fail_msg("madvise failed %d\n", ret);
90
91         for (i = 0; i < hpage_len; i++)
92                 map[i] = (char)i;
93
94         if (check_huge(map)) {
95                 ksft_test_result_pass("Test %s huge page allocation\n", __func__);
96
97                 clear_softdirty();
98                 for (i = 0 ; i < TEST_ITERATIONS ; i++) {
99                         if (pagemap_is_softdirty(pagemap_fd, map) == 1) {
100                                 ksft_print_msg("dirty bit was 1, but should be 0 (i=%d)\n", i);
101                                 break;
102                         }
103
104                         clear_softdirty();
105                         // Write something to the page to get the dirty bit enabled on the page
106                         map[0]++;
107
108                         if (pagemap_is_softdirty(pagemap_fd, map) == 0) {
109                                 ksft_print_msg("dirty bit was 0, but should be 1 (i=%d)\n", i);
110                                 break;
111                         }
112                         clear_softdirty();
113                 }
114
115                 ksft_test_result(i == TEST_ITERATIONS, "Test %s huge page dirty bit\n", __func__);
116         } else {
117                 // hugepage allocation failed. skip these tests
118                 ksft_test_result_skip("Test %s huge page allocation\n", __func__);
119                 ksft_test_result_skip("Test %s huge page dirty bit\n", __func__);
120         }
121         free(map);
122 }
123
124 static void test_mprotect(int pagemap_fd, int pagesize, bool anon)
125 {
126         const char *type[] = {"file", "anon"};
127         const char *fname = "./soft-dirty-test-file";
128         int test_fd;
129         char *map;
130
131         if (anon) {
132                 map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
133                            MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
134                 if (!map)
135                         ksft_exit_fail_msg("anon mmap failed\n");
136         } else {
137                 test_fd = open(fname, O_RDWR | O_CREAT);
138                 if (test_fd < 0) {
139                         ksft_test_result_skip("Test %s open() file failed\n", __func__);
140                         return;
141                 }
142                 unlink(fname);
143                 ftruncate(test_fd, pagesize);
144                 map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
145                            MAP_SHARED, test_fd, 0);
146                 if (!map)
147                         ksft_exit_fail_msg("file mmap failed\n");
148         }
149
150         *map = 1;
151         ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1,
152                          "Test %s-%s dirty bit of new written page\n",
153                          __func__, type[anon]);
154         clear_softdirty();
155         ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
156                          "Test %s-%s soft-dirty clear after clear_refs\n",
157                          __func__, type[anon]);
158         mprotect(map, pagesize, PROT_READ);
159         ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
160                          "Test %s-%s soft-dirty clear after marking RO\n",
161                          __func__, type[anon]);
162         mprotect(map, pagesize, PROT_READ|PROT_WRITE);
163         ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
164                          "Test %s-%s soft-dirty clear after marking RW\n",
165                          __func__, type[anon]);
166         *map = 2;
167         ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1,
168                          "Test %s-%s soft-dirty after rewritten\n",
169                          __func__, type[anon]);
170
171         munmap(map, pagesize);
172
173         if (!anon)
174                 close(test_fd);
175 }
176
177 static void test_mprotect_anon(int pagemap_fd, int pagesize)
178 {
179         test_mprotect(pagemap_fd, pagesize, true);
180 }
181
182 static void test_mprotect_file(int pagemap_fd, int pagesize)
183 {
184         test_mprotect(pagemap_fd, pagesize, false);
185 }
186
187 int main(int argc, char **argv)
188 {
189         int pagemap_fd;
190         int pagesize;
191
192         ksft_print_header();
193         ksft_set_plan(15);
194
195         pagemap_fd = open(PAGEMAP_FILE_PATH, O_RDONLY);
196         if (pagemap_fd < 0)
197                 ksft_exit_fail_msg("Failed to open %s\n", PAGEMAP_FILE_PATH);
198
199         pagesize = getpagesize();
200
201         test_simple(pagemap_fd, pagesize);
202         test_vma_reuse(pagemap_fd, pagesize);
203         test_hugepage(pagemap_fd, pagesize);
204         test_mprotect_anon(pagemap_fd, pagesize);
205         test_mprotect_file(pagemap_fd, pagesize);
206
207         close(pagemap_fd);
208
209         return ksft_exit_pass();
210 }