xref: /kvmtool/ioeventfd.c (revision 55628a95bdf6c2c15de3f8fae5b5663eff46926a)
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 = 1;
48ea6eeb1cSSasha Levin 	tmp = write(epoll_stop_fd, &tmp, sizeof(tmp));
49ea6eeb1cSSasha Levin 
50ea6eeb1cSSasha Levin 	return NULL;
51ea6eeb1cSSasha Levin }
52ea6eeb1cSSasha Levin 
53ea6eeb1cSSasha Levin static int ioeventfd__start(void)
54ea6eeb1cSSasha Levin {
55ea6eeb1cSSasha Levin 	pthread_t thread;
56ea6eeb1cSSasha Levin 
57ea6eeb1cSSasha Levin 	if (!ioeventfd_avail)
58ea6eeb1cSSasha Levin 		return -ENOSYS;
59ea6eeb1cSSasha Levin 
60ea6eeb1cSSasha Levin 	return pthread_create(&thread, NULL, ioeventfd__thread, NULL);
61ea6eeb1cSSasha Levin }
62ea6eeb1cSSasha Levin 
63ea6eeb1cSSasha Levin int ioeventfd__init(struct kvm *kvm)
64ea6eeb1cSSasha Levin {
65ea6eeb1cSSasha Levin 	struct epoll_event epoll_event = {.events = EPOLLIN};
66ea6eeb1cSSasha Levin 	int r;
67ea6eeb1cSSasha Levin 
681d6fb3f2SSasha Levin 	ioeventfd_avail = kvm__supports_extension(kvm, KVM_CAP_IOEVENTFD);
69e1337781SSasha Levin 	if (!ioeventfd_avail)
7085dde7b2SMatt Evans 		return 1; /* Not fatal, but let caller determine no-go. */
71e1337781SSasha Levin 
7237f3d50eSSasha Levin 	epoll_fd = epoll_create(IOEVENTFD_MAX_EVENTS);
7337f3d50eSSasha Levin 	if (epoll_fd < 0)
74ea6eeb1cSSasha Levin 		return -errno;
75ea6eeb1cSSasha Levin 
76ea6eeb1cSSasha Levin 	epoll_stop_fd = eventfd(0, 0);
77ea6eeb1cSSasha Levin 	epoll_event.data.fd = epoll_stop_fd;
78ea6eeb1cSSasha Levin 
79ea6eeb1cSSasha Levin 	r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, epoll_stop_fd, &epoll_event);
80ea6eeb1cSSasha Levin 	if (r < 0)
81ea6eeb1cSSasha Levin 		goto cleanup;
82ea6eeb1cSSasha Levin 
83ea6eeb1cSSasha Levin 	r = ioeventfd__start();
84ea6eeb1cSSasha Levin 	if (r < 0)
85ea6eeb1cSSasha Levin 		goto cleanup;
86ea6eeb1cSSasha Levin 
87ea6eeb1cSSasha Levin 	r = 0;
88ea6eeb1cSSasha Levin 
89ea6eeb1cSSasha Levin 	return r;
90ea6eeb1cSSasha Levin 
91ea6eeb1cSSasha Levin cleanup:
92ea6eeb1cSSasha Levin 	close(epoll_stop_fd);
93ea6eeb1cSSasha Levin 	close(epoll_fd);
94ea6eeb1cSSasha Levin 
95ea6eeb1cSSasha Levin 	return r;
9637f3d50eSSasha Levin }
9737f3d50eSSasha Levin 
98ea6eeb1cSSasha Levin int ioeventfd__exit(struct kvm *kvm)
99ea6eeb1cSSasha Levin {
100ea6eeb1cSSasha Levin 	u64 tmp = 1;
101ea6eeb1cSSasha Levin 	int r;
102ea6eeb1cSSasha Levin 
103*55628a95SMichael Ellerman 	if (!ioeventfd_avail)
104*55628a95SMichael Ellerman 		return 0;
105*55628a95SMichael 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 }
119ea6eeb1cSSasha Levin 
120ea6eeb1cSSasha Levin int ioeventfd__add_event(struct ioevent *ioevent)
12137f3d50eSSasha Levin {
12237f3d50eSSasha Levin 	struct kvm_ioeventfd kvm_ioevent;
12337f3d50eSSasha Levin 	struct epoll_event epoll_event;
12437f3d50eSSasha Levin 	struct ioevent *new_ioevent;
125ea6eeb1cSSasha Levin 	int event, r;
12637f3d50eSSasha Levin 
127e1337781SSasha Levin 	if (!ioeventfd_avail)
128ea6eeb1cSSasha Levin 		return -ENOSYS;
129e1337781SSasha Levin 
13037f3d50eSSasha Levin 	new_ioevent = malloc(sizeof(*new_ioevent));
13137f3d50eSSasha Levin 	if (new_ioevent == NULL)
132ea6eeb1cSSasha Levin 		return -ENOMEM;
13337f3d50eSSasha Levin 
13437f3d50eSSasha Levin 	*new_ioevent = *ioevent;
13537f3d50eSSasha Levin 	event = new_ioevent->fd;
13637f3d50eSSasha Levin 
13737f3d50eSSasha Levin 	kvm_ioevent = (struct kvm_ioeventfd) {
13837f3d50eSSasha Levin 		.addr			= ioevent->io_addr,
13937f3d50eSSasha Levin 		.len			= ioevent->io_len,
14037f3d50eSSasha Levin 		.datamatch		= ioevent->datamatch,
14137f3d50eSSasha Levin 		.fd			= event,
14237f3d50eSSasha Levin 		.flags			= KVM_IOEVENTFD_FLAG_PIO | KVM_IOEVENTFD_FLAG_DATAMATCH,
14337f3d50eSSasha Levin 	};
14437f3d50eSSasha Levin 
145ea6eeb1cSSasha Levin 	r = ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
146ea6eeb1cSSasha Levin 	if (r) {
147ea6eeb1cSSasha Levin 		r = -errno;
148ea6eeb1cSSasha Levin 		goto cleanup;
149ea6eeb1cSSasha Levin 	}
15037f3d50eSSasha Levin 
15137f3d50eSSasha Levin 	epoll_event = (struct epoll_event) {
15237f3d50eSSasha Levin 		.events			= EPOLLIN,
15337f3d50eSSasha Levin 		.data.ptr		= new_ioevent,
15437f3d50eSSasha Levin 	};
15537f3d50eSSasha Levin 
156ea6eeb1cSSasha Levin 	r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, event, &epoll_event);
157ea6eeb1cSSasha Levin 	if (r) {
158ea6eeb1cSSasha Levin 		r = -errno;
159ea6eeb1cSSasha Levin 		goto cleanup;
16037f3d50eSSasha Levin 	}
16137f3d50eSSasha Levin 
162ea6eeb1cSSasha Levin 	list_add_tail(&new_ioevent->list, &used_ioevents);
163ea6eeb1cSSasha Levin 
164ea6eeb1cSSasha Levin 	return 0;
165ea6eeb1cSSasha Levin 
166ea6eeb1cSSasha Levin cleanup:
167ea6eeb1cSSasha Levin 	free(new_ioevent);
168ea6eeb1cSSasha Levin 	return r;
169ea6eeb1cSSasha Levin }
170ea6eeb1cSSasha Levin 
171ea6eeb1cSSasha Levin int ioeventfd__del_event(u64 addr, u64 datamatch)
17237f3d50eSSasha Levin {
17337f3d50eSSasha Levin 	struct kvm_ioeventfd kvm_ioevent;
17437f3d50eSSasha Levin 	struct ioevent *ioevent;
17537f3d50eSSasha Levin 	u8 found = 0;
17637f3d50eSSasha Levin 
177e1337781SSasha Levin 	if (!ioeventfd_avail)
178ea6eeb1cSSasha Levin 		return -ENOSYS;
179e1337781SSasha Levin 
18037f3d50eSSasha Levin 	list_for_each_entry(ioevent, &used_ioevents, list) {
18137f3d50eSSasha Levin 		if (ioevent->io_addr == addr) {
18237f3d50eSSasha Levin 			found = 1;
18337f3d50eSSasha Levin 			break;
18437f3d50eSSasha Levin 		}
18537f3d50eSSasha Levin 	}
18637f3d50eSSasha Levin 
18737f3d50eSSasha Levin 	if (found == 0 || ioevent == NULL)
188ea6eeb1cSSasha Levin 		return -ENOENT;
18937f3d50eSSasha Levin 
19037f3d50eSSasha Levin 	kvm_ioevent = (struct kvm_ioeventfd) {
19137f3d50eSSasha Levin 		.addr			= ioevent->io_addr,
19237f3d50eSSasha Levin 		.len			= ioevent->io_len,
19337f3d50eSSasha Levin 		.datamatch		= ioevent->datamatch,
19437f3d50eSSasha Levin 		.flags			= KVM_IOEVENTFD_FLAG_PIO
19537f3d50eSSasha Levin 					| KVM_IOEVENTFD_FLAG_DEASSIGN
19637f3d50eSSasha Levin 					| KVM_IOEVENTFD_FLAG_DATAMATCH,
19737f3d50eSSasha Levin 	};
19837f3d50eSSasha Levin 
19937f3d50eSSasha Levin 	ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
20037f3d50eSSasha Levin 
20137f3d50eSSasha Levin 	epoll_ctl(epoll_fd, EPOLL_CTL_DEL, ioevent->fd, NULL);
20237f3d50eSSasha Levin 
20337f3d50eSSasha Levin 	list_del(&ioevent->list);
20437f3d50eSSasha Levin 
20537f3d50eSSasha Levin 	close(ioevent->fd);
20637f3d50eSSasha Levin 	free(ioevent);
20737f3d50eSSasha Levin 
208ea6eeb1cSSasha Levin 	return 0;
20937f3d50eSSasha Levin }
210