xref: /kvmtool/ioeventfd.c (revision 5e9dd8524f7f7960e80125787e16f3ec8f258ddf)
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 
14827347f76SWill Deacon 	if (flags & IOEVENTFD_FLAG_PIO)
1499ff91339SAsias He 		kvm_ioevent.flags |= KVM_IOEVENTFD_FLAG_PIO;
1509ff91339SAsias He 
151ea6eeb1cSSasha Levin 	r = ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
152ea6eeb1cSSasha Levin 	if (r) {
153ea6eeb1cSSasha Levin 		r = -errno;
154ea6eeb1cSSasha Levin 		goto cleanup;
155ea6eeb1cSSasha Levin 	}
15637f3d50eSSasha Levin 
157*5e9dd852SJean-Philippe Brucker 	if (flags & IOEVENTFD_FLAG_USER_POLL) {
15837f3d50eSSasha Levin 		epoll_event = (struct epoll_event) {
15937f3d50eSSasha Levin 			.events		= EPOLLIN,
16037f3d50eSSasha Levin 			.data.ptr	= new_ioevent,
16137f3d50eSSasha Levin 		};
16237f3d50eSSasha Levin 
163ea6eeb1cSSasha Levin 		r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, event, &epoll_event);
164ea6eeb1cSSasha Levin 		if (r) {
165ea6eeb1cSSasha Levin 			r = -errno;
166ea6eeb1cSSasha Levin 			goto cleanup;
16737f3d50eSSasha Levin 		}
168*5e9dd852SJean-Philippe Brucker 	}
16937f3d50eSSasha Levin 
170ea6eeb1cSSasha Levin 	list_add_tail(&new_ioevent->list, &used_ioevents);
171ea6eeb1cSSasha Levin 
172ea6eeb1cSSasha Levin 	return 0;
173ea6eeb1cSSasha Levin 
174ea6eeb1cSSasha Levin cleanup:
175ea6eeb1cSSasha Levin 	free(new_ioevent);
176ea6eeb1cSSasha Levin 	return r;
177ea6eeb1cSSasha Levin }
178ea6eeb1cSSasha Levin 
179ea6eeb1cSSasha Levin int ioeventfd__del_event(u64 addr, u64 datamatch)
18037f3d50eSSasha Levin {
18137f3d50eSSasha Levin 	struct kvm_ioeventfd kvm_ioevent;
18237f3d50eSSasha Levin 	struct ioevent *ioevent;
18337f3d50eSSasha Levin 	u8 found = 0;
18437f3d50eSSasha Levin 
185e1337781SSasha Levin 	if (!ioeventfd_avail)
186ea6eeb1cSSasha Levin 		return -ENOSYS;
187e1337781SSasha Levin 
18837f3d50eSSasha Levin 	list_for_each_entry(ioevent, &used_ioevents, list) {
18937f3d50eSSasha Levin 		if (ioevent->io_addr == addr) {
19037f3d50eSSasha Levin 			found = 1;
19137f3d50eSSasha Levin 			break;
19237f3d50eSSasha Levin 		}
19337f3d50eSSasha Levin 	}
19437f3d50eSSasha Levin 
19537f3d50eSSasha Levin 	if (found == 0 || ioevent == NULL)
196ea6eeb1cSSasha Levin 		return -ENOENT;
19737f3d50eSSasha Levin 
19837f3d50eSSasha Levin 	kvm_ioevent = (struct kvm_ioeventfd) {
19937f3d50eSSasha Levin 		.addr			= ioevent->io_addr,
20037f3d50eSSasha Levin 		.len			= ioevent->io_len,
20137f3d50eSSasha Levin 		.datamatch		= ioevent->datamatch,
20237f3d50eSSasha Levin 		.flags			= KVM_IOEVENTFD_FLAG_PIO
20337f3d50eSSasha Levin 					| KVM_IOEVENTFD_FLAG_DEASSIGN
20437f3d50eSSasha Levin 					| KVM_IOEVENTFD_FLAG_DATAMATCH,
20537f3d50eSSasha Levin 	};
20637f3d50eSSasha Levin 
20737f3d50eSSasha Levin 	ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
20837f3d50eSSasha Levin 
20937f3d50eSSasha Levin 	epoll_ctl(epoll_fd, EPOLL_CTL_DEL, ioevent->fd, NULL);
21037f3d50eSSasha Levin 
21137f3d50eSSasha Levin 	list_del(&ioevent->list);
21237f3d50eSSasha Levin 
21337f3d50eSSasha Levin 	close(ioevent->fd);
21437f3d50eSSasha Levin 	free(ioevent);
21537f3d50eSSasha Levin 
216ea6eeb1cSSasha Levin 	return 0;
21737f3d50eSSasha Levin }
218