xref: /kvmtool/ioeventfd.c (revision 56c82a0305d7f889a05c71fd11bff4c6609c5199)
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 
1237f3d50eSSasha Levin #include "kvm/ioeventfd.h"
1337f3d50eSSasha Levin #include "kvm/kvm.h"
1437f3d50eSSasha Levin #include "kvm/util.h"
1537f3d50eSSasha Levin 
1637f3d50eSSasha Levin #define IOEVENTFD_MAX_EVENTS	20
1737f3d50eSSasha Levin 
1837f3d50eSSasha Levin static struct	epoll_event events[IOEVENTFD_MAX_EVENTS];
19ea6eeb1cSSasha Levin static int	epoll_fd, epoll_stop_fd;
2037f3d50eSSasha Levin static LIST_HEAD(used_ioevents);
21e1337781SSasha Levin static bool	ioeventfd_avail;
2237f3d50eSSasha Levin 
23ea6eeb1cSSasha Levin static void *ioeventfd__thread(void *param)
2437f3d50eSSasha Levin {
25ea6eeb1cSSasha Levin 	u64 tmp = 1;
26ea6eeb1cSSasha Levin 
27a4d8c55eSSasha Levin 	kvm__set_thread_name("ioeventfd-worker");
28a4d8c55eSSasha Levin 
29ea6eeb1cSSasha Levin 	for (;;) {
30ea6eeb1cSSasha Levin 		int nfds, i;
31ea6eeb1cSSasha Levin 
32ea6eeb1cSSasha Levin 		nfds = epoll_wait(epoll_fd, events, IOEVENTFD_MAX_EVENTS, -1);
33ea6eeb1cSSasha Levin 		for (i = 0; i < nfds; i++) {
34ea6eeb1cSSasha Levin 			struct ioevent *ioevent;
35ea6eeb1cSSasha Levin 
36ea6eeb1cSSasha Levin 			if (events[i].data.fd == epoll_stop_fd)
37ea6eeb1cSSasha Levin 				goto done;
38ea6eeb1cSSasha Levin 
39ea6eeb1cSSasha Levin 			ioevent = events[i].data.ptr;
40ea6eeb1cSSasha Levin 
41ea6eeb1cSSasha Levin 			if (read(ioevent->fd, &tmp, sizeof(tmp)) < 0)
42ea6eeb1cSSasha Levin 				die("Failed reading event");
43ea6eeb1cSSasha Levin 
44ea6eeb1cSSasha Levin 			ioevent->fn(ioevent->fn_kvm, ioevent->fn_ptr);
45ea6eeb1cSSasha Levin 		}
46ea6eeb1cSSasha Levin 	}
47ea6eeb1cSSasha Levin 
48ea6eeb1cSSasha Levin done:
49ea6eeb1cSSasha Levin 	tmp = write(epoll_stop_fd, &tmp, sizeof(tmp));
50ea6eeb1cSSasha Levin 
51ea6eeb1cSSasha Levin 	return NULL;
52ea6eeb1cSSasha Levin }
53ea6eeb1cSSasha Levin 
54ea6eeb1cSSasha Levin static int ioeventfd__start(void)
55ea6eeb1cSSasha Levin {
56ea6eeb1cSSasha Levin 	pthread_t thread;
57ea6eeb1cSSasha Levin 
58ea6eeb1cSSasha Levin 	if (!ioeventfd_avail)
59ea6eeb1cSSasha Levin 		return -ENOSYS;
60ea6eeb1cSSasha Levin 
61ea6eeb1cSSasha Levin 	return pthread_create(&thread, NULL, ioeventfd__thread, NULL);
62ea6eeb1cSSasha Levin }
63ea6eeb1cSSasha Levin 
64ea6eeb1cSSasha Levin int ioeventfd__init(struct kvm *kvm)
65ea6eeb1cSSasha Levin {
66ea6eeb1cSSasha Levin 	struct epoll_event epoll_event = {.events = EPOLLIN};
67ea6eeb1cSSasha Levin 	int r;
68ea6eeb1cSSasha Levin 
691d6fb3f2SSasha Levin 	ioeventfd_avail = kvm__supports_extension(kvm, KVM_CAP_IOEVENTFD);
70e1337781SSasha Levin 	if (!ioeventfd_avail)
7185dde7b2SMatt Evans 		return 1; /* Not fatal, but let caller determine no-go. */
72e1337781SSasha Levin 
7337f3d50eSSasha Levin 	epoll_fd = epoll_create(IOEVENTFD_MAX_EVENTS);
7437f3d50eSSasha Levin 	if (epoll_fd < 0)
75ea6eeb1cSSasha Levin 		return -errno;
76ea6eeb1cSSasha Levin 
77ea6eeb1cSSasha Levin 	epoll_stop_fd = eventfd(0, 0);
78ea6eeb1cSSasha Levin 	epoll_event.data.fd = epoll_stop_fd;
79ea6eeb1cSSasha Levin 
80ea6eeb1cSSasha Levin 	r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, epoll_stop_fd, &epoll_event);
81ea6eeb1cSSasha Levin 	if (r < 0)
82ea6eeb1cSSasha Levin 		goto cleanup;
83ea6eeb1cSSasha Levin 
84ea6eeb1cSSasha Levin 	r = ioeventfd__start();
85ea6eeb1cSSasha Levin 	if (r < 0)
86ea6eeb1cSSasha Levin 		goto cleanup;
87ea6eeb1cSSasha Levin 
88ea6eeb1cSSasha Levin 	r = 0;
89ea6eeb1cSSasha Levin 
90ea6eeb1cSSasha Levin 	return r;
91ea6eeb1cSSasha Levin 
92ea6eeb1cSSasha Levin cleanup:
93ea6eeb1cSSasha Levin 	close(epoll_stop_fd);
94ea6eeb1cSSasha Levin 	close(epoll_fd);
95ea6eeb1cSSasha Levin 
96ea6eeb1cSSasha Levin 	return r;
9737f3d50eSSasha Levin }
9849a8afd1SSasha Levin base_init(ioeventfd__init);
9937f3d50eSSasha Levin 
100ea6eeb1cSSasha Levin int ioeventfd__exit(struct kvm *kvm)
101ea6eeb1cSSasha Levin {
102ea6eeb1cSSasha Levin 	u64 tmp = 1;
103ea6eeb1cSSasha Levin 	int r;
104ea6eeb1cSSasha Levin 
10555628a95SMichael Ellerman 	if (!ioeventfd_avail)
10655628a95SMichael Ellerman 		return 0;
10755628a95SMichael Ellerman 
108ea6eeb1cSSasha Levin 	r = write(epoll_stop_fd, &tmp, sizeof(tmp));
109ea6eeb1cSSasha Levin 	if (r < 0)
110ea6eeb1cSSasha Levin 		return r;
111ea6eeb1cSSasha Levin 
112ea6eeb1cSSasha Levin 	r = read(epoll_stop_fd, &tmp, sizeof(tmp));
113ea6eeb1cSSasha Levin 	if (r < 0)
114ea6eeb1cSSasha Levin 		return r;
115ea6eeb1cSSasha Levin 
116ea6eeb1cSSasha Levin 	close(epoll_fd);
117ea6eeb1cSSasha Levin 	close(epoll_stop_fd);
118ea6eeb1cSSasha Levin 
119ea6eeb1cSSasha Levin 	return 0;
120ea6eeb1cSSasha Levin }
12149a8afd1SSasha Levin base_exit(ioeventfd__exit);
122ea6eeb1cSSasha Levin 
12327347f76SWill Deacon int ioeventfd__add_event(struct ioevent *ioevent, int flags)
12437f3d50eSSasha Levin {
12537f3d50eSSasha Levin 	struct kvm_ioeventfd kvm_ioevent;
12637f3d50eSSasha Levin 	struct epoll_event epoll_event;
12737f3d50eSSasha Levin 	struct ioevent *new_ioevent;
128ea6eeb1cSSasha Levin 	int event, r;
12937f3d50eSSasha Levin 
130e1337781SSasha Levin 	if (!ioeventfd_avail)
131ea6eeb1cSSasha Levin 		return -ENOSYS;
132e1337781SSasha Levin 
13337f3d50eSSasha Levin 	new_ioevent = malloc(sizeof(*new_ioevent));
13437f3d50eSSasha Levin 	if (new_ioevent == NULL)
135ea6eeb1cSSasha Levin 		return -ENOMEM;
13637f3d50eSSasha Levin 
13737f3d50eSSasha Levin 	*new_ioevent = *ioevent;
13837f3d50eSSasha Levin 	event = new_ioevent->fd;
13937f3d50eSSasha Levin 
14037f3d50eSSasha Levin 	kvm_ioevent = (struct kvm_ioeventfd) {
14137f3d50eSSasha Levin 		.addr		= ioevent->io_addr,
14237f3d50eSSasha Levin 		.len		= ioevent->io_len,
14337f3d50eSSasha Levin 		.datamatch	= ioevent->datamatch,
14437f3d50eSSasha Levin 		.fd		= event,
1459ff91339SAsias He 		.flags		= KVM_IOEVENTFD_FLAG_DATAMATCH,
14637f3d50eSSasha Levin 	};
14737f3d50eSSasha Levin 
148ed83730fSJean-Philippe Brucker 	/*
149ed83730fSJean-Philippe Brucker 	 * For architectures that don't recognize PIO accesses, always register
150ed83730fSJean-Philippe Brucker 	 * on the MMIO bus. Otherwise PIO accesses will cause returns to
151ed83730fSJean-Philippe Brucker 	 * userspace.
152ed83730fSJean-Philippe Brucker 	 */
153ed83730fSJean-Philippe Brucker 	if (KVM_IOEVENTFD_HAS_PIO && flags & IOEVENTFD_FLAG_PIO)
1549ff91339SAsias He 		kvm_ioevent.flags |= KVM_IOEVENTFD_FLAG_PIO;
1559ff91339SAsias He 
156ea6eeb1cSSasha Levin 	r = ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
157ea6eeb1cSSasha Levin 	if (r) {
158ea6eeb1cSSasha Levin 		r = -errno;
159ea6eeb1cSSasha Levin 		goto cleanup;
160ea6eeb1cSSasha Levin 	}
16137f3d50eSSasha Levin 
1625e9dd852SJean-Philippe Brucker 	if (flags & IOEVENTFD_FLAG_USER_POLL) {
16337f3d50eSSasha Levin 		epoll_event = (struct epoll_event) {
16437f3d50eSSasha Levin 			.events		= EPOLLIN,
16537f3d50eSSasha Levin 			.data.ptr	= new_ioevent,
16637f3d50eSSasha Levin 		};
16737f3d50eSSasha Levin 
168ea6eeb1cSSasha Levin 		r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, event, &epoll_event);
169ea6eeb1cSSasha Levin 		if (r) {
170ea6eeb1cSSasha Levin 			r = -errno;
171ea6eeb1cSSasha Levin 			goto cleanup;
17237f3d50eSSasha Levin 		}
1735e9dd852SJean-Philippe Brucker 	}
17437f3d50eSSasha Levin 
175*56c82a03SJean-Philippe Brucker 	new_ioevent->flags = kvm_ioevent.flags;
176ea6eeb1cSSasha Levin 	list_add_tail(&new_ioevent->list, &used_ioevents);
177ea6eeb1cSSasha Levin 
178ea6eeb1cSSasha Levin 	return 0;
179ea6eeb1cSSasha Levin 
180ea6eeb1cSSasha Levin cleanup:
181ea6eeb1cSSasha Levin 	free(new_ioevent);
182ea6eeb1cSSasha Levin 	return r;
183ea6eeb1cSSasha Levin }
184ea6eeb1cSSasha Levin 
185ea6eeb1cSSasha Levin int ioeventfd__del_event(u64 addr, u64 datamatch)
18637f3d50eSSasha Levin {
18737f3d50eSSasha Levin 	struct kvm_ioeventfd kvm_ioevent;
18837f3d50eSSasha Levin 	struct ioevent *ioevent;
18937f3d50eSSasha Levin 	u8 found = 0;
19037f3d50eSSasha Levin 
191e1337781SSasha Levin 	if (!ioeventfd_avail)
192ea6eeb1cSSasha Levin 		return -ENOSYS;
193e1337781SSasha Levin 
19437f3d50eSSasha Levin 	list_for_each_entry(ioevent, &used_ioevents, list) {
195*56c82a03SJean-Philippe Brucker 		if (ioevent->io_addr == addr &&
196*56c82a03SJean-Philippe Brucker 		    ioevent->datamatch == datamatch) {
19737f3d50eSSasha Levin 			found = 1;
19837f3d50eSSasha Levin 			break;
19937f3d50eSSasha Levin 		}
20037f3d50eSSasha Levin 	}
20137f3d50eSSasha Levin 
20237f3d50eSSasha Levin 	if (found == 0 || ioevent == NULL)
203ea6eeb1cSSasha Levin 		return -ENOENT;
20437f3d50eSSasha Levin 
20537f3d50eSSasha Levin 	kvm_ioevent = (struct kvm_ioeventfd) {
206*56c82a03SJean-Philippe Brucker 		.fd			= ioevent->fd,
20737f3d50eSSasha Levin 		.addr			= ioevent->io_addr,
20837f3d50eSSasha Levin 		.len			= ioevent->io_len,
20937f3d50eSSasha Levin 		.datamatch		= ioevent->datamatch,
210ed83730fSJean-Philippe Brucker 		.flags			= ioevent->flags
211ed83730fSJean-Philippe Brucker 					| KVM_IOEVENTFD_FLAG_DEASSIGN,
21237f3d50eSSasha Levin 	};
21337f3d50eSSasha Levin 
21437f3d50eSSasha Levin 	ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
21537f3d50eSSasha Levin 
21637f3d50eSSasha Levin 	epoll_ctl(epoll_fd, EPOLL_CTL_DEL, ioevent->fd, NULL);
21737f3d50eSSasha Levin 
21837f3d50eSSasha Levin 	list_del(&ioevent->list);
21937f3d50eSSasha Levin 
22037f3d50eSSasha Levin 	close(ioevent->fd);
22137f3d50eSSasha Levin 	free(ioevent);
22237f3d50eSSasha Levin 
223ea6eeb1cSSasha Levin 	return 0;
22437f3d50eSSasha Levin }
225