xref: /kvmtool/ioeventfd.c (revision d30d94872e7fe998bd527e5cc6c38db58d46ee03)
137f3d50eSSasha Levin #include <sys/epoll.h>
237f3d50eSSasha Levin #include <sys/ioctl.h>
337f3d50eSSasha Levin #include <pthread.h>
437f3d50eSSasha Levin #include <unistd.h>
537f3d50eSSasha Levin #include <stdio.h>
637f3d50eSSasha Levin #include <signal.h>
737f3d50eSSasha Levin 
837f3d50eSSasha Levin #include <linux/kernel.h>
937f3d50eSSasha Levin #include <linux/kvm.h>
1037f3d50eSSasha Levin #include <linux/types.h>
1137f3d50eSSasha Levin 
12*d30d9487SJean-Philippe Brucker #include "kvm/epoll.h"
1337f3d50eSSasha Levin #include "kvm/ioeventfd.h"
1437f3d50eSSasha Levin #include "kvm/kvm.h"
1537f3d50eSSasha Levin #include "kvm/util.h"
1637f3d50eSSasha Levin 
1737f3d50eSSasha Levin #define IOEVENTFD_MAX_EVENTS	20
1837f3d50eSSasha Levin 
1937f3d50eSSasha Levin static LIST_HEAD(used_ioevents);
20e1337781SSasha Levin static bool	ioeventfd_avail;
21*d30d9487SJean-Philippe Brucker static struct kvm__epoll epoll;
2237f3d50eSSasha Levin 
ioeventfd__handle_event(struct kvm * kvm,struct epoll_event * ev)23*d30d9487SJean-Philippe Brucker static void ioeventfd__handle_event(struct kvm *kvm, struct epoll_event *ev)
2437f3d50eSSasha Levin {
25*d30d9487SJean-Philippe Brucker 	u64 tmp;
26*d30d9487SJean-Philippe Brucker 	struct ioevent *ioevent = ev->data.ptr;
27ea6eeb1cSSasha Levin 
28ea6eeb1cSSasha Levin 	if (read(ioevent->fd, &tmp, sizeof(tmp)) < 0)
29ea6eeb1cSSasha Levin 		die("Failed reading event");
30ea6eeb1cSSasha Levin 
31ea6eeb1cSSasha Levin 	ioevent->fn(ioevent->fn_kvm, ioevent->fn_ptr);
32ea6eeb1cSSasha Levin }
33ea6eeb1cSSasha Levin 
ioeventfd__init(struct kvm * kvm)34ea6eeb1cSSasha Levin int ioeventfd__init(struct kvm *kvm)
35ea6eeb1cSSasha Levin {
361d6fb3f2SSasha Levin 	ioeventfd_avail = kvm__supports_extension(kvm, KVM_CAP_IOEVENTFD);
37e1337781SSasha Levin 	if (!ioeventfd_avail)
3885dde7b2SMatt Evans 		return 1; /* Not fatal, but let caller determine no-go. */
39e1337781SSasha Levin 
40*d30d9487SJean-Philippe Brucker 	return epoll__init(kvm, &epoll, "ioeventfd-worker",
41*d30d9487SJean-Philippe Brucker 			   ioeventfd__handle_event);
4237f3d50eSSasha Levin }
4349a8afd1SSasha Levin base_init(ioeventfd__init);
4437f3d50eSSasha Levin 
ioeventfd__exit(struct kvm * kvm)45ea6eeb1cSSasha Levin int ioeventfd__exit(struct kvm *kvm)
46ea6eeb1cSSasha Levin {
4755628a95SMichael Ellerman 	if (!ioeventfd_avail)
4855628a95SMichael Ellerman 		return 0;
4955628a95SMichael Ellerman 
50*d30d9487SJean-Philippe Brucker 	epoll__exit(&epoll);
51ea6eeb1cSSasha Levin 	return 0;
52ea6eeb1cSSasha Levin }
5349a8afd1SSasha Levin base_exit(ioeventfd__exit);
54ea6eeb1cSSasha Levin 
ioeventfd__add_event(struct ioevent * ioevent,int flags)5527347f76SWill Deacon int ioeventfd__add_event(struct ioevent *ioevent, int flags)
5637f3d50eSSasha Levin {
5737f3d50eSSasha Levin 	struct kvm_ioeventfd kvm_ioevent;
5837f3d50eSSasha Levin 	struct epoll_event epoll_event;
5937f3d50eSSasha Levin 	struct ioevent *new_ioevent;
60ea6eeb1cSSasha Levin 	int event, r;
6137f3d50eSSasha Levin 
62e1337781SSasha Levin 	if (!ioeventfd_avail)
63ea6eeb1cSSasha Levin 		return -ENOSYS;
64e1337781SSasha Levin 
6537f3d50eSSasha Levin 	new_ioevent = malloc(sizeof(*new_ioevent));
6637f3d50eSSasha Levin 	if (new_ioevent == NULL)
67ea6eeb1cSSasha Levin 		return -ENOMEM;
6837f3d50eSSasha Levin 
6937f3d50eSSasha Levin 	*new_ioevent = *ioevent;
7037f3d50eSSasha Levin 	event = new_ioevent->fd;
7137f3d50eSSasha Levin 
7237f3d50eSSasha Levin 	kvm_ioevent = (struct kvm_ioeventfd) {
7337f3d50eSSasha Levin 		.addr		= ioevent->io_addr,
7437f3d50eSSasha Levin 		.len		= ioevent->io_len,
7537f3d50eSSasha Levin 		.datamatch	= ioevent->datamatch,
7637f3d50eSSasha Levin 		.fd		= event,
779ff91339SAsias He 		.flags		= KVM_IOEVENTFD_FLAG_DATAMATCH,
7837f3d50eSSasha Levin 	};
7937f3d50eSSasha Levin 
80ed83730fSJean-Philippe Brucker 	/*
81ed83730fSJean-Philippe Brucker 	 * For architectures that don't recognize PIO accesses, always register
82ed83730fSJean-Philippe Brucker 	 * on the MMIO bus. Otherwise PIO accesses will cause returns to
83ed83730fSJean-Philippe Brucker 	 * userspace.
84ed83730fSJean-Philippe Brucker 	 */
85ed83730fSJean-Philippe Brucker 	if (KVM_IOEVENTFD_HAS_PIO && flags & IOEVENTFD_FLAG_PIO)
869ff91339SAsias He 		kvm_ioevent.flags |= KVM_IOEVENTFD_FLAG_PIO;
879ff91339SAsias He 
88ea6eeb1cSSasha Levin 	r = ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
89ea6eeb1cSSasha Levin 	if (r) {
90ea6eeb1cSSasha Levin 		r = -errno;
91ea6eeb1cSSasha Levin 		goto cleanup;
92ea6eeb1cSSasha Levin 	}
9337f3d50eSSasha Levin 
945e9dd852SJean-Philippe Brucker 	if (flags & IOEVENTFD_FLAG_USER_POLL) {
9537f3d50eSSasha Levin 		epoll_event = (struct epoll_event) {
9637f3d50eSSasha Levin 			.events		= EPOLLIN,
9737f3d50eSSasha Levin 			.data.ptr	= new_ioevent,
9837f3d50eSSasha Levin 		};
9937f3d50eSSasha Levin 
100*d30d9487SJean-Philippe Brucker 		r = epoll_ctl(epoll.fd, EPOLL_CTL_ADD, event, &epoll_event);
101ea6eeb1cSSasha Levin 		if (r) {
102ea6eeb1cSSasha Levin 			r = -errno;
103ea6eeb1cSSasha Levin 			goto cleanup;
10437f3d50eSSasha Levin 		}
1055e9dd852SJean-Philippe Brucker 	}
10637f3d50eSSasha Levin 
10756c82a03SJean-Philippe Brucker 	new_ioevent->flags = kvm_ioevent.flags;
108ea6eeb1cSSasha Levin 	list_add_tail(&new_ioevent->list, &used_ioevents);
109ea6eeb1cSSasha Levin 
110ea6eeb1cSSasha Levin 	return 0;
111ea6eeb1cSSasha Levin 
112ea6eeb1cSSasha Levin cleanup:
113ea6eeb1cSSasha Levin 	free(new_ioevent);
114ea6eeb1cSSasha Levin 	return r;
115ea6eeb1cSSasha Levin }
116ea6eeb1cSSasha Levin 
ioeventfd__del_event(u64 addr,u64 datamatch)117ea6eeb1cSSasha Levin int ioeventfd__del_event(u64 addr, u64 datamatch)
11837f3d50eSSasha Levin {
11937f3d50eSSasha Levin 	struct kvm_ioeventfd kvm_ioevent;
12037f3d50eSSasha Levin 	struct ioevent *ioevent;
12137f3d50eSSasha Levin 	u8 found = 0;
12237f3d50eSSasha Levin 
123e1337781SSasha Levin 	if (!ioeventfd_avail)
124ea6eeb1cSSasha Levin 		return -ENOSYS;
125e1337781SSasha Levin 
12637f3d50eSSasha Levin 	list_for_each_entry(ioevent, &used_ioevents, list) {
12756c82a03SJean-Philippe Brucker 		if (ioevent->io_addr == addr &&
12856c82a03SJean-Philippe Brucker 		    ioevent->datamatch == datamatch) {
12937f3d50eSSasha Levin 			found = 1;
13037f3d50eSSasha Levin 			break;
13137f3d50eSSasha Levin 		}
13237f3d50eSSasha Levin 	}
13337f3d50eSSasha Levin 
13437f3d50eSSasha Levin 	if (found == 0 || ioevent == NULL)
135ea6eeb1cSSasha Levin 		return -ENOENT;
13637f3d50eSSasha Levin 
13737f3d50eSSasha Levin 	kvm_ioevent = (struct kvm_ioeventfd) {
13856c82a03SJean-Philippe Brucker 		.fd			= ioevent->fd,
13937f3d50eSSasha Levin 		.addr			= ioevent->io_addr,
14037f3d50eSSasha Levin 		.len			= ioevent->io_len,
14137f3d50eSSasha Levin 		.datamatch		= ioevent->datamatch,
142ed83730fSJean-Philippe Brucker 		.flags			= ioevent->flags
143ed83730fSJean-Philippe Brucker 					| KVM_IOEVENTFD_FLAG_DEASSIGN,
14437f3d50eSSasha Levin 	};
14537f3d50eSSasha Levin 
14637f3d50eSSasha Levin 	ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
14737f3d50eSSasha Levin 
148*d30d9487SJean-Philippe Brucker 	epoll_ctl(epoll.fd, EPOLL_CTL_DEL, ioevent->fd, NULL);
14937f3d50eSSasha Levin 
15037f3d50eSSasha Levin 	list_del(&ioevent->list);
15137f3d50eSSasha Levin 
15237f3d50eSSasha Levin 	close(ioevent->fd);
15337f3d50eSSasha Levin 	free(ioevent);
15437f3d50eSSasha Levin 
155ea6eeb1cSSasha Levin 	return 0;
15637f3d50eSSasha Levin }
157