android13/external/linux-kselftest/android/patches/0019-userfaultfd.patch

262 lines
7.3 KiB
Diff

--- 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);