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