xref: /kvmtool/ioeventfd.c (revision 49a8afd1b9a4e503bdafb2bbc04549e03d514836)
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 
27ea6eeb1cSSasha Levin 	for (;;) {
28ea6eeb1cSSasha Levin 		int nfds, i;
29ea6eeb1cSSasha Levin 
30ea6eeb1cSSasha Levin 		nfds = epoll_wait(epoll_fd, events, IOEVENTFD_MAX_EVENTS, -1);
31ea6eeb1cSSasha Levin 		for (i = 0; i < nfds; i++) {
32ea6eeb1cSSasha Levin 			struct ioevent *ioevent;
33ea6eeb1cSSasha Levin 
34ea6eeb1cSSasha Levin 			if (events[i].data.fd == epoll_stop_fd)
35ea6eeb1cSSasha Levin 				goto done;
36ea6eeb1cSSasha Levin 
37ea6eeb1cSSasha Levin 			ioevent = events[i].data.ptr;
38ea6eeb1cSSasha Levin 
39ea6eeb1cSSasha Levin 			if (read(ioevent->fd, &tmp, sizeof(tmp)) < 0)
40ea6eeb1cSSasha Levin 				die("Failed reading event");
41ea6eeb1cSSasha Levin 
42ea6eeb1cSSasha Levin 			ioevent->fn(ioevent->fn_kvm, ioevent->fn_ptr);
43ea6eeb1cSSasha Levin 		}
44ea6eeb1cSSasha Levin 	}
45ea6eeb1cSSasha Levin 
46ea6eeb1cSSasha Levin done:
47ea6eeb1cSSasha Levin 	tmp = write(epoll_stop_fd, &tmp, sizeof(tmp));
48ea6eeb1cSSasha Levin 
49ea6eeb1cSSasha Levin 	return NULL;
50ea6eeb1cSSasha Levin }
51ea6eeb1cSSasha Levin 
52ea6eeb1cSSasha Levin static int ioeventfd__start(void)
53ea6eeb1cSSasha Levin {
54ea6eeb1cSSasha Levin 	pthread_t thread;
55ea6eeb1cSSasha Levin 
56ea6eeb1cSSasha Levin 	if (!ioeventfd_avail)
57ea6eeb1cSSasha Levin 		return -ENOSYS;
58ea6eeb1cSSasha Levin 
59ea6eeb1cSSasha Levin 	return pthread_create(&thread, NULL, ioeventfd__thread, NULL);
60ea6eeb1cSSasha Levin }
61ea6eeb1cSSasha Levin 
62ea6eeb1cSSasha Levin int ioeventfd__init(struct kvm *kvm)
63ea6eeb1cSSasha Levin {
64ea6eeb1cSSasha Levin 	struct epoll_event epoll_event = {.events = EPOLLIN};
65ea6eeb1cSSasha Levin 	int r;
66ea6eeb1cSSasha Levin 
671d6fb3f2SSasha Levin 	ioeventfd_avail = kvm__supports_extension(kvm, KVM_CAP_IOEVENTFD);
68e1337781SSasha Levin 	if (!ioeventfd_avail)
6985dde7b2SMatt Evans 		return 1; /* Not fatal, but let caller determine no-go. */
70e1337781SSasha Levin 
7137f3d50eSSasha Levin 	epoll_fd = epoll_create(IOEVENTFD_MAX_EVENTS);
7237f3d50eSSasha Levin 	if (epoll_fd < 0)
73ea6eeb1cSSasha Levin 		return -errno;
74ea6eeb1cSSasha Levin 
75ea6eeb1cSSasha Levin 	epoll_stop_fd = eventfd(0, 0);
76ea6eeb1cSSasha Levin 	epoll_event.data.fd = epoll_stop_fd;
77ea6eeb1cSSasha Levin 
78ea6eeb1cSSasha Levin 	r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, epoll_stop_fd, &epoll_event);
79ea6eeb1cSSasha Levin 	if (r < 0)
80ea6eeb1cSSasha Levin 		goto cleanup;
81ea6eeb1cSSasha Levin 
82ea6eeb1cSSasha Levin 	r = ioeventfd__start();
83ea6eeb1cSSasha Levin 	if (r < 0)
84ea6eeb1cSSasha Levin 		goto cleanup;
85ea6eeb1cSSasha Levin 
86ea6eeb1cSSasha Levin 	r = 0;
87ea6eeb1cSSasha Levin 
88ea6eeb1cSSasha Levin 	return r;
89ea6eeb1cSSasha Levin 
90ea6eeb1cSSasha Levin cleanup:
91ea6eeb1cSSasha Levin 	close(epoll_stop_fd);
92ea6eeb1cSSasha Levin 	close(epoll_fd);
93ea6eeb1cSSasha Levin 
94ea6eeb1cSSasha Levin 	return r;
9537f3d50eSSasha Levin }
96*49a8afd1SSasha Levin base_init(ioeventfd__init);
9737f3d50eSSasha Levin 
98ea6eeb1cSSasha Levin int ioeventfd__exit(struct kvm *kvm)
99ea6eeb1cSSasha Levin {
100ea6eeb1cSSasha Levin 	u64 tmp = 1;
101ea6eeb1cSSasha Levin 	int r;
102ea6eeb1cSSasha Levin 
10355628a95SMichael Ellerman 	if (!ioeventfd_avail)
10455628a95SMichael Ellerman 		return 0;
10555628a95SMichael Ellerman 
106ea6eeb1cSSasha Levin 	r = write(epoll_stop_fd, &tmp, sizeof(tmp));
107ea6eeb1cSSasha Levin 	if (r < 0)
108ea6eeb1cSSasha Levin 		return r;
109ea6eeb1cSSasha Levin 
110ea6eeb1cSSasha Levin 	r = read(epoll_stop_fd, &tmp, sizeof(tmp));
111ea6eeb1cSSasha Levin 	if (r < 0)
112ea6eeb1cSSasha Levin 		return r;
113ea6eeb1cSSasha Levin 
114ea6eeb1cSSasha Levin 	close(epoll_fd);
115ea6eeb1cSSasha Levin 	close(epoll_stop_fd);
116ea6eeb1cSSasha Levin 
117ea6eeb1cSSasha Levin 	return 0;
118ea6eeb1cSSasha Levin }
119*49a8afd1SSasha Levin base_exit(ioeventfd__exit);
120ea6eeb1cSSasha Levin 
121627d6874SAsias He int ioeventfd__add_event(struct ioevent *ioevent, bool is_pio, bool poll_in_userspace)
12237f3d50eSSasha Levin {
12337f3d50eSSasha Levin 	struct kvm_ioeventfd kvm_ioevent;
12437f3d50eSSasha Levin 	struct epoll_event epoll_event;
12537f3d50eSSasha Levin 	struct ioevent *new_ioevent;
126ea6eeb1cSSasha Levin 	int event, r;
12737f3d50eSSasha Levin 
128e1337781SSasha Levin 	if (!ioeventfd_avail)
129ea6eeb1cSSasha Levin 		return -ENOSYS;
130e1337781SSasha Levin 
13137f3d50eSSasha Levin 	new_ioevent = malloc(sizeof(*new_ioevent));
13237f3d50eSSasha Levin 	if (new_ioevent == NULL)
133ea6eeb1cSSasha Levin 		return -ENOMEM;
13437f3d50eSSasha Levin 
13537f3d50eSSasha Levin 	*new_ioevent = *ioevent;
13637f3d50eSSasha Levin 	event = new_ioevent->fd;
13737f3d50eSSasha Levin 
13837f3d50eSSasha Levin 	kvm_ioevent = (struct kvm_ioeventfd) {
13937f3d50eSSasha Levin 		.addr		= ioevent->io_addr,
14037f3d50eSSasha Levin 		.len		= ioevent->io_len,
14137f3d50eSSasha Levin 		.datamatch	= ioevent->datamatch,
14237f3d50eSSasha Levin 		.fd		= event,
1439ff91339SAsias He 		.flags		= KVM_IOEVENTFD_FLAG_DATAMATCH,
14437f3d50eSSasha Levin 	};
14537f3d50eSSasha Levin 
1469ff91339SAsias He 	if (is_pio)
1479ff91339SAsias He 		kvm_ioevent.flags |= KVM_IOEVENTFD_FLAG_PIO;
1489ff91339SAsias He 
149ea6eeb1cSSasha Levin 	r = ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
150ea6eeb1cSSasha Levin 	if (r) {
151ea6eeb1cSSasha Levin 		r = -errno;
152ea6eeb1cSSasha Levin 		goto cleanup;
153ea6eeb1cSSasha Levin 	}
15437f3d50eSSasha Levin 
155627d6874SAsias He 	if (!poll_in_userspace)
156627d6874SAsias He 		return 0;
157627d6874SAsias He 
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 	}
16837f3d50eSSasha Levin 
169ea6eeb1cSSasha Levin 	list_add_tail(&new_ioevent->list, &used_ioevents);
170ea6eeb1cSSasha Levin 
171ea6eeb1cSSasha Levin 	return 0;
172ea6eeb1cSSasha Levin 
173ea6eeb1cSSasha Levin cleanup:
174ea6eeb1cSSasha Levin 	free(new_ioevent);
175ea6eeb1cSSasha Levin 	return r;
176ea6eeb1cSSasha Levin }
177ea6eeb1cSSasha Levin 
178ea6eeb1cSSasha Levin int ioeventfd__del_event(u64 addr, u64 datamatch)
17937f3d50eSSasha Levin {
18037f3d50eSSasha Levin 	struct kvm_ioeventfd kvm_ioevent;
18137f3d50eSSasha Levin 	struct ioevent *ioevent;
18237f3d50eSSasha Levin 	u8 found = 0;
18337f3d50eSSasha Levin 
184e1337781SSasha Levin 	if (!ioeventfd_avail)
185ea6eeb1cSSasha Levin 		return -ENOSYS;
186e1337781SSasha Levin 
18737f3d50eSSasha Levin 	list_for_each_entry(ioevent, &used_ioevents, list) {
18837f3d50eSSasha Levin 		if (ioevent->io_addr == addr) {
18937f3d50eSSasha Levin 			found = 1;
19037f3d50eSSasha Levin 			break;
19137f3d50eSSasha Levin 		}
19237f3d50eSSasha Levin 	}
19337f3d50eSSasha Levin 
19437f3d50eSSasha Levin 	if (found == 0 || ioevent == NULL)
195ea6eeb1cSSasha Levin 		return -ENOENT;
19637f3d50eSSasha Levin 
19737f3d50eSSasha Levin 	kvm_ioevent = (struct kvm_ioeventfd) {
19837f3d50eSSasha Levin 		.addr			= ioevent->io_addr,
19937f3d50eSSasha Levin 		.len			= ioevent->io_len,
20037f3d50eSSasha Levin 		.datamatch		= ioevent->datamatch,
20137f3d50eSSasha Levin 		.flags			= KVM_IOEVENTFD_FLAG_PIO
20237f3d50eSSasha Levin 					| KVM_IOEVENTFD_FLAG_DEASSIGN
20337f3d50eSSasha Levin 					| KVM_IOEVENTFD_FLAG_DATAMATCH,
20437f3d50eSSasha Levin 	};
20537f3d50eSSasha Levin 
20637f3d50eSSasha Levin 	ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
20737f3d50eSSasha Levin 
20837f3d50eSSasha Levin 	epoll_ctl(epoll_fd, EPOLL_CTL_DEL, ioevent->fd, NULL);
20937f3d50eSSasha Levin 
21037f3d50eSSasha Levin 	list_del(&ioevent->list);
21137f3d50eSSasha Levin 
21237f3d50eSSasha Levin 	close(ioevent->fd);
21337f3d50eSSasha Levin 	free(ioevent);
21437f3d50eSSasha Levin 
215ea6eeb1cSSasha Levin 	return 0;
21637f3d50eSSasha Levin }
217