powerpc/mm: Avoid calling arch_enter/leave_lazy_mmu() in set_ptes
[platform/kernel/linux-starfive.git] / tools / testing / selftests / mm / mremap_dontunmap.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 /*
4  * Tests for mremap w/ MREMAP_DONTUNMAP.
5  *
6  * Copyright 2020, Brian Geffon <bgeffon@google.com>
7  */
8 #define _GNU_SOURCE
9 #include <sys/mman.h>
10 #include <errno.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15
16 #include "../kselftest.h"
17
18 unsigned long page_size;
19 char *page_buffer;
20
21 static void dump_maps(void)
22 {
23         char cmd[32];
24
25         snprintf(cmd, sizeof(cmd), "cat /proc/%d/maps", getpid());
26         system(cmd);
27 }
28
29 #define BUG_ON(condition, description)                                        \
30         do {                                                                  \
31                 if (condition) {                                              \
32                         fprintf(stderr, "[FAIL]\t%s():%d\t%s:%s\n", __func__, \
33                                 __LINE__, (description), strerror(errno));    \
34                         dump_maps();                                      \
35                         exit(1);                                              \
36                 }                                                             \
37         } while (0)
38
39 // Try a simple operation for to "test" for kernel support this prevents
40 // reporting tests as failed when it's run on an older kernel.
41 static int kernel_support_for_mremap_dontunmap()
42 {
43         int ret = 0;
44         unsigned long num_pages = 1;
45         void *source_mapping = mmap(NULL, num_pages * page_size, PROT_NONE,
46                                     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
47         BUG_ON(source_mapping == MAP_FAILED, "mmap");
48
49         // This simple remap should only fail if MREMAP_DONTUNMAP isn't
50         // supported.
51         void *dest_mapping =
52             mremap(source_mapping, num_pages * page_size, num_pages * page_size,
53                    MREMAP_DONTUNMAP | MREMAP_MAYMOVE, 0);
54         if (dest_mapping == MAP_FAILED) {
55                 ret = errno;
56         } else {
57                 BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1,
58                        "unable to unmap destination mapping");
59         }
60
61         BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
62                "unable to unmap source mapping");
63         return ret;
64 }
65
66 // This helper will just validate that an entire mapping contains the expected
67 // byte.
68 static int check_region_contains_byte(void *addr, unsigned long size, char byte)
69 {
70         BUG_ON(size & (page_size - 1),
71                "check_region_contains_byte expects page multiples");
72         BUG_ON((unsigned long)addr & (page_size - 1),
73                "check_region_contains_byte expects page alignment");
74
75         memset(page_buffer, byte, page_size);
76
77         unsigned long num_pages = size / page_size;
78         unsigned long i;
79
80         // Compare each page checking that it contains our expected byte.
81         for (i = 0; i < num_pages; ++i) {
82                 int ret =
83                     memcmp(addr + (i * page_size), page_buffer, page_size);
84                 if (ret) {
85                         return ret;
86                 }
87         }
88
89         return 0;
90 }
91
92 // this test validates that MREMAP_DONTUNMAP moves the pagetables while leaving
93 // the source mapping mapped.
94 static void mremap_dontunmap_simple()
95 {
96         unsigned long num_pages = 5;
97
98         void *source_mapping =
99             mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
100                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
101         BUG_ON(source_mapping == MAP_FAILED, "mmap");
102
103         memset(source_mapping, 'a', num_pages * page_size);
104
105         // Try to just move the whole mapping anywhere (not fixed).
106         void *dest_mapping =
107             mremap(source_mapping, num_pages * page_size, num_pages * page_size,
108                    MREMAP_DONTUNMAP | MREMAP_MAYMOVE, NULL);
109         BUG_ON(dest_mapping == MAP_FAILED, "mremap");
110
111         // Validate that the pages have been moved, we know they were moved if
112         // the dest_mapping contains a's.
113         BUG_ON(check_region_contains_byte
114                (dest_mapping, num_pages * page_size, 'a') != 0,
115                "pages did not migrate");
116         BUG_ON(check_region_contains_byte
117                (source_mapping, num_pages * page_size, 0) != 0,
118                "source should have no ptes");
119
120         BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1,
121                "unable to unmap destination mapping");
122         BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
123                "unable to unmap source mapping");
124 }
125
126 // This test validates that MREMAP_DONTUNMAP on a shared mapping works as expected.
127 static void mremap_dontunmap_simple_shmem()
128 {
129         unsigned long num_pages = 5;
130
131         int mem_fd = memfd_create("memfd", MFD_CLOEXEC);
132         BUG_ON(mem_fd < 0, "memfd_create");
133
134         BUG_ON(ftruncate(mem_fd, num_pages * page_size) < 0,
135                         "ftruncate");
136
137         void *source_mapping =
138             mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
139                  MAP_FILE | MAP_SHARED, mem_fd, 0);
140         BUG_ON(source_mapping == MAP_FAILED, "mmap");
141
142         BUG_ON(close(mem_fd) < 0, "close");
143
144         memset(source_mapping, 'a', num_pages * page_size);
145
146         // Try to just move the whole mapping anywhere (not fixed).
147         void *dest_mapping =
148             mremap(source_mapping, num_pages * page_size, num_pages * page_size,
149                    MREMAP_DONTUNMAP | MREMAP_MAYMOVE, NULL);
150         if (dest_mapping == MAP_FAILED && errno == EINVAL) {
151                 // Old kernel which doesn't support MREMAP_DONTUNMAP on shmem.
152                 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
153                         "unable to unmap source mapping");
154                 return;
155         }
156
157         BUG_ON(dest_mapping == MAP_FAILED, "mremap");
158
159         // Validate that the pages have been moved, we know they were moved if
160         // the dest_mapping contains a's.
161         BUG_ON(check_region_contains_byte
162                (dest_mapping, num_pages * page_size, 'a') != 0,
163                "pages did not migrate");
164
165         // Because the region is backed by shmem, we will actually see the same
166         // memory at the source location still.
167         BUG_ON(check_region_contains_byte
168                (source_mapping, num_pages * page_size, 'a') != 0,
169                "source should have no ptes");
170
171         BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1,
172                "unable to unmap destination mapping");
173         BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
174                "unable to unmap source mapping");
175 }
176
177 // This test validates MREMAP_DONTUNMAP will move page tables to a specific
178 // destination using MREMAP_FIXED, also while validating that the source
179 // remains intact.
180 static void mremap_dontunmap_simple_fixed()
181 {
182         unsigned long num_pages = 5;
183
184         // Since we want to guarantee that we can remap to a point, we will
185         // create a mapping up front.
186         void *dest_mapping =
187             mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
188                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
189         BUG_ON(dest_mapping == MAP_FAILED, "mmap");
190         memset(dest_mapping, 'X', num_pages * page_size);
191
192         void *source_mapping =
193             mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
194                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
195         BUG_ON(source_mapping == MAP_FAILED, "mmap");
196         memset(source_mapping, 'a', num_pages * page_size);
197
198         void *remapped_mapping =
199             mremap(source_mapping, num_pages * page_size, num_pages * page_size,
200                    MREMAP_FIXED | MREMAP_DONTUNMAP | MREMAP_MAYMOVE,
201                    dest_mapping);
202         BUG_ON(remapped_mapping == MAP_FAILED, "mremap");
203         BUG_ON(remapped_mapping != dest_mapping,
204                "mremap should have placed the remapped mapping at dest_mapping");
205
206         // The dest mapping will have been unmap by mremap so we expect the Xs
207         // to be gone and replaced with a's.
208         BUG_ON(check_region_contains_byte
209                (dest_mapping, num_pages * page_size, 'a') != 0,
210                "pages did not migrate");
211
212         // And the source mapping will have had its ptes dropped.
213         BUG_ON(check_region_contains_byte
214                (source_mapping, num_pages * page_size, 0) != 0,
215                "source should have no ptes");
216
217         BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1,
218                "unable to unmap destination mapping");
219         BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
220                "unable to unmap source mapping");
221 }
222
223 // This test validates that we can MREMAP_DONTUNMAP for a portion of an
224 // existing mapping.
225 static void mremap_dontunmap_partial_mapping()
226 {
227         /*
228          *  source mapping:
229          *  --------------
230          *  | aaaaaaaaaa |
231          *  --------------
232          *  to become:
233          *  --------------
234          *  | aaaaa00000 |
235          *  --------------
236          *  With the destination mapping containing 5 pages of As.
237          *  ---------
238          *  | aaaaa |
239          *  ---------
240          */
241         unsigned long num_pages = 10;
242         void *source_mapping =
243             mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
244                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
245         BUG_ON(source_mapping == MAP_FAILED, "mmap");
246         memset(source_mapping, 'a', num_pages * page_size);
247
248         // We will grab the last 5 pages of the source and move them.
249         void *dest_mapping =
250             mremap(source_mapping + (5 * page_size), 5 * page_size,
251                    5 * page_size,
252                    MREMAP_DONTUNMAP | MREMAP_MAYMOVE, NULL);
253         BUG_ON(dest_mapping == MAP_FAILED, "mremap");
254
255         // We expect the first 5 pages of the source to contain a's and the
256         // final 5 pages to contain zeros.
257         BUG_ON(check_region_contains_byte(source_mapping, 5 * page_size, 'a') !=
258                0, "first 5 pages of source should have original pages");
259         BUG_ON(check_region_contains_byte
260                (source_mapping + (5 * page_size), 5 * page_size, 0) != 0,
261                "final 5 pages of source should have no ptes");
262
263         // Finally we expect the destination to have 5 pages worth of a's.
264         BUG_ON(check_region_contains_byte(dest_mapping, 5 * page_size, 'a') !=
265                0, "dest mapping should contain ptes from the source");
266
267         BUG_ON(munmap(dest_mapping, 5 * page_size) == -1,
268                "unable to unmap destination mapping");
269         BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
270                "unable to unmap source mapping");
271 }
272
273 // This test validates that we can remap over only a portion of a mapping.
274 static void mremap_dontunmap_partial_mapping_overwrite(void)
275 {
276         /*
277          *  source mapping:
278          *  ---------
279          *  |aaaaa|
280          *  ---------
281          *  dest mapping initially:
282          *  -----------
283          *  |XXXXXXXXXX|
284          *  ------------
285          *  Source to become:
286          *  ---------
287          *  |00000|
288          *  ---------
289          *  With the destination mapping containing 5 pages of As.
290          *  ------------
291          *  |aaaaaXXXXX|
292          *  ------------
293          */
294         void *source_mapping =
295             mmap(NULL, 5 * page_size, PROT_READ | PROT_WRITE,
296                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
297         BUG_ON(source_mapping == MAP_FAILED, "mmap");
298         memset(source_mapping, 'a', 5 * page_size);
299
300         void *dest_mapping =
301             mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE,
302                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
303         BUG_ON(dest_mapping == MAP_FAILED, "mmap");
304         memset(dest_mapping, 'X', 10 * page_size);
305
306         // We will grab the last 5 pages of the source and move them.
307         void *remapped_mapping =
308             mremap(source_mapping, 5 * page_size,
309                    5 * page_size,
310                    MREMAP_DONTUNMAP | MREMAP_MAYMOVE | MREMAP_FIXED, dest_mapping);
311         BUG_ON(dest_mapping == MAP_FAILED, "mremap");
312         BUG_ON(dest_mapping != remapped_mapping, "expected to remap to dest_mapping");
313
314         BUG_ON(check_region_contains_byte(source_mapping, 5 * page_size, 0) !=
315                0, "first 5 pages of source should have no ptes");
316
317         // Finally we expect the destination to have 5 pages worth of a's.
318         BUG_ON(check_region_contains_byte(dest_mapping, 5 * page_size, 'a') != 0,
319                         "dest mapping should contain ptes from the source");
320
321         // Finally the last 5 pages shouldn't have been touched.
322         BUG_ON(check_region_contains_byte(dest_mapping + (5 * page_size),
323                                 5 * page_size, 'X') != 0,
324                         "dest mapping should have retained the last 5 pages");
325
326         BUG_ON(munmap(dest_mapping, 10 * page_size) == -1,
327                "unable to unmap destination mapping");
328         BUG_ON(munmap(source_mapping, 5 * page_size) == -1,
329                "unable to unmap source mapping");
330 }
331
332 int main(void)
333 {
334         page_size = sysconf(_SC_PAGE_SIZE);
335
336         // test for kernel support for MREMAP_DONTUNMAP skipping the test if
337         // not.
338         if (kernel_support_for_mremap_dontunmap() != 0) {
339                 printf("No kernel support for MREMAP_DONTUNMAP\n");
340                 return KSFT_SKIP;
341         }
342
343         // Keep a page sized buffer around for when we need it.
344         page_buffer =
345             mmap(NULL, page_size, PROT_READ | PROT_WRITE,
346                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
347         BUG_ON(page_buffer == MAP_FAILED, "unable to mmap a page.");
348
349         mremap_dontunmap_simple();
350         mremap_dontunmap_simple_shmem();
351         mremap_dontunmap_simple_fixed();
352         mremap_dontunmap_partial_mapping();
353         mremap_dontunmap_partial_mapping_overwrite();
354
355         BUG_ON(munmap(page_buffer, page_size) == -1,
356                "unable to unmap page buffer");
357
358         printf("OK\n");
359         return 0;
360 }