aboutsummaryrefslogtreecommitdiff
path: root/android/patches/0018-userfaultfd-Fix-arm-issues-and-Android-compatibility.patch
diff options
context:
space:
mode:
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.patch193
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
+