diff options
Diffstat (limited to 'android/patches/0018-userfaultfd-Fix-arm-issues-and-Android-compatibility.patch')
-rw-r--r-- | android/patches/0018-userfaultfd-Fix-arm-issues-and-Android-compatibility.patch | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/android/patches/0018-userfaultfd-Fix-arm-issues-and-Android-compatibility.patch b/android/patches/0018-userfaultfd-Fix-arm-issues-and-Android-compatibility.patch new file mode 100644 index 000000000000..e42218b145b4 --- /dev/null +++ b/android/patches/0018-userfaultfd-Fix-arm-issues-and-Android-compatibility.patch @@ -0,0 +1,193 @@ +From 3fce534596d950ce2524275e90eaf9c949a81423 Mon Sep 17 00:00:00 2001 +From: Edward Liaw <edliaw@google.com> +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 + |