diff options
author | Mike Rapoport <rppt@linux.vnet.ibm.com> | 2017-02-22 18:44:06 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-22 19:41:29 -0500 |
commit | da5502c0a39b7ba28f403a4b87d1dd690e7829bf (patch) | |
tree | 86560b3bf7f765d973f4ccb90c826ca0297b6eb7 /tools | |
parent | aa0d27217477acbc1cfcc4fdaa4de4f3ce545b4e (diff) |
userfaultfd: non-cooperative: selftest: add test for FORK, MADVDONTNEED and REMAP events
Add test for userfaultfd events used in non-cooperative scenario when
the process that monitors the userfaultfd and handles user faults is not
the same process that causes the page faults.
Link: http://lkml.kernel.org/r/20161216144821.5183-41-aarcange@redhat.com
Signed-off-by: Mike Rapoport <rppt@linux.vnet.ibm.com>
Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
Cc: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
Cc: Hillf Danton <hillf.zj@alibaba-inc.com>
Cc: Michael Rapoport <RAPOPORT@il.ibm.com>
Cc: Mike Kravetz <mike.kravetz@oracle.com>
Cc: Pavel Emelyanov <xemul@parallels.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/userfaultfd.c | 175 |
1 files changed, 163 insertions, 12 deletions
diff --git a/tools/testing/selftests/vm/userfaultfd.c b/tools/testing/selftests/vm/userfaultfd.c index c79c372db2da..71b4d820b011 100644 --- a/tools/testing/selftests/vm/userfaultfd.c +++ b/tools/testing/selftests/vm/userfaultfd.c | |||
@@ -63,6 +63,7 @@ | |||
63 | #include <sys/mman.h> | 63 | #include <sys/mman.h> |
64 | #include <sys/syscall.h> | 64 | #include <sys/syscall.h> |
65 | #include <sys/ioctl.h> | 65 | #include <sys/ioctl.h> |
66 | #include <sys/wait.h> | ||
66 | #include <pthread.h> | 67 | #include <pthread.h> |
67 | #include <linux/userfaultfd.h> | 68 | #include <linux/userfaultfd.h> |
68 | 69 | ||
@@ -347,6 +348,7 @@ static void *uffd_poll_thread(void *arg) | |||
347 | unsigned long cpu = (unsigned long) arg; | 348 | unsigned long cpu = (unsigned long) arg; |
348 | struct pollfd pollfd[2]; | 349 | struct pollfd pollfd[2]; |
349 | struct uffd_msg msg; | 350 | struct uffd_msg msg; |
351 | struct uffdio_register uffd_reg; | ||
350 | int ret; | 352 | int ret; |
351 | unsigned long offset; | 353 | unsigned long offset; |
352 | char tmp_chr; | 354 | char tmp_chr; |
@@ -378,16 +380,35 @@ static void *uffd_poll_thread(void *arg) | |||
378 | continue; | 380 | continue; |
379 | perror("nonblocking read error"), exit(1); | 381 | perror("nonblocking read error"), exit(1); |
380 | } | 382 | } |
381 | if (msg.event != UFFD_EVENT_PAGEFAULT) | 383 | switch (msg.event) { |
384 | default: | ||
382 | fprintf(stderr, "unexpected msg event %u\n", | 385 | fprintf(stderr, "unexpected msg event %u\n", |
383 | msg.event), exit(1); | 386 | msg.event), exit(1); |
384 | if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) | 387 | break; |
385 | fprintf(stderr, "unexpected write fault\n"), exit(1); | 388 | case UFFD_EVENT_PAGEFAULT: |
386 | offset = (char *)(unsigned long)msg.arg.pagefault.address - | 389 | if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) |
387 | area_dst; | 390 | fprintf(stderr, "unexpected write fault\n"), exit(1); |
388 | offset &= ~(page_size-1); | 391 | offset = (char *)(unsigned long)msg.arg.pagefault.address - |
389 | if (copy_page(uffd, offset)) | 392 | area_dst; |
390 | userfaults++; | 393 | offset &= ~(page_size-1); |
394 | if (copy_page(uffd, offset)) | ||
395 | userfaults++; | ||
396 | break; | ||
397 | case UFFD_EVENT_FORK: | ||
398 | uffd = msg.arg.fork.ufd; | ||
399 | pollfd[0].fd = uffd; | ||
400 | break; | ||
401 | case UFFD_EVENT_MADVDONTNEED: | ||
402 | uffd_reg.range.start = msg.arg.madv_dn.start; | ||
403 | uffd_reg.range.len = msg.arg.madv_dn.end - | ||
404 | msg.arg.madv_dn.start; | ||
405 | if (ioctl(uffd, UFFDIO_UNREGISTER, &uffd_reg.range)) | ||
406 | fprintf(stderr, "madv_dn failure\n"), exit(1); | ||
407 | break; | ||
408 | case UFFD_EVENT_REMAP: | ||
409 | area_dst = (char *)(unsigned long)msg.arg.remap.to; | ||
410 | break; | ||
411 | } | ||
391 | } | 412 | } |
392 | return (void *)userfaults; | 413 | return (void *)userfaults; |
393 | } | 414 | } |
@@ -512,7 +533,7 @@ static int stress(unsigned long *userfaults) | |||
512 | return 0; | 533 | return 0; |
513 | } | 534 | } |
514 | 535 | ||
515 | static int userfaultfd_open(void) | 536 | static int userfaultfd_open(int features) |
516 | { | 537 | { |
517 | struct uffdio_api uffdio_api; | 538 | struct uffdio_api uffdio_api; |
518 | 539 | ||
@@ -525,7 +546,7 @@ static int userfaultfd_open(void) | |||
525 | uffd_flags = fcntl(uffd, F_GETFD, NULL); | 546 | uffd_flags = fcntl(uffd, F_GETFD, NULL); |
526 | 547 | ||
527 | uffdio_api.api = UFFD_API; | 548 | uffdio_api.api = UFFD_API; |
528 | uffdio_api.features = 0; | 549 | uffdio_api.features = features; |
529 | if (ioctl(uffd, UFFDIO_API, &uffdio_api)) { | 550 | if (ioctl(uffd, UFFDIO_API, &uffdio_api)) { |
530 | fprintf(stderr, "UFFDIO_API\n"); | 551 | fprintf(stderr, "UFFDIO_API\n"); |
531 | return 1; | 552 | return 1; |
@@ -538,6 +559,132 @@ static int userfaultfd_open(void) | |||
538 | return 0; | 559 | return 0; |
539 | } | 560 | } |
540 | 561 | ||
562 | /* | ||
563 | * For non-cooperative userfaultfd test we fork() a process that will | ||
564 | * generate pagefaults, will mremap the area monitored by the | ||
565 | * userfaultfd and at last this process will release the monitored | ||
566 | * area. | ||
567 | * For the anonymous and shared memory the area is divided into two | ||
568 | * parts, the first part is accessed before mremap, and the second | ||
569 | * part is accessed after mremap. Since hugetlbfs does not support | ||
570 | * mremap, the entire monitored area is accessed in a single pass for | ||
571 | * HUGETLB_TEST. | ||
572 | * The release of the pages currently generates event only for | ||
573 | * anonymous memory (UFFD_EVENT_MADVDONTNEED), hence it is not checked | ||
574 | * for hugetlb and shmem. | ||
575 | */ | ||
576 | static int faulting_process(void) | ||
577 | { | ||
578 | unsigned long nr; | ||
579 | unsigned long long count; | ||
580 | |||
581 | #ifndef HUGETLB_TEST | ||
582 | unsigned long split_nr_pages = (nr_pages + 1) / 2; | ||
583 | #else | ||
584 | unsigned long split_nr_pages = nr_pages; | ||
585 | #endif | ||
586 | |||
587 | for (nr = 0; nr < split_nr_pages; nr++) { | ||
588 | count = *area_count(area_dst, nr); | ||
589 | if (count != count_verify[nr]) { | ||
590 | fprintf(stderr, | ||
591 | "nr %lu memory corruption %Lu %Lu\n", | ||
592 | nr, count, | ||
593 | count_verify[nr]), exit(1); | ||
594 | } | ||
595 | } | ||
596 | |||
597 | #ifndef HUGETLB_TEST | ||
598 | area_dst = mremap(area_dst, nr_pages * page_size, nr_pages * page_size, | ||
599 | MREMAP_MAYMOVE | MREMAP_FIXED, area_src); | ||
600 | if (area_dst == MAP_FAILED) | ||
601 | perror("mremap"), exit(1); | ||
602 | |||
603 | for (; nr < nr_pages; nr++) { | ||
604 | count = *area_count(area_dst, nr); | ||
605 | if (count != count_verify[nr]) { | ||
606 | fprintf(stderr, | ||
607 | "nr %lu memory corruption %Lu %Lu\n", | ||
608 | nr, count, | ||
609 | count_verify[nr]), exit(1); | ||
610 | } | ||
611 | } | ||
612 | |||
613 | #ifndef SHMEM_TEST | ||
614 | if (release_pages(area_dst)) | ||
615 | return 1; | ||
616 | |||
617 | for (nr = 0; nr < nr_pages; nr++) { | ||
618 | if (my_bcmp(area_dst + nr * page_size, zeropage, page_size)) | ||
619 | fprintf(stderr, "nr %lu is not zero\n", nr), exit(1); | ||
620 | } | ||
621 | #endif /* SHMEM_TEST */ | ||
622 | |||
623 | #endif /* HUGETLB_TEST */ | ||
624 | |||
625 | return 0; | ||
626 | } | ||
627 | |||
628 | static int userfaultfd_events_test(void) | ||
629 | { | ||
630 | struct uffdio_register uffdio_register; | ||
631 | unsigned long expected_ioctls; | ||
632 | unsigned long userfaults; | ||
633 | pthread_t uffd_mon; | ||
634 | int err, features; | ||
635 | pid_t pid; | ||
636 | char c; | ||
637 | |||
638 | printf("testing events (fork, remap, madv_dn): "); | ||
639 | fflush(stdout); | ||
640 | |||
641 | if (release_pages(area_dst)) | ||
642 | return 1; | ||
643 | |||
644 | features = UFFD_FEATURE_EVENT_FORK | UFFD_FEATURE_EVENT_REMAP | | ||
645 | UFFD_FEATURE_EVENT_MADVDONTNEED; | ||
646 | if (userfaultfd_open(features) < 0) | ||
647 | return 1; | ||
648 | fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); | ||
649 | |||
650 | uffdio_register.range.start = (unsigned long) area_dst; | ||
651 | uffdio_register.range.len = nr_pages * page_size; | ||
652 | uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; | ||
653 | if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) | ||
654 | fprintf(stderr, "register failure\n"), exit(1); | ||
655 | |||
656 | expected_ioctls = EXPECTED_IOCTLS; | ||
657 | if ((uffdio_register.ioctls & expected_ioctls) != | ||
658 | expected_ioctls) | ||
659 | fprintf(stderr, | ||
660 | "unexpected missing ioctl for anon memory\n"), | ||
661 | exit(1); | ||
662 | |||
663 | if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, NULL)) | ||
664 | perror("uffd_poll_thread create"), exit(1); | ||
665 | |||
666 | pid = fork(); | ||
667 | if (pid < 0) | ||
668 | perror("fork"), exit(1); | ||
669 | |||
670 | if (!pid) | ||
671 | return faulting_process(); | ||
672 | |||
673 | waitpid(pid, &err, 0); | ||
674 | if (err) | ||
675 | fprintf(stderr, "faulting process failed\n"), exit(1); | ||
676 | |||
677 | if (write(pipefd[1], &c, sizeof(c)) != sizeof(c)) | ||
678 | perror("pipe write"), exit(1); | ||
679 | if (pthread_join(uffd_mon, (void **)&userfaults)) | ||
680 | return 1; | ||
681 | |||
682 | close(uffd); | ||
683 | printf("userfaults: %ld\n", userfaults); | ||
684 | |||
685 | return userfaults != nr_pages; | ||
686 | } | ||
687 | |||
541 | static int userfaultfd_stress(void) | 688 | static int userfaultfd_stress(void) |
542 | { | 689 | { |
543 | void *area; | 690 | void *area; |
@@ -555,7 +702,7 @@ static int userfaultfd_stress(void) | |||
555 | if (!area_dst) | 702 | if (!area_dst) |
556 | return 1; | 703 | return 1; |
557 | 704 | ||
558 | if (userfaultfd_open() < 0) | 705 | if (userfaultfd_open(0) < 0) |
559 | return 1; | 706 | return 1; |
560 | 707 | ||
561 | count_verify = malloc(nr_pages * sizeof(unsigned long long)); | 708 | count_verify = malloc(nr_pages * sizeof(unsigned long long)); |
@@ -702,7 +849,11 @@ static int userfaultfd_stress(void) | |||
702 | printf("\n"); | 849 | printf("\n"); |
703 | } | 850 | } |
704 | 851 | ||
705 | return err; | 852 | if (err) |
853 | return err; | ||
854 | |||
855 | close(uffd); | ||
856 | return userfaultfd_events_test(); | ||
706 | } | 857 | } |
707 | 858 | ||
708 | #ifndef HUGETLB_TEST | 859 | #ifndef HUGETLB_TEST |