diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-02-17 02:49:22 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-02-17 02:49:22 +0000 |
commit | 4a3b0ad6c9729dd9356ebf60b83ad44bd7861393 (patch) | |
tree | f595bb458e3d00877d287a5705087ebf9a8c109e | |
parent | b89dc3da1a308fdcd019c6a675583a3e135b4f90 (diff) | |
parent | d4063865cc3ab201ab3cc24965b1d2896902cb8b (diff) | |
download | linux-kselftest-4a3b0ad6c9729dd9356ebf60b83ad44bd7861393.tar.gz |
Snap for 8192738 from d4063865cc3ab201ab3cc24965b1d2896902cb8b to tm-frc-extservices-release
Change-Id: Ibcf844e6f1566d5c56ea83277e9f136f0b8b73ce
-rw-r--r-- | Android.bp | 9 | ||||
-rw-r--r-- | OWNERS | 1 | ||||
-rw-r--r-- | android/README.md | 3 | ||||
-rw-r--r-- | android/kselftest_test_list.mk | 2 | ||||
-rw-r--r-- | android/patches/0019-userfaultfd.patch | 261 | ||||
-rw-r--r-- | android/patches/0020-selftests-binderfs-fix-Wunused-label-error.patch | 31 | ||||
-rw-r--r-- | android/patches/0021-selftests-binderfs-skip-unprivileged-test-when-USER_.patch | 36 | ||||
-rw-r--r-- | android/patches/0022-selftests-binderfs-skip-tests-for-unsupported-kernel.patch | 65 | ||||
-rw-r--r-- | tools/testing/selftests/filesystems/binderfs/binderfs_test.c | 36 | ||||
-rw-r--r-- | tools/testing/selftests/vm/userfaultfd.c | 106 |
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", ], @@ -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(); } |