Merge tag 'mtd/for-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
[linux-2.6-microblaze.git] / tools / testing / selftests / mincore / mincore_selftest.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * kselftest suite for mincore().
4  *
5  * Copyright (C) 2020 Collabora, Ltd.
6  */
7
8 #define _GNU_SOURCE
9
10 #include <stdio.h>
11 #include <errno.h>
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <sys/mman.h>
15 #include <string.h>
16 #include <fcntl.h>
17 #include <string.h>
18
19 #include "../kselftest.h"
20 #include "../kselftest_harness.h"
21
22 /* Default test file size: 4MB */
23 #define MB (1UL << 20)
24 #define FILE_SIZE (4 * MB)
25
26
27 /*
28  * Tests the user interface. This test triggers most of the documented
29  * error conditions in mincore().
30  */
31 TEST(basic_interface)
32 {
33         int retval;
34         int page_size;
35         unsigned char vec[1];
36         char *addr;
37
38         page_size = sysconf(_SC_PAGESIZE);
39
40         /* Query a 0 byte sized range */
41         retval = mincore(0, 0, vec);
42         EXPECT_EQ(0, retval);
43
44         /* Addresses in the specified range are invalid or unmapped */
45         errno = 0;
46         retval = mincore(NULL, page_size, vec);
47         EXPECT_EQ(-1, retval);
48         EXPECT_EQ(ENOMEM, errno);
49
50         errno = 0;
51         addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
52                 MAP_SHARED | MAP_ANONYMOUS, -1, 0);
53         ASSERT_NE(MAP_FAILED, addr) {
54                 TH_LOG("mmap error: %s", strerror(errno));
55         }
56
57         /* <addr> argument is not page-aligned */
58         errno = 0;
59         retval = mincore(addr + 1, page_size, vec);
60         EXPECT_EQ(-1, retval);
61         EXPECT_EQ(EINVAL, errno);
62
63         /* <length> argument is too large */
64         errno = 0;
65         retval = mincore(addr, -1, vec);
66         EXPECT_EQ(-1, retval);
67         EXPECT_EQ(ENOMEM, errno);
68
69         /* <vec> argument points to an illegal address */
70         errno = 0;
71         retval = mincore(addr, page_size, NULL);
72         EXPECT_EQ(-1, retval);
73         EXPECT_EQ(EFAULT, errno);
74         munmap(addr, page_size);
75 }
76
77
78 /*
79  * Test mincore() behavior on a private anonymous page mapping.
80  * Check that the page is not loaded into memory right after the mapping
81  * but after accessing it (on-demand allocation).
82  * Then free the page and check that it's not memory-resident.
83  */
84 TEST(check_anonymous_locked_pages)
85 {
86         unsigned char vec[1];
87         char *addr;
88         int retval;
89         int page_size;
90
91         page_size = sysconf(_SC_PAGESIZE);
92
93         /* Map one page and check it's not memory-resident */
94         errno = 0;
95         addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
96                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
97         ASSERT_NE(MAP_FAILED, addr) {
98                 TH_LOG("mmap error: %s", strerror(errno));
99         }
100         retval = mincore(addr, page_size, vec);
101         ASSERT_EQ(0, retval);
102         ASSERT_EQ(0, vec[0]) {
103                 TH_LOG("Page found in memory before use");
104         }
105
106         /* Touch the page and check again. It should now be in memory */
107         addr[0] = 1;
108         mlock(addr, page_size);
109         retval = mincore(addr, page_size, vec);
110         ASSERT_EQ(0, retval);
111         ASSERT_EQ(1, vec[0]) {
112                 TH_LOG("Page not found in memory after use");
113         }
114
115         /*
116          * It shouldn't be memory-resident after unlocking it and
117          * marking it as unneeded.
118          */
119         munlock(addr, page_size);
120         madvise(addr, page_size, MADV_DONTNEED);
121         retval = mincore(addr, page_size, vec);
122         ASSERT_EQ(0, retval);
123         ASSERT_EQ(0, vec[0]) {
124                 TH_LOG("Page in memory after being zapped");
125         }
126         munmap(addr, page_size);
127 }
128
129
130 /*
131  * Check mincore() behavior on huge pages.
132  * This test will be skipped if the mapping fails (ie. if there are no
133  * huge pages available).
134  *
135  * Make sure the system has at least one free huge page, check
136  * "HugePages_Free" in /proc/meminfo.
137  * Increment /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages if
138  * needed.
139  */
140 TEST(check_huge_pages)
141 {
142         unsigned char vec[1];
143         char *addr;
144         int retval;
145         int page_size;
146
147         page_size = sysconf(_SC_PAGESIZE);
148
149         errno = 0;
150         addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
151                 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
152                 -1, 0);
153         if (addr == MAP_FAILED) {
154                 if (errno == ENOMEM)
155                         SKIP(return, "No huge pages available.");
156                 else
157                         TH_LOG("mmap error: %s", strerror(errno));
158         }
159         retval = mincore(addr, page_size, vec);
160         ASSERT_EQ(0, retval);
161         ASSERT_EQ(0, vec[0]) {
162                 TH_LOG("Page found in memory before use");
163         }
164
165         addr[0] = 1;
166         mlock(addr, page_size);
167         retval = mincore(addr, page_size, vec);
168         ASSERT_EQ(0, retval);
169         ASSERT_EQ(1, vec[0]) {
170                 TH_LOG("Page not found in memory after use");
171         }
172
173         munlock(addr, page_size);
174         munmap(addr, page_size);
175 }
176
177
178 /*
179  * Test mincore() behavior on a file-backed page.
180  * No pages should be loaded into memory right after the mapping. Then,
181  * accessing any address in the mapping range should load the page
182  * containing the address and a number of subsequent pages (readahead).
183  *
184  * The actual readahead settings depend on the test environment, so we
185  * can't make a lot of assumptions about that. This test covers the most
186  * general cases.
187  */
188 TEST(check_file_mmap)
189 {
190         unsigned char *vec;
191         int vec_size;
192         char *addr;
193         int retval;
194         int page_size;
195         int fd;
196         int i;
197         int ra_pages = 0;
198
199         page_size = sysconf(_SC_PAGESIZE);
200         vec_size = FILE_SIZE / page_size;
201         if (FILE_SIZE % page_size)
202                 vec_size++;
203
204         vec = calloc(vec_size, sizeof(unsigned char));
205         ASSERT_NE(NULL, vec) {
206                 TH_LOG("Can't allocate array");
207         }
208
209         errno = 0;
210         fd = open(".", O_TMPFILE | O_RDWR, 0600);
211         ASSERT_NE(-1, fd) {
212                 TH_LOG("Can't create temporary file: %s",
213                         strerror(errno));
214         }
215         errno = 0;
216         retval = fallocate(fd, 0, 0, FILE_SIZE);
217         ASSERT_EQ(0, retval) {
218                 TH_LOG("Error allocating space for the temporary file: %s",
219                         strerror(errno));
220         }
221
222         /*
223          * Map the whole file, the pages shouldn't be fetched yet.
224          */
225         errno = 0;
226         addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE,
227                         MAP_SHARED, fd, 0);
228         ASSERT_NE(MAP_FAILED, addr) {
229                 TH_LOG("mmap error: %s", strerror(errno));
230         }
231         retval = mincore(addr, FILE_SIZE, vec);
232         ASSERT_EQ(0, retval);
233         for (i = 0; i < vec_size; i++) {
234                 ASSERT_EQ(0, vec[i]) {
235                         TH_LOG("Unexpected page in memory");
236                 }
237         }
238
239         /*
240          * Touch a page in the middle of the mapping. We expect the next
241          * few pages (the readahead window) to be populated too.
242          */
243         addr[FILE_SIZE / 2] = 1;
244         retval = mincore(addr, FILE_SIZE, vec);
245         ASSERT_EQ(0, retval);
246         ASSERT_EQ(1, vec[FILE_SIZE / 2 / page_size]) {
247                 TH_LOG("Page not found in memory after use");
248         }
249
250         i = FILE_SIZE / 2 / page_size + 1;
251         while (i < vec_size && vec[i]) {
252                 ra_pages++;
253                 i++;
254         }
255         EXPECT_GT(ra_pages, 0) {
256                 TH_LOG("No read-ahead pages found in memory");
257         }
258
259         EXPECT_LT(i, vec_size) {
260                 TH_LOG("Read-ahead pages reached the end of the file");
261         }
262         /*
263          * End of the readahead window. The rest of the pages shouldn't
264          * be in memory.
265          */
266         if (i < vec_size) {
267                 while (i < vec_size && !vec[i])
268                         i++;
269                 EXPECT_EQ(vec_size, i) {
270                         TH_LOG("Unexpected page in memory beyond readahead window");
271                 }
272         }
273
274         munmap(addr, FILE_SIZE);
275         close(fd);
276         free(vec);
277 }
278
279
280 /*
281  * Test mincore() behavior on a page backed by a tmpfs file.  This test
282  * performs the same steps as the previous one. However, we don't expect
283  * any readahead in this case.
284  */
285 TEST(check_tmpfs_mmap)
286 {
287         unsigned char *vec;
288         int vec_size;
289         char *addr;
290         int retval;
291         int page_size;
292         int fd;
293         int i;
294         int ra_pages = 0;
295
296         page_size = sysconf(_SC_PAGESIZE);
297         vec_size = FILE_SIZE / page_size;
298         if (FILE_SIZE % page_size)
299                 vec_size++;
300
301         vec = calloc(vec_size, sizeof(unsigned char));
302         ASSERT_NE(NULL, vec) {
303                 TH_LOG("Can't allocate array");
304         }
305
306         errno = 0;
307         fd = open("/dev/shm", O_TMPFILE | O_RDWR, 0600);
308         ASSERT_NE(-1, fd) {
309                 TH_LOG("Can't create temporary file: %s",
310                         strerror(errno));
311         }
312         errno = 0;
313         retval = fallocate(fd, 0, 0, FILE_SIZE);
314         ASSERT_EQ(0, retval) {
315                 TH_LOG("Error allocating space for the temporary file: %s",
316                         strerror(errno));
317         }
318
319         /*
320          * Map the whole file, the pages shouldn't be fetched yet.
321          */
322         errno = 0;
323         addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE,
324                         MAP_SHARED, fd, 0);
325         ASSERT_NE(MAP_FAILED, addr) {
326                 TH_LOG("mmap error: %s", strerror(errno));
327         }
328         retval = mincore(addr, FILE_SIZE, vec);
329         ASSERT_EQ(0, retval);
330         for (i = 0; i < vec_size; i++) {
331                 ASSERT_EQ(0, vec[i]) {
332                         TH_LOG("Unexpected page in memory");
333                 }
334         }
335
336         /*
337          * Touch a page in the middle of the mapping. We expect only
338          * that page to be fetched into memory.
339          */
340         addr[FILE_SIZE / 2] = 1;
341         retval = mincore(addr, FILE_SIZE, vec);
342         ASSERT_EQ(0, retval);
343         ASSERT_EQ(1, vec[FILE_SIZE / 2 / page_size]) {
344                 TH_LOG("Page not found in memory after use");
345         }
346
347         i = FILE_SIZE / 2 / page_size + 1;
348         while (i < vec_size && vec[i]) {
349                 ra_pages++;
350                 i++;
351         }
352         ASSERT_EQ(ra_pages, 0) {
353                 TH_LOG("Read-ahead pages found in memory");
354         }
355
356         munmap(addr, FILE_SIZE);
357         close(fd);
358         free(vec);
359 }
360
361 TEST_HARNESS_MAIN