7cb13cede483a84c607619d163dd6ebaebdc568f
[cascardo/linux.git] / tools / testing / selftests / vm / mlock2-tests.c
1 #define _GNU_SOURCE
2 #include <sys/mman.h>
3 #include <stdint.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include <string.h>
8 #include <sys/time.h>
9 #include <sys/resource.h>
10 #include <stdbool.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         char *end_addr;
24         char *stop;
25         unsigned long start;
26         unsigned long end;
27
28         if (!area)
29                 return ret;
30
31         file = fopen("/proc/self/maps", "r");
32         if (!file) {
33                 perror("fopen");
34                 return ret;
35         }
36
37         memset(area, 0, sizeof(struct vm_boundaries));
38
39         while(fgets(line, 1024, file)) {
40                 end_addr = strchr(line, '-');
41                 if (!end_addr) {
42                         printf("cannot parse /proc/self/maps\n");
43                         goto out;
44                 }
45                 *end_addr = '\0';
46                 end_addr++;
47                 stop = strchr(end_addr, ' ');
48                 if (!stop) {
49                         printf("cannot parse /proc/self/maps\n");
50                         goto out;
51                 }
52                 stop = '\0';
53
54                 sscanf(line, "%lx", &start);
55                 sscanf(end_addr, "%lx", &end);
56
57                 if (start <= addr && end > addr) {
58                         area->start = start;
59                         area->end = end;
60                         ret = 0;
61                         goto out;
62                 }
63         }
64 out:
65         fclose(file);
66         return ret;
67 }
68
69 static uint64_t get_pageflags(unsigned long addr)
70 {
71         FILE *file;
72         uint64_t pfn;
73         unsigned long offset;
74
75         file = fopen("/proc/self/pagemap", "r");
76         if (!file) {
77                 perror("fopen pagemap");
78                 _exit(1);
79         }
80
81         offset = addr / getpagesize() * sizeof(pfn);
82
83         if (fseek(file, offset, SEEK_SET)) {
84                 perror("fseek pagemap");
85                 _exit(1);
86         }
87
88         if (fread(&pfn, sizeof(pfn), 1, file) != 1) {
89                 perror("fread pagemap");
90                 _exit(1);
91         }
92
93         fclose(file);
94         return pfn;
95 }
96
97 static uint64_t get_kpageflags(unsigned long pfn)
98 {
99         uint64_t flags;
100         FILE *file;
101
102         file = fopen("/proc/kpageflags", "r");
103         if (!file) {
104                 perror("fopen kpageflags");
105                 _exit(1);
106         }
107
108         if (fseek(file, pfn * sizeof(flags), SEEK_SET)) {
109                 perror("fseek kpageflags");
110                 _exit(1);
111         }
112
113         if (fread(&flags, sizeof(flags), 1, file) != 1) {
114                 perror("fread kpageflags");
115                 _exit(1);
116         }
117
118         fclose(file);
119         return flags;
120 }
121
122 static FILE *seek_to_smaps_entry(unsigned long addr)
123 {
124         FILE *file;
125         char *line = NULL;
126         size_t size = 0;
127         unsigned long start, end;
128         char perms[5];
129         unsigned long offset;
130         char dev[32];
131         unsigned long inode;
132         char path[BUFSIZ];
133
134         file = fopen("/proc/self/smaps", "r");
135         if (!file) {
136                 perror("fopen smaps");
137                 _exit(1);
138         }
139
140         while (getline(&line, &size, file) > 0) {
141                 if (sscanf(line, "%lx-%lx %s %lx %s %lu %s\n",
142                            &start, &end, perms, &offset, dev, &inode, path) < 6)
143                         goto next;
144
145                 if (start <= addr && addr < end)
146                         goto out;
147
148 next:
149                 free(line);
150                 line = NULL;
151                 size = 0;
152         }
153
154         fclose(file);
155         file = NULL;
156
157 out:
158         free(line);
159         return file;
160 }
161
162 #define VMFLAGS "VmFlags:"
163
164 static bool is_vmflag_set(unsigned long addr, const char *vmflag)
165 {
166         char *line = NULL;
167         char *flags;
168         size_t size = 0;
169         bool ret = false;
170         FILE *smaps;
171
172         smaps = seek_to_smaps_entry(addr);
173         if (!smaps) {
174                 printf("Unable to parse /proc/self/smaps\n");
175                 goto out;
176         }
177
178         while (getline(&line, &size, smaps) > 0) {
179                 if (!strstr(line, VMFLAGS)) {
180                         free(line);
181                         line = NULL;
182                         size = 0;
183                         continue;
184                 }
185
186                 flags = line + strlen(VMFLAGS);
187                 ret = (strstr(flags, vmflag) != NULL);
188                 goto out;
189         }
190
191 out:
192         free(line);
193         fclose(smaps);
194         return ret;
195 }
196
197 #define SIZE "Size:"
198 #define RSS  "Rss:"
199 #define LOCKED "lo"
200
201 static bool is_vma_lock_on_fault(unsigned long addr)
202 {
203         bool ret = false;
204         bool locked;
205         FILE *smaps = NULL;
206         unsigned long vma_size, vma_rss;
207         char *line = NULL;
208         char *value;
209         size_t size = 0;
210
211         locked = is_vmflag_set(addr, LOCKED);
212         if (!locked)
213                 goto out;
214
215         smaps = seek_to_smaps_entry(addr);
216         if (!smaps) {
217                 printf("Unable to parse /proc/self/smaps\n");
218                 goto out;
219         }
220
221         while (getline(&line, &size, smaps) > 0) {
222                 if (!strstr(line, SIZE)) {
223                         free(line);
224                         line = NULL;
225                         size = 0;
226                         continue;
227                 }
228
229                 value = line + strlen(SIZE);
230                 if (sscanf(value, "%lu kB", &vma_size) < 1) {
231                         printf("Unable to parse smaps entry for Size\n");
232                         goto out;
233                 }
234                 break;
235         }
236
237         while (getline(&line, &size, smaps) > 0) {
238                 if (!strstr(line, RSS)) {
239                         free(line);
240                         line = NULL;
241                         size = 0;
242                         continue;
243                 }
244
245                 value = line + strlen(RSS);
246                 if (sscanf(value, "%lu kB", &vma_rss) < 1) {
247                         printf("Unable to parse smaps entry for Rss\n");
248                         goto out;
249                 }
250                 break;
251         }
252
253         ret = locked && (vma_rss < vma_size);
254 out:
255         free(line);
256         if (smaps)
257                 fclose(smaps);
258         return ret;
259 }
260
261 #define PRESENT_BIT     0x8000000000000000ULL
262 #define PFN_MASK        0x007FFFFFFFFFFFFFULL
263 #define UNEVICTABLE_BIT (1UL << 18)
264
265 static int lock_check(char *map)
266 {
267         unsigned long page_size = getpagesize();
268         uint64_t page1_flags, page2_flags;
269
270         page1_flags = get_pageflags((unsigned long)map);
271         page2_flags = get_pageflags((unsigned long)map + page_size);
272
273         /* Both pages should be present */
274         if (((page1_flags & PRESENT_BIT) == 0) ||
275             ((page2_flags & PRESENT_BIT) == 0)) {
276                 printf("Failed to make both pages present\n");
277                 return 1;
278         }
279
280         page1_flags = get_kpageflags(page1_flags & PFN_MASK);
281         page2_flags = get_kpageflags(page2_flags & PFN_MASK);
282
283         /* Both pages should be unevictable */
284         if (((page1_flags & UNEVICTABLE_BIT) == 0) ||
285             ((page2_flags & UNEVICTABLE_BIT) == 0)) {
286                 printf("Failed to make both pages unevictable\n");
287                 return 1;
288         }
289
290         if (!is_vmflag_set((unsigned long)map, LOCKED)) {
291                 printf("VMA flag %s is missing on page 1\n", LOCKED);
292                 return 1;
293         }
294
295         if (!is_vmflag_set((unsigned long)map + page_size, LOCKED)) {
296                 printf("VMA flag %s is missing on page 2\n", LOCKED);
297                 return 1;
298         }
299
300         return 0;
301 }
302
303 static int unlock_lock_check(char *map)
304 {
305         unsigned long page_size = getpagesize();
306         uint64_t page1_flags, page2_flags;
307
308         page1_flags = get_pageflags((unsigned long)map);
309         page2_flags = get_pageflags((unsigned long)map + page_size);
310         page1_flags = get_kpageflags(page1_flags & PFN_MASK);
311         page2_flags = get_kpageflags(page2_flags & PFN_MASK);
312
313         if ((page1_flags & UNEVICTABLE_BIT) || (page2_flags & UNEVICTABLE_BIT)) {
314                 printf("A page is still marked unevictable after unlock\n");
315                 return 1;
316         }
317
318         if (is_vmflag_set((unsigned long)map, LOCKED)) {
319                 printf("VMA flag %s is present on page 1 after unlock\n", LOCKED);
320                 return 1;
321         }
322
323         if (is_vmflag_set((unsigned long)map + page_size, LOCKED)) {
324                 printf("VMA flag %s is present on page 2 after unlock\n", LOCKED);
325                 return 1;
326         }
327
328         return 0;
329 }
330
331 static int test_mlock_lock()
332 {
333         char *map;
334         int ret = 1;
335         unsigned long page_size = getpagesize();
336
337         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
338                    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
339         if (map == MAP_FAILED) {
340                 perror("test_mlock_locked mmap");
341                 goto out;
342         }
343
344         if (mlock2_(map, 2 * page_size, 0)) {
345                 if (errno == ENOSYS) {
346                         printf("Cannot call new mlock family, skipping test\n");
347                         _exit(0);
348                 }
349                 perror("mlock2(0)");
350                 goto unmap;
351         }
352
353         if (lock_check(map))
354                 goto unmap;
355
356         /* Now unlock and recheck attributes */
357         if (munlock(map, 2 * page_size)) {
358                 perror("munlock()");
359                 goto unmap;
360         }
361
362         ret = unlock_lock_check(map);
363
364 unmap:
365         munmap(map, 2 * page_size);
366 out:
367         return ret;
368 }
369
370 static int onfault_check(char *map)
371 {
372         unsigned long page_size = getpagesize();
373         uint64_t page1_flags, page2_flags;
374
375         page1_flags = get_pageflags((unsigned long)map);
376         page2_flags = get_pageflags((unsigned long)map + page_size);
377
378         /* Neither page should be present */
379         if ((page1_flags & PRESENT_BIT) || (page2_flags & PRESENT_BIT)) {
380                 printf("Pages were made present by MLOCK_ONFAULT\n");
381                 return 1;
382         }
383
384         *map = 'a';
385         page1_flags = get_pageflags((unsigned long)map);
386         page2_flags = get_pageflags((unsigned long)map + page_size);
387
388         /* Only page 1 should be present */
389         if ((page1_flags & PRESENT_BIT) == 0) {
390                 printf("Page 1 is not present after fault\n");
391                 return 1;
392         } else if (page2_flags & PRESENT_BIT) {
393                 printf("Page 2 was made present\n");
394                 return 1;
395         }
396
397         page1_flags = get_kpageflags(page1_flags & PFN_MASK);
398
399         /* Page 1 should be unevictable */
400         if ((page1_flags & UNEVICTABLE_BIT) == 0) {
401                 printf("Failed to make faulted page unevictable\n");
402                 return 1;
403         }
404
405         if (!is_vma_lock_on_fault((unsigned long)map)) {
406                 printf("VMA is not marked for lock on fault\n");
407                 return 1;
408         }
409
410         if (!is_vma_lock_on_fault((unsigned long)map + page_size)) {
411                 printf("VMA is not marked for lock on fault\n");
412                 return 1;
413         }
414
415         return 0;
416 }
417
418 static int unlock_onfault_check(char *map)
419 {
420         unsigned long page_size = getpagesize();
421         uint64_t page1_flags;
422
423         page1_flags = get_pageflags((unsigned long)map);
424         page1_flags = get_kpageflags(page1_flags & PFN_MASK);
425
426         if (page1_flags & UNEVICTABLE_BIT) {
427                 printf("Page 1 is still marked unevictable after unlock\n");
428                 return 1;
429         }
430
431         if (is_vma_lock_on_fault((unsigned long)map) ||
432             is_vma_lock_on_fault((unsigned long)map + page_size)) {
433                 printf("VMA is still lock on fault after unlock\n");
434                 return 1;
435         }
436
437         return 0;
438 }
439
440 static int test_mlock_onfault()
441 {
442         char *map;
443         int ret = 1;
444         unsigned long page_size = getpagesize();
445
446         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
447                    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
448         if (map == MAP_FAILED) {
449                 perror("test_mlock_locked mmap");
450                 goto out;
451         }
452
453         if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
454                 if (errno == ENOSYS) {
455                         printf("Cannot call new mlock family, skipping test\n");
456                         _exit(0);
457                 }
458                 perror("mlock2(MLOCK_ONFAULT)");
459                 goto unmap;
460         }
461
462         if (onfault_check(map))
463                 goto unmap;
464
465         /* Now unlock and recheck attributes */
466         if (munlock(map, 2 * page_size)) {
467                 if (errno == ENOSYS) {
468                         printf("Cannot call new mlock family, skipping test\n");
469                         _exit(0);
470                 }
471                 perror("munlock()");
472                 goto unmap;
473         }
474
475         ret = unlock_onfault_check(map);
476 unmap:
477         munmap(map, 2 * page_size);
478 out:
479         return ret;
480 }
481
482 static int test_lock_onfault_of_present()
483 {
484         char *map;
485         int ret = 1;
486         unsigned long page_size = getpagesize();
487         uint64_t page1_flags, page2_flags;
488
489         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
490                    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
491         if (map == MAP_FAILED) {
492                 perror("test_mlock_locked mmap");
493                 goto out;
494         }
495
496         *map = 'a';
497
498         if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
499                 if (errno == ENOSYS) {
500                         printf("Cannot call new mlock family, skipping test\n");
501                         _exit(0);
502                 }
503                 perror("mlock2(MLOCK_ONFAULT)");
504                 goto unmap;
505         }
506
507         page1_flags = get_pageflags((unsigned long)map);
508         page2_flags = get_pageflags((unsigned long)map + page_size);
509         page1_flags = get_kpageflags(page1_flags & PFN_MASK);
510         page2_flags = get_kpageflags(page2_flags & PFN_MASK);
511
512         /* Page 1 should be unevictable */
513         if ((page1_flags & UNEVICTABLE_BIT) == 0) {
514                 printf("Failed to make present page unevictable\n");
515                 goto unmap;
516         }
517
518         if (!is_vma_lock_on_fault((unsigned long)map) ||
519             !is_vma_lock_on_fault((unsigned long)map + page_size)) {
520                 printf("VMA with present pages is not marked lock on fault\n");
521                 goto unmap;
522         }
523         ret = 0;
524 unmap:
525         munmap(map, 2 * page_size);
526 out:
527         return ret;
528 }
529
530 static int test_munlockall()
531 {
532         char *map;
533         int ret = 1;
534         unsigned long page_size = getpagesize();
535
536         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
537                    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
538
539         if (map == MAP_FAILED) {
540                 perror("test_munlockall mmap");
541                 goto out;
542         }
543
544         if (mlockall(MCL_CURRENT)) {
545                 perror("mlockall(MCL_CURRENT)");
546                 goto out;
547         }
548
549         if (lock_check(map))
550                 goto unmap;
551
552         if (munlockall()) {
553                 perror("munlockall()");
554                 goto unmap;
555         }
556
557         if (unlock_lock_check(map))
558                 goto unmap;
559
560         munmap(map, 2 * page_size);
561
562         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
563                    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
564
565         if (map == MAP_FAILED) {
566                 perror("test_munlockall second mmap");
567                 goto out;
568         }
569
570         if (mlockall(MCL_CURRENT | MCL_ONFAULT)) {
571                 perror("mlockall(MCL_CURRENT | MCL_ONFAULT)");
572                 goto unmap;
573         }
574
575         if (onfault_check(map))
576                 goto unmap;
577
578         if (munlockall()) {
579                 perror("munlockall()");
580                 goto unmap;
581         }
582
583         if (unlock_onfault_check(map))
584                 goto unmap;
585
586         if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
587                 perror("mlockall(MCL_CURRENT | MCL_FUTURE)");
588                 goto out;
589         }
590
591         if (lock_check(map))
592                 goto unmap;
593
594         if (munlockall()) {
595                 perror("munlockall()");
596                 goto unmap;
597         }
598
599         ret = unlock_lock_check(map);
600
601 unmap:
602         munmap(map, 2 * page_size);
603 out:
604         munlockall();
605         return ret;
606 }
607
608 static int test_vma_management(bool call_mlock)
609 {
610         int ret = 1;
611         void *map;
612         unsigned long page_size = getpagesize();
613         struct vm_boundaries page1;
614         struct vm_boundaries page2;
615         struct vm_boundaries page3;
616
617         map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
618                    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
619         if (map == MAP_FAILED) {
620                 perror("mmap()");
621                 return ret;
622         }
623
624         if (call_mlock && mlock2_(map, 3 * page_size, MLOCK_ONFAULT)) {
625                 if (errno == ENOSYS) {
626                         printf("Cannot call new mlock family, skipping test\n");
627                         _exit(0);
628                 }
629                 perror("mlock(ONFAULT)\n");
630                 goto out;
631         }
632
633         if (get_vm_area((unsigned long)map, &page1) ||
634             get_vm_area((unsigned long)map + page_size, &page2) ||
635             get_vm_area((unsigned long)map + page_size * 2, &page3)) {
636                 printf("couldn't find mapping in /proc/self/maps\n");
637                 goto out;
638         }
639
640         /*
641          * Before we unlock a portion, we need to that all three pages are in
642          * the same VMA.  If they are not we abort this test (Note that this is
643          * not a failure)
644          */
645         if (page1.start != page2.start || page2.start != page3.start) {
646                 printf("VMAs are not merged to start, aborting test\n");
647                 ret = 0;
648                 goto out;
649         }
650
651         if (munlock(map + page_size, page_size)) {
652                 perror("munlock()");
653                 goto out;
654         }
655
656         if (get_vm_area((unsigned long)map, &page1) ||
657             get_vm_area((unsigned long)map + page_size, &page2) ||
658             get_vm_area((unsigned long)map + page_size * 2, &page3)) {
659                 printf("couldn't find mapping in /proc/self/maps\n");
660                 goto out;
661         }
662
663         /* All three VMAs should be different */
664         if (page1.start == page2.start || page2.start == page3.start) {
665                 printf("failed to split VMA for munlock\n");
666                 goto out;
667         }
668
669         /* Now unlock the first and third page and check the VMAs again */
670         if (munlock(map, page_size * 3)) {
671                 perror("munlock()");
672                 goto out;
673         }
674
675         if (get_vm_area((unsigned long)map, &page1) ||
676             get_vm_area((unsigned long)map + page_size, &page2) ||
677             get_vm_area((unsigned long)map + page_size * 2, &page3)) {
678                 printf("couldn't find mapping in /proc/self/maps\n");
679                 goto out;
680         }
681
682         /* Now all three VMAs should be the same */
683         if (page1.start != page2.start || page2.start != page3.start) {
684                 printf("failed to merge VMAs after munlock\n");
685                 goto out;
686         }
687
688         ret = 0;
689 out:
690         munmap(map, 3 * page_size);
691         return ret;
692 }
693
694 static int test_mlockall(int (test_function)(bool call_mlock))
695 {
696         int ret = 1;
697
698         if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE)) {
699                 perror("mlockall");
700                 return ret;
701         }
702
703         ret = test_function(false);
704         munlockall();
705         return ret;
706 }
707
708 int main(int argc, char **argv)
709 {
710         int ret = 0;
711         ret += test_mlock_lock();
712         ret += test_mlock_onfault();
713         ret += test_munlockall();
714         ret += test_lock_onfault_of_present();
715         ret += test_vma_management(true);
716         ret += test_mlockall(test_vma_management);
717         return ret;
718 }