From 3fce534596d950ce2524275e90eaf9c949a81423 Mon Sep 17 00:00:00 2001 From: Edward Liaw Date: Wed, 20 Apr 2022 00:29:06 +0000 Subject: [PATCH 18/20] userfaultfd: Fix arm issues and Android compatibility Fix ARM related issues in userfaultfd selftest Following issues were observed while running userfaultfd selftest on ARM devices. On x86_64 no issues were detected: 1) posix_memalign returned tagged pointer, which isn't handled well by the kernel. So replace its use with mmap 2) pthread_create followed by fork caused deadlock in certain cases wherein fork required some work to be completed by the created thread. Used synchronization to ensure that created thread's start funtion has started before invoking fork. (cherry picked from commit 9b11d1b6ab483cb451b102a30d996dc180330161) Bug: 160737021 Bug: 169683130 Test: atest vts_linux_kselftest_arm_64 Enable userfaultfd selftest Now that the userfaultfd feature is enabled in the Android kernel, it makes sense to have its selftest enabled in the test infra. The test source code required the following changes: 1) Use UFFD_USER_MODE_ONLY as unprivileged processes are not allowed userfaults from kernel space on Android 2) Use random/initstate instead of random_r/initstate_r as bionic doesn't support the latter. 3) Since bionic doesn't have pthread_cancel() implemented, we implemented the same functionality using pthread_kill() and longjmp(). (cherry picked from commit 599ed29be16f2612c1ca1bc2cab1b95e1ae0e6f7) Bug: 160737021 Bug: 169683130 Test: treehugger --- tools/testing/selftests/vm/userfaultfd.c | 57 ++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/vm/userfaultfd.c b/tools/testing/selftests/vm/userfaultfd.c index 297f250c1d95..28230a57fedd 100644 --- a/tools/testing/selftests/vm/userfaultfd.c +++ b/tools/testing/selftests/vm/userfaultfd.c @@ -100,9 +100,11 @@ static int huge_fd; static unsigned long long *count_verify; static int uffd = -1; static int uffd_flags, finished, *pipefd; +static volatile bool ready_for_fork; static char *area_src, *area_src_alias, *area_dst, *area_dst_alias, *area_remap; static char *zeropage; pthread_attr_t attr; +pthread_key_t long_jmp_key; static bool test_collapse; /* Userfaultfd test statistics */ @@ -821,6 +823,9 @@ static void *uffd_poll_thread(void *arg) 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 <= 0) { @@ -868,15 +873,27 @@ static void *uffd_poll_thread(void *arg) 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) { struct uffd_stats *stats = (struct uffd_stats *)arg; struct uffd_msg msg; + jmp_buf env; + int setjmp_ret; - pthread_mutex_unlock(&uffd_read_mutex); - /* from here cancellation is ok */ + pthread_setspecific(long_jmp_key, &env); - for (;;) { + pthread_mutex_unlock(&uffd_read_mutex); + // 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; uffd_handle_page_fault(&msg, stats); @@ -974,7 +991,7 @@ static int stress(struct uffd_stats *uffd_stats) (void *)&uffd_stats[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; @@ -1208,6 +1225,10 @@ static int userfaultfd_events_test(void) char c; struct uffd_stats stats = { 0 }; + // 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); @@ -1231,6 +1252,11 @@ static int userfaultfd_events_test(void) if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, &stats)) err("uffd_poll_thread create"); + // 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) err("fork"); @@ -1261,6 +1287,10 @@ static int userfaultfd_sig_test(void) char c; struct uffd_stats stats = { 0 }; + // 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); @@ -1288,6 +1318,11 @@ static int userfaultfd_sig_test(void) if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, &stats)) err("uffd_poll_thread create"); + // 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) err("fork"); @@ -1540,6 +1575,7 @@ static int userfaultfd_stress(void) void *area; unsigned long nr; struct uffdio_register uffdio_register; + struct sigaction act; struct uffd_stats uffd_stats[nr_cpus]; uffd_test_ctx_init(0); @@ -1554,6 +1590,17 @@ static int userfaultfd_stress(void) pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 16*1024*1024); + // For handling thread termination of read thread in the absence 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; + } + while (bounces--) { printf("bounces: %d, mode:", bounces); if (bounces & BOUNCE_RANDOM) @@ -1670,6 +1717,8 @@ static int userfaultfd_stress(void) userfaultfd_pagemap_test(page_size * 512); } + pthread_key_delete(long_jmp_key); + return userfaultfd_zeropage_test() || userfaultfd_sig_test() || userfaultfd_events_test() || userfaultfd_minor_test(); } -- 2.42.0.609.gbb76f46606-goog