--- b/tools/testing/selftests/vm/userfaultfd.c +++ b/tools/testing/selftests/vm/userfaultfd.c @@ -82,9 +82,11 @@ static char *huge_fd_off0; static unsigned long long *count_verify; static int uffd, uffd_flags, finished, *pipefd; +static volatile bool ready_for_fork; static char *area_src, *area_src_alias, *area_dst, *area_dst_alias; static char *zeropage; pthread_attr_t attr; +pthread_key_t long_jmp_key; /* pthread_mutex_t starts at page offset 0 */ #define area_mutex(___area, ___nr) \ @@ -139,8 +141,11 @@ static void anon_allocate_area(void **alloc_area) { - if (posix_memalign(alloc_area, page_size, nr_pages * page_size)) { - fprintf(stderr, "out of memory\n"); + // We can't use posix_memalign due to pointer-tagging used in bionic. + *alloc_area = mmap(NULL, nr_pages * page_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (*alloc_area == MAP_FAILED) { + fprintf(stderr, "anon memory mmap failed\n"); *alloc_area = NULL; } } @@ -284,23 +289,11 @@ static void *locking_thread(void *arg) { unsigned long cpu = (unsigned long) arg; - struct random_data rand; unsigned long page_nr = *(&(page_nr)); /* uninitialized warning */ - int32_t rand_nr; unsigned long long count; - char randstate[64]; - unsigned int seed; time_t start; - if (bounces & BOUNCE_RANDOM) { - seed = (unsigned int) time(NULL) - bounces; - if (!(bounces & BOUNCE_RACINGFAULTS)) - seed += cpu; - bzero(&rand, sizeof(rand)); - bzero(&randstate, sizeof(randstate)); - if (initstate_r(seed, randstate, sizeof(randstate), &rand)) - fprintf(stderr, "srandom_r error\n"), exit(1); - } else { + if (!(bounces & BOUNCE_RANDOM)) { page_nr = -bounces; if (!(bounces & BOUNCE_RACINGFAULTS)) page_nr += cpu * nr_pages_per_cpu; @@ -308,13 +301,9 @@ while (!finished) { if (bounces & BOUNCE_RANDOM) { - if (random_r(&rand, &rand_nr)) - fprintf(stderr, "random_r 1 error\n"), exit(1); - page_nr = rand_nr; - if (sizeof(page_nr) > sizeof(rand_nr)) { - if (random_r(&rand, &rand_nr)) - fprintf(stderr, "random_r 2 error\n"), exit(1); - page_nr |= (((unsigned long) rand_nr) << 16) << + page_nr = random(); + if (sizeof(page_nr) > sizeof(uint32_t)) { + page_nr |= (((unsigned long) random()) << 16) << 16; } } else @@ -501,6 +490,9 @@ pollfd[1].fd = pipefd[cpu*2]; pollfd[1].events = POLLIN; + // Notify the main thread that it can now fork. + ready_for_fork = true; + for (;;) { ret = poll(pollfd, 2, -1); if (!ret) @@ -548,18 +540,31 @@ pthread_mutex_t uffd_read_mutex = PTHREAD_MUTEX_INITIALIZER; +static void sigusr1_handler(int signum, siginfo_t *siginfo, void *ptr) +{ + jmp_buf *env; + env = pthread_getspecific(long_jmp_key); + longjmp(*env, 1); +} + static void *uffd_read_thread(void *arg) { unsigned long *this_cpu_userfaults; struct uffd_msg msg; + jmp_buf env; + int setjmp_ret; + + pthread_setspecific(long_jmp_key, &env); this_cpu_userfaults = (unsigned long *) arg; *this_cpu_userfaults = 0; pthread_mutex_unlock(&uffd_read_mutex); - /* from here cancellation is ok */ - for (;;) { + // One first return setjmp return 0. On second (fake) return from + // longjmp() it returns the provided value, which will be 1 in our case. + setjmp_ret = setjmp(env); + while (!setjmp_ret) { if (uffd_read_msg(uffd, &msg)) continue; (*this_cpu_userfaults) += uffd_handle_page_fault(&msg); @@ -608,6 +613,7 @@ background_thread, (void *)cpu)) return 1; } + for (cpu = 0; cpu < nr_cpus; cpu++) if (pthread_join(background_threads[cpu], NULL)) return 1; @@ -640,7 +646,7 @@ if (pthread_join(uffd_threads[cpu], &_userfaults[cpu])) return 1; } else { - if (pthread_cancel(uffd_threads[cpu])) + if (pthread_kill(uffd_threads[cpu], SIGUSR1)) return 1; if (pthread_join(uffd_threads[cpu], NULL)) return 1; @@ -654,7 +660,7 @@ { struct uffdio_api uffdio_api; - uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); + 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"); @@ -914,6 +920,10 @@ pid_t pid; char c; + // All the syscalls below up to pthread_create will ensure that this + // write is completed before, the uffd_thread sets it to true. + ready_for_fork = false; + printf("testing events (fork, remap, remove): "); fflush(stdout); @@ -942,6 +952,11 @@ if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, NULL)) perror("uffd_poll_thread create"), exit(1); + // Wait for the poll_thread to start executing before forking. This is + // required to avoid a deadlock, which can happen if poll_thread doesn't + // start getting executed by the time fork is invoked. + while (!ready_for_fork); + pid = fork(); if (pid < 0) perror("fork"), exit(1); @@ -974,6 +989,10 @@ pid_t pid; char c; + // All the syscalls below up to pthread_create will ensure that this + // write is completed before, the uffd_thread sets it to true. + ready_for_fork = false; + printf("testing signal delivery: "); fflush(stdout); @@ -1007,6 +1026,11 @@ if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, NULL)) perror("uffd_poll_thread create"), exit(1); + // Wait for the poll_thread to start executing before forking. This is + // required to avoid a deadlock, which can happen if poll_thread doesn't + // start getting executed by the time fork is invoked. + while (!ready_for_fork); + pid = fork(); if (pid < 0) perror("fork"), exit(1); @@ -1036,6 +1060,7 @@ char *tmp_area; unsigned long nr; struct uffdio_register uffdio_register; + struct sigaction act; unsigned long cpu; int err; unsigned long userfaults[nr_cpus]; @@ -1094,6 +1119,17 @@ pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 16*1024*1024); + // For handling thread termination of read thread in the absense of + // pthread_cancel(). + pthread_key_create(&long_jmp_key, NULL); + memset(&act, 0, sizeof(act)); + act.sa_sigaction = sigusr1_handler; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGUSR1, &act, 0)) { + perror("sigaction"); + return 1; + } + err = 0; while (bounces--) { unsigned long expected_ioctls; @@ -1215,6 +1251,8 @@ printf("\n"); } + pthread_key_delete(long_jmp_key); + if (err) return err; @@ -1291,6 +1329,9 @@ int main(int argc, char **argv) { + char randstate[64]; + unsigned int seed; + if (argc < 4) usage(); @@ -1332,6 +1373,12 @@ } printf("nr_pages: %lu, nr_pages_per_cpu: %lu\n", nr_pages, nr_pages_per_cpu); + + seed = (unsigned int) time(NULL); + bzero(&randstate, sizeof(randstate)); + if (!initstate(seed, randstate, sizeof(randstate))) + fprintf(stderr, "srandom_r error\n"), exit(1); + return userfaultfd_stress(); } --- a/tools/testing/selftests/vm/userfaultfd.c +++ b/tools/testing/selftests/vm/userfaultfd.c @@ -662,8 +662,13 @@ static int userfaultfd_open(int features) uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY); if (uffd < 0) { + if (errno == ENOSYS) { + fprintf(stderr, + "userfaultfd syscall not available in this kernel\n"); + exit(KSFT_PASS); + } fprintf(stderr, - "userfaultfd syscall not available in this kernel\n"); + "userfaultfd syscall failed with errno: %d\n", errno); return 1; } uffd_flags = fcntl(uffd, F_GETFD, NULL);