]> Git Repo - J-linux.git/blob - tools/testing/selftests/mm/mlock2-tests.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / tools / testing / selftests / mm / mlock2-tests.c
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 #include <sys/mman.h>
4 #include <stdint.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <sys/time.h>
8 #include <sys/resource.h>
9 #include <stdbool.h>
10 #include "../kselftest.h"
11 #include "mlock2.h"
12
13 struct vm_boundaries {
14         unsigned long start;
15         unsigned long end;
16 };
17
18 static int get_vm_area(unsigned long addr, struct vm_boundaries *area)
19 {
20         FILE *file;
21         int ret = 1;
22         char line[1024] = {0};
23         unsigned long start;
24         unsigned long end;
25
26         if (!area)
27                 return ret;
28
29         file = fopen("/proc/self/maps", "r");
30         if (!file) {
31                 perror("fopen");
32                 return ret;
33         }
34
35         memset(area, 0, sizeof(struct vm_boundaries));
36
37         while(fgets(line, 1024, file)) {
38                 if (sscanf(line, "%lx-%lx", &start, &end) != 2) {
39                         ksft_print_msg("cannot parse /proc/self/maps\n");
40                         goto out;
41                 }
42
43                 if (start <= addr && end > addr) {
44                         area->start = start;
45                         area->end = end;
46                         ret = 0;
47                         goto out;
48                 }
49         }
50 out:
51         fclose(file);
52         return ret;
53 }
54
55 #define VMFLAGS "VmFlags:"
56
57 static bool is_vmflag_set(unsigned long addr, const char *vmflag)
58 {
59         char *line = NULL;
60         char *flags;
61         size_t size = 0;
62         bool ret = false;
63         FILE *smaps;
64
65         smaps = seek_to_smaps_entry(addr);
66         if (!smaps) {
67                 ksft_print_msg("Unable to parse /proc/self/smaps\n");
68                 goto out;
69         }
70
71         while (getline(&line, &size, smaps) > 0) {
72                 if (!strstr(line, VMFLAGS)) {
73                         free(line);
74                         line = NULL;
75                         size = 0;
76                         continue;
77                 }
78
79                 flags = line + strlen(VMFLAGS);
80                 ret = (strstr(flags, vmflag) != NULL);
81                 goto out;
82         }
83
84 out:
85         free(line);
86         fclose(smaps);
87         return ret;
88 }
89
90 #define SIZE "Size:"
91 #define RSS  "Rss:"
92 #define LOCKED "lo"
93
94 static unsigned long get_value_for_name(unsigned long addr, const char *name)
95 {
96         char *line = NULL;
97         size_t size = 0;
98         char *value_ptr;
99         FILE *smaps = NULL;
100         unsigned long value = -1UL;
101
102         smaps = seek_to_smaps_entry(addr);
103         if (!smaps) {
104                 ksft_print_msg("Unable to parse /proc/self/smaps\n");
105                 goto out;
106         }
107
108         while (getline(&line, &size, smaps) > 0) {
109                 if (!strstr(line, name)) {
110                         free(line);
111                         line = NULL;
112                         size = 0;
113                         continue;
114                 }
115
116                 value_ptr = line + strlen(name);
117                 if (sscanf(value_ptr, "%lu kB", &value) < 1) {
118                         ksft_print_msg("Unable to parse smaps entry for Size\n");
119                         goto out;
120                 }
121                 break;
122         }
123
124 out:
125         if (smaps)
126                 fclose(smaps);
127         free(line);
128         return value;
129 }
130
131 static bool is_vma_lock_on_fault(unsigned long addr)
132 {
133         bool locked;
134         unsigned long vma_size, vma_rss;
135
136         locked = is_vmflag_set(addr, LOCKED);
137         if (!locked)
138                 return false;
139
140         vma_size = get_value_for_name(addr, SIZE);
141         vma_rss = get_value_for_name(addr, RSS);
142
143         /* only one page is faulted in */
144         return (vma_rss < vma_size);
145 }
146
147 #define PRESENT_BIT     0x8000000000000000ULL
148 #define PFN_MASK        0x007FFFFFFFFFFFFFULL
149 #define UNEVICTABLE_BIT (1UL << 18)
150
151 static int lock_check(unsigned long addr)
152 {
153         bool locked;
154         unsigned long vma_size, vma_rss;
155
156         locked = is_vmflag_set(addr, LOCKED);
157         if (!locked)
158                 return false;
159
160         vma_size = get_value_for_name(addr, SIZE);
161         vma_rss = get_value_for_name(addr, RSS);
162
163         return (vma_rss == vma_size);
164 }
165
166 static int unlock_lock_check(char *map)
167 {
168         if (is_vmflag_set((unsigned long)map, LOCKED)) {
169                 ksft_print_msg("VMA flag %s is present on page 1 after unlock\n", LOCKED);
170                 return 1;
171         }
172
173         return 0;
174 }
175
176 static void test_mlock_lock(void)
177 {
178         char *map;
179         unsigned long page_size = getpagesize();
180
181         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
182                    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
183         if (map == MAP_FAILED)
184                 ksft_exit_fail_msg("mmap error: %s", strerror(errno));
185
186         if (mlock2_(map, 2 * page_size, 0)) {
187                 munmap(map, 2 * page_size);
188                 ksft_exit_fail_msg("mlock2(0): %s\n", strerror(errno));
189         }
190
191         ksft_test_result(lock_check((unsigned long)map), "%s: Locked\n", __func__);
192
193         /* Now unlock and recheck attributes */
194         if (munlock(map, 2 * page_size)) {
195                 munmap(map, 2 * page_size);
196                 ksft_exit_fail_msg("munlock(): %s\n", strerror(errno));
197         }
198
199         ksft_test_result(!unlock_lock_check(map), "%s: Locked\n", __func__);
200         munmap(map, 2 * page_size);
201 }
202
203 static int onfault_check(char *map)
204 {
205         *map = 'a';
206         if (!is_vma_lock_on_fault((unsigned long)map)) {
207                 ksft_print_msg("VMA is not marked for lock on fault\n");
208                 return 1;
209         }
210
211         return 0;
212 }
213
214 static int unlock_onfault_check(char *map)
215 {
216         unsigned long page_size = getpagesize();
217
218         if (is_vma_lock_on_fault((unsigned long)map) ||
219             is_vma_lock_on_fault((unsigned long)map + page_size)) {
220                 ksft_print_msg("VMA is still lock on fault after unlock\n");
221                 return 1;
222         }
223
224         return 0;
225 }
226
227 static void test_mlock_onfault(void)
228 {
229         char *map;
230         unsigned long page_size = getpagesize();
231
232         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
233                    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
234         if (map == MAP_FAILED)
235                 ksft_exit_fail_msg("mmap error: %s", strerror(errno));
236
237         if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
238                 munmap(map, 2 * page_size);
239                 ksft_exit_fail_msg("mlock2(MLOCK_ONFAULT): %s\n", strerror(errno));
240         }
241
242         ksft_test_result(!onfault_check(map), "%s: VMA marked for lock on fault\n", __func__);
243
244         /* Now unlock and recheck attributes */
245         if (munlock(map, 2 * page_size)) {
246                 munmap(map, 2 * page_size);
247                 ksft_exit_fail_msg("munlock(): %s\n", strerror(errno));
248         }
249
250         ksft_test_result(!unlock_onfault_check(map), "VMA open lock after fault\n");
251         munmap(map, 2 * page_size);
252 }
253
254 static void test_lock_onfault_of_present(void)
255 {
256         char *map;
257         unsigned long page_size = getpagesize();
258
259         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
260                    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
261         if (map == MAP_FAILED)
262                 ksft_exit_fail_msg("mmap error: %s", strerror(errno));
263
264         *map = 'a';
265
266         if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
267                 munmap(map, 2 * page_size);
268                 ksft_test_result_fail("mlock2(MLOCK_ONFAULT) error: %s", strerror(errno));
269         }
270
271         ksft_test_result(is_vma_lock_on_fault((unsigned long)map) ||
272                          is_vma_lock_on_fault((unsigned long)map + page_size),
273                          "VMA with present pages is not marked lock on fault\n");
274         munmap(map, 2 * page_size);
275 }
276
277 static void test_munlockall0(void)
278 {
279         char *map;
280         unsigned long page_size = getpagesize();
281
282         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
283                    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
284         if (map == MAP_FAILED)
285                 ksft_exit_fail_msg("mmap error: %s\n", strerror(errno));
286
287         if (mlockall(MCL_CURRENT)) {
288                 munmap(map, 2 * page_size);
289                 ksft_exit_fail_msg("mlockall(MCL_CURRENT): %s\n", strerror(errno));
290         }
291
292         ksft_test_result(lock_check((unsigned long)map), "%s: Locked memory area\n", __func__);
293
294         if (munlockall()) {
295                 munmap(map, 2 * page_size);
296                 ksft_exit_fail_msg("munlockall(): %s\n", strerror(errno));
297         }
298
299         ksft_test_result(!unlock_lock_check(map), "%s: No locked memory\n", __func__);
300         munmap(map, 2 * page_size);
301 }
302
303 static void test_munlockall1(void)
304 {
305         char *map;
306         unsigned long page_size = getpagesize();
307
308         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
309                    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
310         if (map == MAP_FAILED)
311                 ksft_exit_fail_msg("mmap error: %s", strerror(errno));
312
313         if (mlockall(MCL_CURRENT | MCL_ONFAULT)) {
314                 munmap(map, 2 * page_size);
315                 ksft_exit_fail_msg("mlockall(MCL_CURRENT | MCL_ONFAULT): %s\n", strerror(errno));
316         }
317
318         ksft_test_result(!onfault_check(map), "%s: VMA marked for lock on fault\n", __func__);
319
320         if (munlockall()) {
321                 munmap(map, 2 * page_size);
322                 ksft_exit_fail_msg("munlockall(): %s\n", strerror(errno));
323         }
324
325         ksft_test_result(!unlock_onfault_check(map), "%s: Unlocked\n", __func__);
326
327         if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
328                 munmap(map, 2 * page_size);
329                 ksft_exit_fail_msg("mlockall(MCL_CURRENT | MCL_FUTURE): %s\n", strerror(errno));
330         }
331
332         ksft_test_result(lock_check((unsigned long)map), "%s: Locked\n", __func__);
333
334         if (munlockall()) {
335                 munmap(map, 2 * page_size);
336                 ksft_exit_fail_msg("munlockall() %s\n", strerror(errno));
337         }
338
339         ksft_test_result(!unlock_lock_check(map), "%s: No locked memory\n", __func__);
340         munmap(map, 2 * page_size);
341 }
342
343 static void test_vma_management(bool call_mlock)
344 {
345         void *map;
346         unsigned long page_size = getpagesize();
347         struct vm_boundaries page1;
348         struct vm_boundaries page2;
349         struct vm_boundaries page3;
350
351         map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
352                    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
353         if (map == MAP_FAILED)
354                 ksft_exit_fail_msg("mmap error: %s", strerror(errno));
355
356         if (call_mlock && mlock2_(map, 3 * page_size, MLOCK_ONFAULT)) {
357                 munmap(map, 3 * page_size);
358                 ksft_test_result_fail("mlock error: %s", strerror(errno));
359         }
360
361         if (get_vm_area((unsigned long)map, &page1) ||
362             get_vm_area((unsigned long)map + page_size, &page2) ||
363             get_vm_area((unsigned long)map + page_size * 2, &page3)) {
364                 munmap(map, 3 * page_size);
365                 ksft_test_result_fail("couldn't find mapping in /proc/self/maps");
366         }
367
368         /*
369          * Before we unlock a portion, we need to that all three pages are in
370          * the same VMA.  If they are not we abort this test (Note that this is
371          * not a failure)
372          */
373         if (page1.start != page2.start || page2.start != page3.start) {
374                 munmap(map, 3 * page_size);
375                 ksft_test_result_fail("VMAs are not merged to start, aborting test");
376         }
377
378         if (munlock(map + page_size, page_size)) {
379                 munmap(map, 3 * page_size);
380                 ksft_test_result_fail("munlock(): %s", strerror(errno));
381         }
382
383         if (get_vm_area((unsigned long)map, &page1) ||
384             get_vm_area((unsigned long)map + page_size, &page2) ||
385             get_vm_area((unsigned long)map + page_size * 2, &page3)) {
386                 munmap(map, 3 * page_size);
387                 ksft_test_result_fail("couldn't find mapping in /proc/self/maps");
388         }
389
390         /* All three VMAs should be different */
391         if (page1.start == page2.start || page2.start == page3.start) {
392                 munmap(map, 3 * page_size);
393                 ksft_test_result_fail("failed to split VMA for munlock");
394         }
395
396         /* Now unlock the first and third page and check the VMAs again */
397         if (munlock(map, page_size * 3)) {
398                 munmap(map, 3 * page_size);
399                 ksft_test_result_fail("munlock(): %s", strerror(errno));
400         }
401
402         if (get_vm_area((unsigned long)map, &page1) ||
403             get_vm_area((unsigned long)map + page_size, &page2) ||
404             get_vm_area((unsigned long)map + page_size * 2, &page3)) {
405                 munmap(map, 3 * page_size);
406                 ksft_test_result_fail("couldn't find mapping in /proc/self/maps");
407         }
408
409         /* Now all three VMAs should be the same */
410         if (page1.start != page2.start || page2.start != page3.start) {
411                 munmap(map, 3 * page_size);
412                 ksft_test_result_fail("failed to merge VMAs after munlock");
413         }
414
415         ksft_test_result_pass("%s call_mlock %d\n", __func__, call_mlock);
416         munmap(map, 3 * page_size);
417 }
418
419 static void test_mlockall(void)
420 {
421         if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE))
422                 ksft_exit_fail_msg("mlockall failed: %s\n", strerror(errno));
423
424         test_vma_management(false);
425         munlockall();
426 }
427
428 int main(int argc, char **argv)
429 {
430         int ret, size = 3 * getpagesize();
431         void *map;
432
433         ksft_print_header();
434
435         map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
436         if (map == MAP_FAILED)
437                 ksft_exit_fail_msg("mmap error: %s", strerror(errno));
438
439         ret = mlock2_(map, size, MLOCK_ONFAULT);
440         if (ret && errno == ENOSYS)
441                 ksft_finished();
442
443         munmap(map, size);
444
445         ksft_set_plan(13);
446
447         test_mlock_lock();
448         test_mlock_onfault();
449         test_munlockall0();
450         test_munlockall1();
451         test_lock_onfault_of_present();
452         test_vma_management(true);
453         test_mlockall();
454
455         ksft_finished();
456 }
This page took 0.05216 seconds and 4 git commands to generate.