aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-02-17 02:49:22 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-02-17 02:49:22 +0000
commit4a3b0ad6c9729dd9356ebf60b83ad44bd7861393 (patch)
treef595bb458e3d00877d287a5705087ebf9a8c109e
parentb89dc3da1a308fdcd019c6a675583a3e135b4f90 (diff)
parentd4063865cc3ab201ab3cc24965b1d2896902cb8b (diff)
downloadlinux-kselftest-4a3b0ad6c9729dd9356ebf60b83ad44bd7861393.tar.gz
Snap for 8192738 from d4063865cc3ab201ab3cc24965b1d2896902cb8b to tm-frc-extservices-release
Change-Id: Ibcf844e6f1566d5c56ea83277e9f136f0b8b73ce
-rw-r--r--Android.bp9
-rw-r--r--OWNERS1
-rw-r--r--android/README.md3
-rw-r--r--android/kselftest_test_list.mk2
-rw-r--r--android/patches/0019-userfaultfd.patch261
-rw-r--r--android/patches/0020-selftests-binderfs-fix-Wunused-label-error.patch31
-rw-r--r--android/patches/0021-selftests-binderfs-skip-unprivileged-test-when-USER_.patch36
-rw-r--r--android/patches/0022-selftests-binderfs-skip-tests-for-unsupported-kernel.patch65
-rw-r--r--tools/testing/selftests/filesystems/binderfs/binderfs_test.c36
-rw-r--r--tools/testing/selftests/vm/userfaultfd.c106
10 files changed, 519 insertions, 31 deletions
diff --git a/Android.bp b/Android.bp
index e56904c3722e..0b9166c56315 100644
--- a/Android.bp
+++ b/Android.bp
@@ -96,6 +96,14 @@ cc_defaults {
test_per_src: true,
}
+// binderfs test
+cc_test {
+ name: "kselftest_binderfs_tests",
+ relative_install_path: "linux-kselftest/filesystems/binderfs",
+ srcs: ["tools/testing/selftests/filesystems/binderfs/binderfs_test.c"],
+ defaults: ["kselftest_defaults"],
+}
+
// breakpoints test
cc_test {
name: "kselftest_breakpoints_tests",
@@ -575,6 +583,7 @@ cc_test {
"tools/testing/selftests/vm/mlock-random-test.c",
"tools/testing/selftests/vm/mlock2-tests.c",
"tools/testing/selftests/vm/on-fault-limit.c",
+ "tools/testing/selftests/vm/userfaultfd.c",
"tools/testing/selftests/vm/thuge-gen.c",
"tools/testing/selftests/vm/transhuge-stress.c",
],
diff --git a/OWNERS b/OWNERS
index 64bf926733d4..3ae489ae8e6c 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1,3 @@
drosen@google.com
+smuckle@google.com
willmcvicker@google.com
diff --git a/android/README.md b/android/README.md
index 2c94e05b4cdf..7a12a1d3aa21 100644
--- a/android/README.md
+++ b/android/README.md
@@ -334,9 +334,6 @@ kernel module is not required by Android.
The hugepage-mmap, hugepage-shm, compaction, and map_hugetlb tests rely on
hugetlbfs/hugetlb page support which is not required by Android.
-The userfaultfd test relies on the userfaultfd syscall which is not required by
-Android.
-
### watchdog/
The watchdog test depends on CONFIG_WATCHDOG which is not required by Android.
diff --git a/android/kselftest_test_list.mk b/android/kselftest_test_list.mk
index 5445bd12d26f..07df6fc5d758 100644
--- a/android/kselftest_test_list.mk
+++ b/android/kselftest_test_list.mk
@@ -12,6 +12,7 @@ kselftest_modules := \
# From Android.bp
kselftest_modules += \
+ kselftest_binderfs_tests_binderfs_test \
kselftest_breakpoints_tests_breakpoint_test \
kselftest_breakpoints_tests_breakpoint_test_arm64 \
kselftest_breakpoints_tests_step_after_suspend_test \
@@ -52,6 +53,7 @@ kselftest_modules += \
kselftest_vdso_test \
kselftest_vm_tests_mlock2-tests \
kselftest_vm_tests_on-fault-limit \
+ kselftest_vm_tests_userfaultfd \
kselftest_vm64_tests_virtual_address_range \
kselftest_x86_ptrace_syscall \
kselftest_x86_check_initial_reg_state \
diff --git a/android/patches/0019-userfaultfd.patch b/android/patches/0019-userfaultfd.patch
new file mode 100644
index 000000000000..67fe8edc9848
--- /dev/null
+++ b/android/patches/0019-userfaultfd.patch
@@ -0,0 +1,261 @@
+--- 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);
diff --git a/android/patches/0020-selftests-binderfs-fix-Wunused-label-error.patch b/android/patches/0020-selftests-binderfs-fix-Wunused-label-error.patch
new file mode 100644
index 000000000000..8b872f18e51f
--- /dev/null
+++ b/android/patches/0020-selftests-binderfs-fix-Wunused-label-error.patch
@@ -0,0 +1,31 @@
+From c4970a0b5461b65ce3b7d1c40a76672bd18d3ba5 Mon Sep 17 00:00:00 2001
+From: Carlos Llamas <cmllamas@google.com>
+Date: Mon, 26 Jul 2021 17:06:02 +0000
+Subject: [PATCH 1/2] selftests/binderfs: fix Wunused-label error
+
+Fix the following build issue:
+ tools/testing/selftests/filesystems/binderfs/binderfs_test.c:244:1:
+ error: unused label 'on_error' [-Werror,-Wunused-label]
+ on_error:
+ ^~~~~~~~~
+
+Signed-off-by: Carlos Llamas <cmllamas@google.com>
+---
+ tools/testing/selftests/filesystems/binderfs/binderfs_test.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c
+index 8c2ed962e1c7..540ad4840495 100644
+--- a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c
++++ b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c
+@@ -241,7 +241,6 @@ static void __do_binderfs_test(void)
+ /* binder-control device removal failed as expected */
+ ksft_inc_xfail_cnt();
+
+-on_error:
+ ret = umount2("/dev/binderfs", MNT_DETACH);
+ keep ?: rmdir_protect_errno("/dev/binderfs");
+ if (ret < 0)
+--
+2.32.0.432.gabb21c7263-goog
+
diff --git a/android/patches/0021-selftests-binderfs-skip-unprivileged-test-when-USER_.patch b/android/patches/0021-selftests-binderfs-skip-unprivileged-test-when-USER_.patch
new file mode 100644
index 000000000000..8a5af5794c01
--- /dev/null
+++ b/android/patches/0021-selftests-binderfs-skip-unprivileged-test-when-USER_.patch
@@ -0,0 +1,36 @@
+From e51806a0b6918cefbd068cbb8100f410e7b01970 Mon Sep 17 00:00:00 2001
+From: Carlos Llamas <cmllamas@google.com>
+Date: Mon, 26 Jul 2021 17:12:42 +0000
+Subject: [PATCH 2/2] selftests/binderfs: skip unprivileged test when !USER_NS
+
+Binderfs unprivileged test requires CONFIG_USER_NS to be set. However,
+this option is not required by Android platforms. Lets skip the test
+as the underlying routine __do_binderfs_test() has already been checked
+by the binderfs_test_privileged() anyway.
+
+Signed-off-by: Carlos Llamas <cmllamas@google.com>
+---
+ .../testing/selftests/filesystems/binderfs/binderfs_test.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c
+index 540ad4840495..397bf265ba97 100644
+--- a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c
++++ b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c
+@@ -262,6 +262,13 @@ static void binderfs_test_privileged()
+
+ static void binderfs_test_unprivileged()
+ {
++ /* This test depends on USER_NS which is not required by Android */
++ if (access("/proc/self/ns/user", F_OK) != 0) {
++ ksft_test_result_skip("%s: user namespace not supported\n",
++ __func__);
++ return;
++ }
++
+ change_to_userns();
+ __do_binderfs_test();
+ }
+--
+2.32.0.432.gabb21c7263-goog
+
diff --git a/android/patches/0022-selftests-binderfs-skip-tests-for-unsupported-kernel.patch b/android/patches/0022-selftests-binderfs-skip-tests-for-unsupported-kernel.patch
new file mode 100644
index 000000000000..7b1e1a1c32bc
--- /dev/null
+++ b/android/patches/0022-selftests-binderfs-skip-tests-for-unsupported-kernel.patch
@@ -0,0 +1,65 @@
+From 57b74c1b318625057398ccbe479a157507488d8c Mon Sep 17 00:00:00 2001
+From: Carlos Llamas <cmllamas@google.com>
+Date: Tue, 14 Dec 2021 05:58:48 +0000
+Subject: [PATCH] selftests/binderfs: skip tests for unsupported kernels
+
+Binderfs was first introduced in Linux 5.0 release, so we need to skip
+these tests for older kernels. Also, we force a successful exit as the
+current framework doesn't handle KSFT_SKIP return codes.
+
+Signed-off-by: Carlos Llamas <cmllamas@google.com>
+---
+ .../filesystems/binderfs/binderfs_test.c | 28 +++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+diff --git a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c
+index 8c2ed962e1c7..be5a8f446066 100644
+--- a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c
++++ b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c
+@@ -12,6 +12,7 @@
+ #include <sys/mount.h>
+ #include <sys/stat.h>
+ #include <sys/types.h>
++#include <sys/utsname.h>
+ #include <unistd.h>
+ #include <linux/android/binder.h>
+ #include <linux/android/binderfs.h>
+@@ -267,8 +268,35 @@ static void binderfs_test_unprivileged()
+ __do_binderfs_test();
+ }
+
++/* binderfs first introduced in Linux 5.0 */
++bool binderfs_supported(void)
++{
++ const int min_kernel_version = 5;
++ struct utsname utsname;
++ int ret, version;
++
++ ret = uname(&utsname);
++ if (ret)
++ ksft_exit_fail_msg("%s - Failed to get kernel version\n",
++ strerror(errno));
++
++ ret = sscanf(utsname.release, "%d.", &version);
++ if (ret != 1)
++ ksft_exit_fail_msg("%s - Failed to parse uname: %s\n",
++ strerror(errno), utsname.release);
++
++ return version >= min_kernel_version? true: false;
++}
++
++
+ int main(int argc, char *argv[])
+ {
++ /* Force success exit for older kernels */
++ if (!binderfs_supported()) {
++ ksft_print_msg("Skipping tests - binderfs not supported\n");
++ ksft_exit_pass();
++ }
++
+ binderfs_test_privileged();
+ binderfs_test_unprivileged();
+ ksft_exit_pass();
+--
+2.34.1.173.g76aa8bc2d0-goog
+
diff --git a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c
index 8c2ed962e1c7..4465b4f6a81a 100644
--- a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c
+++ b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c
@@ -12,6 +12,7 @@
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/utsname.h>
#include <unistd.h>
#include <linux/android/binder.h>
#include <linux/android/binderfs.h>
@@ -241,7 +242,6 @@ static void __do_binderfs_test(void)
/* binder-control device removal failed as expected */
ksft_inc_xfail_cnt();
-on_error:
ret = umount2("/dev/binderfs", MNT_DETACH);
keep ?: rmdir_protect_errno("/dev/binderfs");
if (ret < 0)
@@ -263,12 +263,46 @@ static void binderfs_test_privileged()
static void binderfs_test_unprivileged()
{
+ /* This test depends on USER_NS which is not required by Android */
+ if (access("/proc/self/ns/user", F_OK) != 0) {
+ ksft_test_result_skip("%s: user namespace not supported\n",
+ __func__);
+ return;
+ }
+
change_to_userns();
__do_binderfs_test();
}
+/* binderfs first introduced in Linux 5.0 */
+bool binderfs_supported(void)
+{
+ const int min_kernel_version = 5;
+ struct utsname utsname;
+ int ret, version;
+
+ ret = uname(&utsname);
+ if (ret)
+ ksft_exit_fail_msg("%s - Failed to get kernel version\n",
+ strerror(errno));
+
+ ret = sscanf(utsname.release, "%d.", &version);
+ if (ret != 1)
+ ksft_exit_fail_msg("%s - Failed to parse uname: %s\n",
+ strerror(errno), utsname.release);
+
+ return version >= min_kernel_version? true: false;
+}
+
+
int main(int argc, char *argv[])
{
+ /* Force success exit for older kernels */
+ if (!binderfs_supported()) {
+ ksft_print_msg("Skipping tests - binderfs not supported\n");
+ ksft_exit_pass();
+ }
+
binderfs_test_privileged();
binderfs_test_unprivileged();
ksft_exit_pass();
diff --git a/tools/testing/selftests/vm/userfaultfd.c b/tools/testing/selftests/vm/userfaultfd.c
index d3362777a425..ffc78f3e4444 100644
--- a/tools/testing/selftests/vm/userfaultfd.c
+++ b/tools/testing/selftests/vm/userfaultfd.c
@@ -82,9 +82,11 @@ static int huge_fd;
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 int anon_release_pages(char *rel_area)
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 int my_bcmp(char *str1, char *str2, size_t n)
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 @@ static void *locking_thread(void *arg)
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 @@ 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)
@@ -548,18 +540,31 @@ 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)
{
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 @@ static int stress(unsigned long *userfaults)
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 @@ static int stress(unsigned long *userfaults)
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,10 +660,15 @@ static int userfaultfd_open(int features)
{
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) {
+ 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);
@@ -914,6 +925,10 @@ static int userfaultfd_events_test(void)
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 +957,11 @@ static int userfaultfd_events_test(void)
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 +994,10 @@ static int userfaultfd_sig_test(void)
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 +1031,11 @@ static int userfaultfd_sig_test(void)
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 +1065,7 @@ static int userfaultfd_stress(void)
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 +1124,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 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 +1256,8 @@ static int userfaultfd_stress(void)
printf("\n");
}
+ pthread_key_delete(long_jmp_key);
+
if (err)
return err;
@@ -1291,6 +1334,9 @@ static void sigalrm(int sig)
int main(int argc, char **argv)
{
+ char randstate[64];
+ unsigned int seed;
+
if (argc < 4)
usage();
@@ -1332,6 +1378,12 @@ int main(int argc, char **argv)
}
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();
}