diff options
Diffstat (limited to 'tools/testing/selftests/kvm/lib/userfaultfd_util.c')
| -rw-r--r-- | tools/testing/selftests/kvm/lib/userfaultfd_util.c | 156 | 
1 files changed, 88 insertions, 68 deletions
diff --git a/tools/testing/selftests/kvm/lib/userfaultfd_util.c b/tools/testing/selftests/kvm/lib/userfaultfd_util.c index f4eef6eb2dc2..7c9de8414462 100644 --- a/tools/testing/selftests/kvm/lib/userfaultfd_util.c +++ b/tools/testing/selftests/kvm/lib/userfaultfd_util.c @@ -6,9 +6,6 @@   * Copyright (C) 2018, Red Hat, Inc.   * Copyright (C) 2019-2022 Google LLC   */ - -#define _GNU_SOURCE /* for pipe2 */ -  #include <inttypes.h>  #include <stdio.h>  #include <stdlib.h> @@ -16,6 +13,7 @@  #include <poll.h>  #include <pthread.h>  #include <linux/userfaultfd.h> +#include <sys/epoll.h>  #include <sys/syscall.h>  #include "kvm_util.h" @@ -27,76 +25,69 @@  static void *uffd_handler_thread_fn(void *arg)  { -	struct uffd_desc *uffd_desc = (struct uffd_desc *)arg; -	int uffd = uffd_desc->uffd; -	int pipefd = uffd_desc->pipefds[0]; -	useconds_t delay = uffd_desc->delay; +	struct uffd_reader_args *reader_args = (struct uffd_reader_args *)arg; +	int uffd = reader_args->uffd;  	int64_t pages = 0;  	struct timespec start;  	struct timespec ts_diff; +	struct epoll_event evt; +	int epollfd; + +	epollfd = epoll_create(1); +	TEST_ASSERT(epollfd >= 0, "Failed to create epollfd."); + +	evt.events = EPOLLIN | EPOLLEXCLUSIVE; +	evt.data.u32 = 0; +	TEST_ASSERT(!epoll_ctl(epollfd, EPOLL_CTL_ADD, uffd, &evt), +		    "Failed to add uffd to epollfd"); + +	evt.events = EPOLLIN; +	evt.data.u32 = 1; +	TEST_ASSERT(!epoll_ctl(epollfd, EPOLL_CTL_ADD, reader_args->pipe, &evt), +		    "Failed to add pipe to epollfd");  	clock_gettime(CLOCK_MONOTONIC, &start);  	while (1) {  		struct uffd_msg msg; -		struct pollfd pollfd[2]; -		char tmp_chr;  		int r; -		pollfd[0].fd = uffd; -		pollfd[0].events = POLLIN; -		pollfd[1].fd = pipefd; -		pollfd[1].events = POLLIN; +		r = epoll_wait(epollfd, &evt, 1, -1); +		TEST_ASSERT(r == 1, +			    "Unexpected number of events (%d) from epoll, errno = %d", +			    r, errno); -		r = poll(pollfd, 2, -1); -		switch (r) { -		case -1: -			pr_info("poll err"); -			continue; -		case 0: -			continue; -		case 1: -			break; -		default: -			pr_info("Polling uffd returned %d", r); -			return NULL; -		} - -		if (pollfd[0].revents & POLLERR) { -			pr_info("uffd revents has POLLERR"); -			return NULL; -		} +		if (evt.data.u32 == 1) { +			char tmp_chr; -		if (pollfd[1].revents & POLLIN) { -			r = read(pollfd[1].fd, &tmp_chr, 1); +			TEST_ASSERT(!(evt.events & (EPOLLERR | EPOLLHUP)), +				    "Reader thread received EPOLLERR or EPOLLHUP on pipe."); +			r = read(reader_args->pipe, &tmp_chr, 1);  			TEST_ASSERT(r == 1, -				    "Error reading pipefd in UFFD thread"); +				    "Error reading pipefd in uffd reader thread");  			break;  		} -		if (!(pollfd[0].revents & POLLIN)) -			continue; +		TEST_ASSERT(!(evt.events & (EPOLLERR | EPOLLHUP)), +			    "Reader thread received EPOLLERR or EPOLLHUP on uffd.");  		r = read(uffd, &msg, sizeof(msg));  		if (r == -1) { -			if (errno == EAGAIN) -				continue; -			pr_info("Read of uffd got errno %d\n", errno); -			return NULL; +			TEST_ASSERT(errno == EAGAIN, +				    "Error reading from UFFD: errno = %d", errno); +			continue;  		} -		if (r != sizeof(msg)) { -			pr_info("Read on uffd returned unexpected size: %d bytes", r); -			return NULL; -		} +		TEST_ASSERT(r == sizeof(msg), +			    "Read on uffd returned unexpected number of bytes (%d)", r);  		if (!(msg.event & UFFD_EVENT_PAGEFAULT))  			continue; -		if (delay) -			usleep(delay); -		r = uffd_desc->handler(uffd_desc->uffd_mode, uffd, &msg); -		if (r < 0) -			return NULL; +		if (reader_args->delay) +			usleep(reader_args->delay); +		r = reader_args->handler(reader_args->uffd_mode, uffd, &msg); +		TEST_ASSERT(r >= 0, +			    "Reader thread handler fn returned negative value %d", r);  		pages++;  	} @@ -110,6 +101,7 @@ static void *uffd_handler_thread_fn(void *arg)  struct uffd_desc *uffd_setup_demand_paging(int uffd_mode, useconds_t delay,  					   void *hva, uint64_t len, +					   uint64_t num_readers,  					   uffd_handler_t handler)  {  	struct uffd_desc *uffd_desc; @@ -118,14 +110,25 @@ struct uffd_desc *uffd_setup_demand_paging(int uffd_mode, useconds_t delay,  	struct uffdio_api uffdio_api;  	struct uffdio_register uffdio_register;  	uint64_t expected_ioctls = ((uint64_t) 1) << _UFFDIO_COPY; -	int ret; +	int ret, i;  	PER_PAGE_DEBUG("Userfaultfd %s mode, faults resolved with %s\n",  		       is_minor ? "MINOR" : "MISSING",  		       is_minor ? "UFFDIO_CONINUE" : "UFFDIO_COPY");  	uffd_desc = malloc(sizeof(struct uffd_desc)); -	TEST_ASSERT(uffd_desc, "malloc failed"); +	TEST_ASSERT(uffd_desc, "Failed to malloc uffd descriptor"); + +	uffd_desc->pipefds = calloc(sizeof(int), num_readers); +	TEST_ASSERT(uffd_desc->pipefds, "Failed to alloc pipes"); + +	uffd_desc->readers = calloc(sizeof(pthread_t), num_readers); +	TEST_ASSERT(uffd_desc->readers, "Failed to alloc reader threads"); + +	uffd_desc->reader_args = calloc(sizeof(struct uffd_reader_args), num_readers); +	TEST_ASSERT(uffd_desc->reader_args, "Failed to alloc reader_args"); + +	uffd_desc->num_readers = num_readers;  	/* In order to get minor faults, prefault via the alias. */  	if (is_minor) @@ -148,18 +151,28 @@ struct uffd_desc *uffd_setup_demand_paging(int uffd_mode, useconds_t delay,  	TEST_ASSERT((uffdio_register.ioctls & expected_ioctls) ==  		    expected_ioctls, "missing userfaultfd ioctls"); -	ret = pipe2(uffd_desc->pipefds, O_CLOEXEC | O_NONBLOCK); -	TEST_ASSERT(!ret, "Failed to set up pipefd"); - -	uffd_desc->uffd_mode = uffd_mode;  	uffd_desc->uffd = uffd; -	uffd_desc->delay = delay; -	uffd_desc->handler = handler; -	pthread_create(&uffd_desc->thread, NULL, uffd_handler_thread_fn, -		       uffd_desc); +	for (i = 0; i < uffd_desc->num_readers; ++i) { +		int pipes[2]; -	PER_VCPU_DEBUG("Created uffd thread for HVA range [%p, %p)\n", -		       hva, hva + len); +		ret = pipe2((int *) &pipes, O_CLOEXEC | O_NONBLOCK); +		TEST_ASSERT(!ret, "Failed to set up pipefd %i for uffd_desc %p", +			    i, uffd_desc); + +		uffd_desc->pipefds[i] = pipes[1]; + +		uffd_desc->reader_args[i].uffd_mode = uffd_mode; +		uffd_desc->reader_args[i].uffd = uffd; +		uffd_desc->reader_args[i].delay = delay; +		uffd_desc->reader_args[i].handler = handler; +		uffd_desc->reader_args[i].pipe = pipes[0]; + +		pthread_create(&uffd_desc->readers[i], NULL, uffd_handler_thread_fn, +			       &uffd_desc->reader_args[i]); + +		PER_VCPU_DEBUG("Created uffd thread %i for HVA range [%p, %p)\n", +			       i, hva, hva + len); +	}  	return uffd_desc;  } @@ -167,19 +180,26 @@ struct uffd_desc *uffd_setup_demand_paging(int uffd_mode, useconds_t delay,  void uffd_stop_demand_paging(struct uffd_desc *uffd)  {  	char c = 0; -	int ret; +	int i; -	ret = write(uffd->pipefds[1], &c, 1); -	TEST_ASSERT(ret == 1, "Unable to write to pipefd"); +	for (i = 0; i < uffd->num_readers; ++i) +		TEST_ASSERT(write(uffd->pipefds[i], &c, 1) == 1, +			    "Unable to write to pipefd %i for uffd_desc %p", i, uffd); -	ret = pthread_join(uffd->thread, NULL); -	TEST_ASSERT(ret == 0, "Pthread_join failed."); +	for (i = 0; i < uffd->num_readers; ++i) +		TEST_ASSERT(!pthread_join(uffd->readers[i], NULL), +			    "Pthread_join failed on reader %i for uffd_desc %p", i, uffd);  	close(uffd->uffd); -	close(uffd->pipefds[1]); -	close(uffd->pipefds[0]); +	for (i = 0; i < uffd->num_readers; ++i) { +		close(uffd->pipefds[i]); +		close(uffd->reader_args[i].pipe); +	} +	free(uffd->pipefds); +	free(uffd->readers); +	free(uffd->reader_args);  	free(uffd);  }  | 
