9 #include <sys/resource.h>
15 #define MLOCK_ONFAULT 1
19 #define MCL_ONFAULT (MCL_FUTURE << 1)
22 static int mlock2_(void *start
, size_t len
, int flags
)
25 return syscall(__NR_mlock2
, start
, len
, flags
);
32 struct vm_boundaries
{
37 static int get_vm_area(unsigned long addr
, struct vm_boundaries
*area
)
41 char line
[1024] = {0};
50 file
= fopen("/proc/self/maps", "r");
56 memset(area
, 0, sizeof(struct vm_boundaries
));
58 while(fgets(line
, 1024, file
)) {
59 end_addr
= strchr(line
, '-');
61 printf("cannot parse /proc/self/maps\n");
66 stop
= strchr(end_addr
, ' ');
68 printf("cannot parse /proc/self/maps\n");
73 sscanf(line
, "%lx", &start
);
74 sscanf(end_addr
, "%lx", &end
);
76 if (start
<= addr
&& end
> addr
) {
88 static uint64_t get_pageflags(unsigned long addr
)
94 file
= fopen("/proc/self/pagemap", "r");
96 perror("fopen pagemap");
100 offset
= addr
/ getpagesize() * sizeof(pfn
);
102 if (fseek(file
, offset
, SEEK_SET
)) {
103 perror("fseek pagemap");
107 if (fread(&pfn
, sizeof(pfn
), 1, file
) != 1) {
108 perror("fread pagemap");
116 static uint64_t get_kpageflags(unsigned long pfn
)
121 file
= fopen("/proc/kpageflags", "r");
123 perror("fopen kpageflags");
127 if (fseek(file
, pfn
* sizeof(flags
), SEEK_SET
)) {
128 perror("fseek kpageflags");
132 if (fread(&flags
, sizeof(flags
), 1, file
) != 1) {
133 perror("fread kpageflags");
141 static FILE *seek_to_smaps_entry(unsigned long addr
)
146 unsigned long start
, end
;
148 unsigned long offset
;
153 file
= fopen("/proc/self/smaps", "r");
155 perror("fopen smaps");
159 while (getline(&line
, &size
, file
) > 0) {
160 if (sscanf(line
, "%lx-%lx %s %lx %s %lu %s\n",
161 &start
, &end
, perms
, &offset
, dev
, &inode
, path
) < 6)
164 if (start
<= addr
&& addr
< end
)
181 #define VMFLAGS "VmFlags:"
183 static bool is_vmflag_set(unsigned long addr
, const char *vmflag
)
191 smaps
= seek_to_smaps_entry(addr
);
193 printf("Unable to parse /proc/self/smaps\n");
197 while (getline(&line
, &size
, smaps
) > 0) {
198 if (!strstr(line
, VMFLAGS
)) {
205 flags
= line
+ strlen(VMFLAGS
);
206 ret
= (strstr(flags
, vmflag
) != NULL
);
220 static bool is_vma_lock_on_fault(unsigned long addr
)
225 unsigned long vma_size
, vma_rss
;
230 locked
= is_vmflag_set(addr
, LOCKED
);
234 smaps
= seek_to_smaps_entry(addr
);
236 printf("Unable to parse /proc/self/smaps\n");
240 while (getline(&line
, &size
, smaps
) > 0) {
241 if (!strstr(line
, SIZE
)) {
248 value
= line
+ strlen(SIZE
);
249 if (sscanf(value
, "%lu kB", &vma_size
) < 1) {
250 printf("Unable to parse smaps entry for Size\n");
256 while (getline(&line
, &size
, smaps
) > 0) {
257 if (!strstr(line
, RSS
)) {
264 value
= line
+ strlen(RSS
);
265 if (sscanf(value
, "%lu kB", &vma_rss
) < 1) {
266 printf("Unable to parse smaps entry for Rss\n");
272 ret
= locked
&& (vma_rss
< vma_size
);
280 #define PRESENT_BIT 0x8000000000000000ULL
281 #define PFN_MASK 0x007FFFFFFFFFFFFFULL
282 #define UNEVICTABLE_BIT (1UL << 18)
284 static int lock_check(char *map
)
286 unsigned long page_size
= getpagesize();
287 uint64_t page1_flags
, page2_flags
;
289 page1_flags
= get_pageflags((unsigned long)map
);
290 page2_flags
= get_pageflags((unsigned long)map
+ page_size
);
292 /* Both pages should be present */
293 if (((page1_flags
& PRESENT_BIT
) == 0) ||
294 ((page2_flags
& PRESENT_BIT
) == 0)) {
295 printf("Failed to make both pages present\n");
299 page1_flags
= get_kpageflags(page1_flags
& PFN_MASK
);
300 page2_flags
= get_kpageflags(page2_flags
& PFN_MASK
);
302 /* Both pages should be unevictable */
303 if (((page1_flags
& UNEVICTABLE_BIT
) == 0) ||
304 ((page2_flags
& UNEVICTABLE_BIT
) == 0)) {
305 printf("Failed to make both pages unevictable\n");
309 if (!is_vmflag_set((unsigned long)map
, LOCKED
)) {
310 printf("VMA flag %s is missing on page 1\n", LOCKED
);
314 if (!is_vmflag_set((unsigned long)map
+ page_size
, LOCKED
)) {
315 printf("VMA flag %s is missing on page 2\n", LOCKED
);
322 static int unlock_lock_check(char *map
)
324 unsigned long page_size
= getpagesize();
325 uint64_t page1_flags
, page2_flags
;
327 page1_flags
= get_pageflags((unsigned long)map
);
328 page2_flags
= get_pageflags((unsigned long)map
+ page_size
);
329 page1_flags
= get_kpageflags(page1_flags
& PFN_MASK
);
330 page2_flags
= get_kpageflags(page2_flags
& PFN_MASK
);
332 if ((page1_flags
& UNEVICTABLE_BIT
) || (page2_flags
& UNEVICTABLE_BIT
)) {
333 printf("A page is still marked unevictable after unlock\n");
337 if (is_vmflag_set((unsigned long)map
, LOCKED
)) {
338 printf("VMA flag %s is present on page 1 after unlock\n", LOCKED
);
342 if (is_vmflag_set((unsigned long)map
+ page_size
, LOCKED
)) {
343 printf("VMA flag %s is present on page 2 after unlock\n", LOCKED
);
350 static int test_mlock_lock()
354 unsigned long page_size
= getpagesize();
356 map
= mmap(NULL
, 2 * page_size
, PROT_READ
| PROT_WRITE
,
357 MAP_ANONYMOUS
| MAP_PRIVATE
, 0, 0);
358 if (map
== MAP_FAILED
) {
359 perror("test_mlock_locked mmap");
363 if (mlock2_(map
, 2 * page_size
, 0)) {
364 if (errno
== ENOSYS
) {
365 printf("Cannot call new mlock family, skipping test\n");
375 /* Now unlock and recheck attributes */
376 if (munlock(map
, 2 * page_size
)) {
381 ret
= unlock_lock_check(map
);
384 munmap(map
, 2 * page_size
);
389 static int onfault_check(char *map
)
391 unsigned long page_size
= getpagesize();
392 uint64_t page1_flags
, page2_flags
;
394 page1_flags
= get_pageflags((unsigned long)map
);
395 page2_flags
= get_pageflags((unsigned long)map
+ page_size
);
397 /* Neither page should be present */
398 if ((page1_flags
& PRESENT_BIT
) || (page2_flags
& PRESENT_BIT
)) {
399 printf("Pages were made present by MLOCK_ONFAULT\n");
404 page1_flags
= get_pageflags((unsigned long)map
);
405 page2_flags
= get_pageflags((unsigned long)map
+ page_size
);
407 /* Only page 1 should be present */
408 if ((page1_flags
& PRESENT_BIT
) == 0) {
409 printf("Page 1 is not present after fault\n");
411 } else if (page2_flags
& PRESENT_BIT
) {
412 printf("Page 2 was made present\n");
416 page1_flags
= get_kpageflags(page1_flags
& PFN_MASK
);
418 /* Page 1 should be unevictable */
419 if ((page1_flags
& UNEVICTABLE_BIT
) == 0) {
420 printf("Failed to make faulted page unevictable\n");
424 if (!is_vma_lock_on_fault((unsigned long)map
)) {
425 printf("VMA is not marked for lock on fault\n");
429 if (!is_vma_lock_on_fault((unsigned long)map
+ page_size
)) {
430 printf("VMA is not marked for lock on fault\n");
437 static int unlock_onfault_check(char *map
)
439 unsigned long page_size
= getpagesize();
440 uint64_t page1_flags
;
442 page1_flags
= get_pageflags((unsigned long)map
);
443 page1_flags
= get_kpageflags(page1_flags
& PFN_MASK
);
445 if (page1_flags
& UNEVICTABLE_BIT
) {
446 printf("Page 1 is still marked unevictable after unlock\n");
450 if (is_vma_lock_on_fault((unsigned long)map
) ||
451 is_vma_lock_on_fault((unsigned long)map
+ page_size
)) {
452 printf("VMA is still lock on fault after unlock\n");
459 static int test_mlock_onfault()
463 unsigned long page_size
= getpagesize();
465 map
= mmap(NULL
, 2 * page_size
, PROT_READ
| PROT_WRITE
,
466 MAP_ANONYMOUS
| MAP_PRIVATE
, 0, 0);
467 if (map
== MAP_FAILED
) {
468 perror("test_mlock_locked mmap");
472 if (mlock2_(map
, 2 * page_size
, MLOCK_ONFAULT
)) {
473 if (errno
== ENOSYS
) {
474 printf("Cannot call new mlock family, skipping test\n");
477 perror("mlock2(MLOCK_ONFAULT)");
481 if (onfault_check(map
))
484 /* Now unlock and recheck attributes */
485 if (munlock(map
, 2 * page_size
)) {
486 if (errno
== ENOSYS
) {
487 printf("Cannot call new mlock family, skipping test\n");
494 ret
= unlock_onfault_check(map
);
496 munmap(map
, 2 * page_size
);
501 static int test_lock_onfault_of_present()
505 unsigned long page_size
= getpagesize();
506 uint64_t page1_flags
, page2_flags
;
508 map
= mmap(NULL
, 2 * page_size
, PROT_READ
| PROT_WRITE
,
509 MAP_ANONYMOUS
| MAP_PRIVATE
, 0, 0);
510 if (map
== MAP_FAILED
) {
511 perror("test_mlock_locked mmap");
517 if (mlock2_(map
, 2 * page_size
, MLOCK_ONFAULT
)) {
518 if (errno
== ENOSYS
) {
519 printf("Cannot call new mlock family, skipping test\n");
522 perror("mlock2(MLOCK_ONFAULT)");
526 page1_flags
= get_pageflags((unsigned long)map
);
527 page2_flags
= get_pageflags((unsigned long)map
+ page_size
);
528 page1_flags
= get_kpageflags(page1_flags
& PFN_MASK
);
529 page2_flags
= get_kpageflags(page2_flags
& PFN_MASK
);
531 /* Page 1 should be unevictable */
532 if ((page1_flags
& UNEVICTABLE_BIT
) == 0) {
533 printf("Failed to make present page unevictable\n");
537 if (!is_vma_lock_on_fault((unsigned long)map
) ||
538 !is_vma_lock_on_fault((unsigned long)map
+ page_size
)) {
539 printf("VMA with present pages is not marked lock on fault\n");
544 munmap(map
, 2 * page_size
);
549 static int test_munlockall()
553 unsigned long page_size
= getpagesize();
555 map
= mmap(NULL
, 2 * page_size
, PROT_READ
| PROT_WRITE
,
556 MAP_ANONYMOUS
| MAP_PRIVATE
, 0, 0);
558 if (map
== MAP_FAILED
) {
559 perror("test_munlockall mmap");
563 if (mlockall(MCL_CURRENT
)) {
564 perror("mlockall(MCL_CURRENT)");
572 perror("munlockall()");
576 if (unlock_lock_check(map
))
579 munmap(map
, 2 * page_size
);
581 map
= mmap(NULL
, 2 * page_size
, PROT_READ
| PROT_WRITE
,
582 MAP_ANONYMOUS
| MAP_PRIVATE
, 0, 0);
584 if (map
== MAP_FAILED
) {
585 perror("test_munlockall second mmap");
589 if (mlockall(MCL_CURRENT
| MCL_ONFAULT
)) {
590 perror("mlockall(MCL_CURRENT | MCL_ONFAULT)");
594 if (onfault_check(map
))
598 perror("munlockall()");
602 if (unlock_onfault_check(map
))
605 if (mlockall(MCL_CURRENT
| MCL_FUTURE
)) {
606 perror("mlockall(MCL_CURRENT | MCL_FUTURE)");
614 perror("munlockall()");
618 ret
= unlock_lock_check(map
);
621 munmap(map
, 2 * page_size
);
627 static int test_vma_management(bool call_mlock
)
631 unsigned long page_size
= getpagesize();
632 struct vm_boundaries page1
;
633 struct vm_boundaries page2
;
634 struct vm_boundaries page3
;
636 map
= mmap(NULL
, 3 * page_size
, PROT_READ
| PROT_WRITE
,
637 MAP_ANONYMOUS
| MAP_PRIVATE
, 0, 0);
638 if (map
== MAP_FAILED
) {
643 if (call_mlock
&& mlock2_(map
, 3 * page_size
, MLOCK_ONFAULT
)) {
644 if (errno
== ENOSYS
) {
645 printf("Cannot call new mlock family, skipping test\n");
648 perror("mlock(ONFAULT)\n");
652 if (get_vm_area((unsigned long)map
, &page1
) ||
653 get_vm_area((unsigned long)map
+ page_size
, &page2
) ||
654 get_vm_area((unsigned long)map
+ page_size
* 2, &page3
)) {
655 printf("couldn't find mapping in /proc/self/maps\n");
660 * Before we unlock a portion, we need to that all three pages are in
661 * the same VMA. If they are not we abort this test (Note that this is
664 if (page1
.start
!= page2
.start
|| page2
.start
!= page3
.start
) {
665 printf("VMAs are not merged to start, aborting test\n");
670 if (munlock(map
+ page_size
, page_size
)) {
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");
682 /* All three VMAs should be different */
683 if (page1
.start
== page2
.start
|| page2
.start
== page3
.start
) {
684 printf("failed to split VMA for munlock\n");
688 /* Now unlock the first and third page and check the VMAs again */
689 if (munlock(map
, page_size
* 3)) {
694 if (get_vm_area((unsigned long)map
, &page1
) ||
695 get_vm_area((unsigned long)map
+ page_size
, &page2
) ||
696 get_vm_area((unsigned long)map
+ page_size
* 2, &page3
)) {
697 printf("couldn't find mapping in /proc/self/maps\n");
701 /* Now all three VMAs should be the same */
702 if (page1
.start
!= page2
.start
|| page2
.start
!= page3
.start
) {
703 printf("failed to merge VMAs after munlock\n");
709 munmap(map
, 3 * page_size
);
713 static int test_mlockall(int (test_function
)(bool call_mlock
))
717 if (mlockall(MCL_CURRENT
| MCL_ONFAULT
| MCL_FUTURE
)) {
722 ret
= test_function(false);
727 int main(int argc
, char **argv
)
730 ret
+= test_mlock_lock();
731 ret
+= test_mlock_onfault();
732 ret
+= test_munlockall();
733 ret
+= test_lock_onfault_of_present();
734 ret
+= test_vma_management(true);
735 ret
+= test_mlockall(test_vma_management
);