diff --git a/tools/testing/selftests/vm/userfaultfd.c b/tools/testing/selftests/vm/userfaultfd.c index 613b384ea90c..bbfa456be3e5 100644 --- a/tools/testing/selftests/vm/userfaultfd.c +++ b/tools/testing/selftests/vm/userfaultfd.c @@ -85,8 +85,7 @@ static int shm_fd; static int huge_fd; static char *huge_fd_off0; static unsigned long long *count_verify; -static int uffd = -1; -static int uffd_flags, finished, *pipefd; +static int uffd, uffd_flags, finished, *pipefd; static char *area_src, *area_src_alias, *area_dst, *area_dst_alias; static char *zeropage; pthread_attr_t attr; @@ -310,146 +309,6 @@ static struct uffd_test_ops hugetlb_uffd_test_ops = { static struct uffd_test_ops *uffd_test_ops; -static int userfaultfd_open(uint64_t *features) -{ - struct uffdio_api uffdio_api; - - uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY); - if (uffd < 0) { - fprintf(stderr, - "userfaultfd syscall not available in this kernel\n"); - return 1; - } - uffd_flags = fcntl(uffd, F_GETFD, NULL); - - uffdio_api.api = UFFD_API; - uffdio_api.features = *features; - if (ioctl(uffd, UFFDIO_API, &uffdio_api)) { - fprintf(stderr, "UFFDIO_API failed.\nPlease make sure to " - "run with either root or ptrace capability.\n"); - return 1; - } - if (uffdio_api.api != UFFD_API) { - fprintf(stderr, "UFFDIO_API error: %" PRIu64 "\n", - (uint64_t)uffdio_api.api); - return 1; - } - - *features = uffdio_api.features; - return 0; -} - -static int uffd_test_ctx_init_ext(uint64_t *features) -{ - unsigned long nr, cpu; - - uffd_test_ops->allocate_area((void **)&area_src); - if (!area_src) - return 1; - uffd_test_ops->allocate_area((void **)&area_dst); - if (!area_dst) - return 1; - - if (uffd_test_ops->release_pages(area_src)) - return 1; - - if (uffd_test_ops->release_pages(area_dst)) - return 1; - - if (userfaultfd_open(features)) - return 1; - - count_verify = malloc(nr_pages * sizeof(unsigned long long)); - if (!count_verify) { - perror("count_verify"); - return 1; - } - - for (nr = 0; nr < nr_pages; nr++) { - *area_mutex(area_src, nr) = - (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; - count_verify[nr] = *area_count(area_src, nr) = 1; - /* - * In the transition between 255 to 256, powerpc will - * read out of order in my_bcmp and see both bytes as - * zero, so leave a placeholder below always non-zero - * after the count, to avoid my_bcmp to trigger false - * positives. - */ - *(area_count(area_src, nr) + 1) = 1; - } - - pipefd = malloc(sizeof(int) * nr_cpus * 2); - if (!pipefd) { - perror("pipefd"); - return 1; - } - for (cpu = 0; cpu < nr_cpus; cpu++) { - if (pipe2(&pipefd[cpu * 2], O_CLOEXEC | O_NONBLOCK)) { - perror("pipe"); - return 1; - } - } - - return 0; -} - -static inline int uffd_test_ctx_init(uint64_t features) -{ - return uffd_test_ctx_init_ext(&features); -} - -static inline int munmap_area(void **area) -{ - if (*area) { - if (munmap(*area, nr_pages * page_size)) { - perror("munmap"); - return 1; - } - } - - *area = NULL; - return 0; -} - -static int uffd_test_ctx_clear(void) -{ - int ret = 0; - size_t i; - - if (pipefd) { - for (i = 0; i < nr_cpus * 2; ++i) { - if (close(pipefd[i])) { - perror("close pipefd"); - ret = 1; - } - } - free(pipefd); - pipefd = NULL; - } - - if (count_verify) { - free(count_verify); - count_verify = NULL; - } - - if (uffd != -1) { - if (close(uffd)) { - perror("close uffd"); - ret = 1; - } - uffd = -1; - } - - huge_fd_off0 = NULL; - ret |= munmap_area((void **)&area_src); - ret |= munmap_area((void **)&area_src_alias); - ret |= munmap_area((void **)&area_dst); - ret |= munmap_area((void **)&area_dst_alias); - - return ret; -} - static int my_bcmp(char *str1, char *str2, size_t n) { unsigned long i; @@ -814,6 +673,38 @@ static int stress(unsigned long *userfaults) return 0; } +static int userfaultfd_open_ext(uint64_t *features) +{ + struct uffdio_api uffdio_api; + + uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); + if (uffd < 0) { + fprintf(stderr, + "userfaultfd syscall not available in this kernel\n"); + return 1; + } + uffd_flags = fcntl(uffd, F_GETFD, NULL); + + uffdio_api.api = UFFD_API; + uffdio_api.features = *features; + if (ioctl(uffd, UFFDIO_API, &uffdio_api)) { + fprintf(stderr, "UFFDIO_API\n"); + return 1; + } + if (uffdio_api.api != UFFD_API) { + fprintf(stderr, "UFFDIO_API error %Lu\n", uffdio_api.api); + return 1; + } + + *features = uffdio_api.features; + return 0; +} + +static int userfaultfd_open(uint64_t features) +{ + return userfaultfd_open_ext(&features); +} + sigjmp_buf jbuf, *sigbuf; static void sighndl(int sig, siginfo_t *siginfo, void *ptr) @@ -908,12 +799,8 @@ static int faulting_process(int signal_test) area_dst = mremap(area_dst, nr_pages * page_size, nr_pages * page_size, MREMAP_MAYMOVE | MREMAP_FIXED, area_src); - if (area_dst == MAP_FAILED) { - perror("mremap"); - exit(1); - } - /* Reset area_src since we just clobbered it */ - area_src = NULL; + if (area_dst == MAP_FAILED) + perror("mremap"), exit(1); for (; nr < nr_pages; nr++) { count = *area_count(area_dst, nr); @@ -1018,9 +905,11 @@ static int userfaultfd_zeropage_test(void) printf("testing UFFDIO_ZEROPAGE: "); fflush(stdout); - if (uffd_test_ctx_clear() || uffd_test_ctx_init(0)) + if (uffd_test_ops->release_pages(area_dst)) return 1; + if (userfaultfd_open(0) < 0) + return 1; uffdio_register.range.start = (unsigned long) area_dst; uffdio_register.range.len = nr_pages * page_size; uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; @@ -1039,6 +928,7 @@ static int userfaultfd_zeropage_test(void) fprintf(stderr, "zeropage is not zero\n"), exit(1); } + close(uffd); printf("done.\n"); return 0; } @@ -1056,11 +946,13 @@ static int userfaultfd_events_test(void) printf("testing events (fork, remap, remove): "); fflush(stdout); - features = UFFD_FEATURE_EVENT_FORK | UFFD_FEATURE_EVENT_REMAP | - UFFD_FEATURE_EVENT_REMOVE; - if (uffd_test_ctx_clear() || uffd_test_ctx_init(features)) + if (uffd_test_ops->release_pages(area_dst)) return 1; + features = UFFD_FEATURE_EVENT_FORK | UFFD_FEATURE_EVENT_REMAP | + UFFD_FEATURE_EVENT_REMOVE; + if (userfaultfd_open(features) < 0) + return 1; fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); uffdio_register.range.start = (unsigned long) area_dst; @@ -1095,6 +987,7 @@ static int userfaultfd_events_test(void) if (pthread_join(uffd_mon, (void **)&userfaults)) return 1; + close(uffd); printf("userfaults: %ld\n", userfaults); return userfaults != nr_pages; @@ -1113,10 +1006,12 @@ static int userfaultfd_sig_test(void) printf("testing signal delivery: "); fflush(stdout); - features = UFFD_FEATURE_EVENT_FORK|UFFD_FEATURE_SIGBUS; - if (uffd_test_ctx_clear() || uffd_test_ctx_init(features)) + if (uffd_test_ops->release_pages(area_dst)) return 1; + features = UFFD_FEATURE_EVENT_FORK|UFFD_FEATURE_SIGBUS; + if (userfaultfd_open(features) < 0) + return 1; fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); uffdio_register.range.start = (unsigned long) area_dst; @@ -1161,6 +1056,7 @@ static int userfaultfd_sig_test(void) if (userfaults) fprintf(stderr, "Signal test failed, userfaults: %ld\n", userfaults); + close(uffd); return userfaults != 0; } @@ -1182,7 +1078,10 @@ static int userfaultfd_minor_test(void) printf("testing minor faults: "); fflush(stdout); - if (uffd_test_ctx_clear() || uffd_test_ctx_init_ext(&features)) + if (uffd_test_ops->release_pages(area_dst)) + return 1; + + if (userfaultfd_open_ext(&features)) return 1; /* If kernel reports the feature isn't supported, skip the test. */ if (!(features & UFFD_FEATURE_MINOR_HUGETLBFS)) { @@ -1251,6 +1150,7 @@ static int userfaultfd_minor_test(void) return 1; printf("userfaults: %ld\n", userfaults); + close(uffd); return userfaults != nr_pages; } @@ -1260,11 +1160,51 @@ static int userfaultfd_stress(void) char *tmp_area; unsigned long nr; struct uffdio_register uffdio_register; + unsigned long cpu; int err; unsigned long userfaults[nr_cpus]; - if (uffd_test_ctx_init(0)) + uffd_test_ops->allocate_area((void **)&area_src); + if (!area_src) return 1; + uffd_test_ops->allocate_area((void **)&area_dst); + if (!area_dst) + return 1; + + if (userfaultfd_open(0) < 0) + return 1; + + count_verify = malloc(nr_pages * sizeof(unsigned long long)); + if (!count_verify) { + perror("count_verify"); + return 1; + } + + for (nr = 0; nr < nr_pages; nr++) { + *area_mutex(area_src, nr) = (pthread_mutex_t) + PTHREAD_MUTEX_INITIALIZER; + count_verify[nr] = *area_count(area_src, nr) = 1; + /* + * In the transition between 255 to 256, powerpc will + * read out of order in my_bcmp and see both bytes as + * zero, so leave a placeholder below always non-zero + * after the count, to avoid my_bcmp to trigger false + * positives. + */ + *(area_count(area_src, nr) + 1) = 1; + } + + pipefd = malloc(sizeof(int) * nr_cpus * 2); + if (!pipefd) { + perror("pipefd"); + return 1; + } + for (cpu = 0; cpu < nr_cpus; cpu++) { + if (pipe2(&pipefd[cpu*2], O_CLOEXEC | O_NONBLOCK)) { + perror("pipe"); + return 1; + } + } if (posix_memalign(&area, page_size, page_size)) { fprintf(stderr, "out of memory\n"); @@ -1402,6 +1342,7 @@ static int userfaultfd_stress(void) if (err) return err; + close(uffd); return userfaultfd_zeropage_test() || userfaultfd_sig_test() || userfaultfd_events_test() || userfaultfd_minor_test(); }