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