userfaultfd/selftests: reinitialize test context in each test
[linux-2.6-microblaze.git] / tools / testing / selftests / vm / userfaultfd.c
index 1f8e2be..f788161 100644 (file)
@@ -89,7 +89,8 @@ static int shm_fd;
 static int huge_fd;
 static char *huge_fd_off0;
 static unsigned long long *count_verify;
-static int uffd, uffd_flags, finished, *pipefd;
+static int uffd = -1;
+static int uffd_flags, finished, *pipefd;
 static char *area_src, *area_src_alias, *area_dst, *area_dst_alias;
 static char *zeropage;
 pthread_attr_t attr;
@@ -342,6 +343,111 @@ static struct uffd_test_ops hugetlb_uffd_test_ops = {
 
 static struct uffd_test_ops *uffd_test_ops;
 
+static void 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)
+               err("userfaultfd syscall not available in this kernel");
+       uffd_flags = fcntl(uffd, F_GETFD, NULL);
+
+       uffdio_api.api = UFFD_API;
+       uffdio_api.features = *features;
+       if (ioctl(uffd, UFFDIO_API, &uffdio_api))
+               err("UFFDIO_API failed.\nPlease make sure to "
+                   "run with either root or ptrace capability.");
+       if (uffdio_api.api != UFFD_API)
+               err("UFFDIO_API error: %" PRIu64, (uint64_t)uffdio_api.api);
+
+       *features = uffdio_api.features;
+}
+
+static inline void munmap_area(void **area)
+{
+       if (*area)
+               if (munmap(*area, nr_pages * page_size))
+                       err("munmap");
+
+       *area = NULL;
+}
+
+static void uffd_test_ctx_clear(void)
+{
+       size_t i;
+
+       if (pipefd) {
+               for (i = 0; i < nr_cpus * 2; ++i) {
+                       if (close(pipefd[i]))
+                               err("close pipefd");
+               }
+               free(pipefd);
+               pipefd = NULL;
+       }
+
+       if (count_verify) {
+               free(count_verify);
+               count_verify = NULL;
+       }
+
+       if (uffd != -1) {
+               if (close(uffd))
+                       err("close uffd");
+               uffd = -1;
+       }
+
+       huge_fd_off0 = NULL;
+       munmap_area((void **)&area_src);
+       munmap_area((void **)&area_src_alias);
+       munmap_area((void **)&area_dst);
+       munmap_area((void **)&area_dst_alias);
+}
+
+static void uffd_test_ctx_init_ext(uint64_t *features)
+{
+       unsigned long nr, cpu;
+
+       uffd_test_ctx_clear();
+
+       uffd_test_ops->allocate_area((void **)&area_src);
+       uffd_test_ops->allocate_area((void **)&area_dst);
+
+       uffd_test_ops->release_pages(area_src);
+       uffd_test_ops->release_pages(area_dst);
+
+       userfaultfd_open(features);
+
+       count_verify = malloc(nr_pages * sizeof(unsigned long long));
+       if (!count_verify)
+               err("count_verify");
+
+       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)
+               err("pipefd");
+       for (cpu = 0; cpu < nr_cpus; cpu++)
+               if (pipe2(&pipefd[cpu * 2], O_CLOEXEC | O_NONBLOCK))
+                       err("pipe");
+}
+
+static inline void uffd_test_ctx_init(uint64_t features)
+{
+       uffd_test_ctx_init_ext(&features);
+}
+
 static int my_bcmp(char *str1, char *str2, size_t n)
 {
        unsigned long i;
@@ -726,40 +832,6 @@ static int stress(struct uffd_stats *uffd_stats)
        return 0;
 }
 
-static int userfaultfd_open_ext(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 userfaultfd_open(uint64_t features)
-{
-       return userfaultfd_open_ext(&features);
-}
-
 sigjmp_buf jbuf, *sigbuf;
 
 static void sighndl(int sig, siginfo_t *siginfo, void *ptr)
@@ -868,6 +940,8 @@ static int faulting_process(int signal_test)
                          MREMAP_MAYMOVE | MREMAP_FIXED, area_src);
        if (area_dst == MAP_FAILED)
                err("mremap");
+       /* Reset area_src since we just clobbered it */
+       area_src = NULL;
 
        for (; nr < nr_pages; nr++) {
                count = *area_count(area_dst, nr);
@@ -961,10 +1035,8 @@ static int userfaultfd_zeropage_test(void)
        printf("testing UFFDIO_ZEROPAGE: ");
        fflush(stdout);
 
-       uffd_test_ops->release_pages(area_dst);
+       uffd_test_ctx_init(0);
 
-       if (userfaultfd_open(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;
@@ -981,7 +1053,6 @@ static int userfaultfd_zeropage_test(void)
                if (my_bcmp(area_dst, zeropage, page_size))
                        err("zeropage is not zero");
 
-       close(uffd);
        printf("done.\n");
        return 0;
 }
@@ -999,12 +1070,10 @@ static int userfaultfd_events_test(void)
        printf("testing events (fork, remap, remove): ");
        fflush(stdout);
 
-       uffd_test_ops->release_pages(area_dst);
-
        features = UFFD_FEATURE_EVENT_FORK | UFFD_FEATURE_EVENT_REMAP |
                UFFD_FEATURE_EVENT_REMOVE;
-       if (userfaultfd_open(features))
-               return 1;
+       uffd_test_ctx_init(features);
+
        fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK);
 
        uffdio_register.range.start = (unsigned long) area_dst;
@@ -1037,8 +1106,6 @@ static int userfaultfd_events_test(void)
        if (pthread_join(uffd_mon, NULL))
                return 1;
 
-       close(uffd);
-
        uffd_stats_report(&stats, 1);
 
        return stats.missing_faults != nr_pages;
@@ -1058,11 +1125,9 @@ static int userfaultfd_sig_test(void)
        printf("testing signal delivery: ");
        fflush(stdout);
 
-       uffd_test_ops->release_pages(area_dst);
-
        features = UFFD_FEATURE_EVENT_FORK|UFFD_FEATURE_SIGBUS;
-       if (userfaultfd_open(features))
-               return 1;
+       uffd_test_ctx_init(features);
+
        fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK);
 
        uffdio_register.range.start = (unsigned long) area_dst;
@@ -1103,7 +1168,6 @@ static int userfaultfd_sig_test(void)
        printf("done.\n");
        if (userfaults)
                err("Signal test failed, userfaults: %ld", userfaults);
-       close(uffd);
 
        return userfaults != 0;
 }
@@ -1126,10 +1190,7 @@ static int userfaultfd_minor_test(void)
        printf("testing minor faults: ");
        fflush(stdout);
 
-       uffd_test_ops->release_pages(area_dst);
-
-       if (userfaultfd_open_ext(&features))
-               return 1;
+       uffd_test_ctx_init_ext(&features);
        /* If kernel reports the feature isn't supported, skip the test. */
        if (!(features & UFFD_FEATURE_MINOR_HUGETLBFS)) {
                printf("skipping test due to lack of feature support\n");
@@ -1183,8 +1244,6 @@ static int userfaultfd_minor_test(void)
        if (pthread_join(uffd_mon, NULL))
                return 1;
 
-       close(uffd);
-
        uffd_stats_report(&stats, 1);
 
        return stats.missing_faults != 0 || stats.minor_faults != nr_pages;
@@ -1267,7 +1326,7 @@ static void userfaultfd_pagemap_test(unsigned int test_pgsize)
        /* Flush so it doesn't flush twice in parent/child later */
        fflush(stdout);
 
-       uffd_test_ops->release_pages(area_dst);
+       uffd_test_ctx_init(0);
 
        if (test_pgsize > page_size) {
                /* This is a thp test */
@@ -1279,9 +1338,6 @@ static void userfaultfd_pagemap_test(unsigned int test_pgsize)
                        err("madvise(MADV_NOHUGEPAGE) failed");
        }
 
-       if (userfaultfd_open(0))
-               err("userfaultfd_open");
-
        uffdio_register.range.start = (unsigned long) area_dst;
        uffdio_register.range.len = nr_pages * page_size;
        uffdio_register.mode = UFFDIO_REGISTER_MODE_WP;
@@ -1324,7 +1380,6 @@ static void userfaultfd_pagemap_test(unsigned int test_pgsize)
        pagemap_check_wp(value, false);
 
        close(pagemap_fd);
-       close(uffd);
        printf("done\n");
 }
 
@@ -1334,50 +1389,9 @@ static int userfaultfd_stress(void)
        char *tmp_area;
        unsigned long nr;
        struct uffdio_register uffdio_register;
-       unsigned long cpu;
        struct uffd_stats uffd_stats[nr_cpus];
 
-       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))
-               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;
-               }
-       }
+       uffd_test_ctx_init(0);
 
        if (posix_memalign(&area, page_size, page_size))
                err("out of memory");
@@ -1498,8 +1512,6 @@ static int userfaultfd_stress(void)
                uffd_stats_report(uffd_stats, nr_cpus);
        }
 
-       close(uffd);
-
        if (test_type == TEST_ANON) {
                /*
                 * shmem/hugetlb won't be able to run since they have different