xref: /kvmtool/ioeventfd.c (revision ea6eeb1c10ccd3ebc9b3070829053e66e3b53da6)
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];
19*ea6eeb1cSSasha Levin static int	epoll_fd, epoll_stop_fd;
2037f3d50eSSasha Levin static LIST_HEAD(used_ioevents);
21e1337781SSasha Levin static bool	ioeventfd_avail;
2237f3d50eSSasha Levin 
23*ea6eeb1cSSasha Levin static void *ioeventfd__thread(void *param)
2437f3d50eSSasha Levin {
25*ea6eeb1cSSasha Levin 	u64 tmp = 1;
26*ea6eeb1cSSasha Levin 
27*ea6eeb1cSSasha Levin 	for (;;) {
28*ea6eeb1cSSasha Levin 		int nfds, i;
29*ea6eeb1cSSasha Levin 
30*ea6eeb1cSSasha Levin 		nfds = epoll_wait(epoll_fd, events, IOEVENTFD_MAX_EVENTS, -1);
31*ea6eeb1cSSasha Levin 		for (i = 0; i < nfds; i++) {
32*ea6eeb1cSSasha Levin 			struct ioevent *ioevent;
33*ea6eeb1cSSasha Levin 
34*ea6eeb1cSSasha Levin 			if (events[i].data.fd == epoll_stop_fd)
35*ea6eeb1cSSasha Levin 				goto done;
36*ea6eeb1cSSasha Levin 
37*ea6eeb1cSSasha Levin 			ioevent = events[i].data.ptr;
38*ea6eeb1cSSasha Levin 
39*ea6eeb1cSSasha Levin 			if (read(ioevent->fd, &tmp, sizeof(tmp)) < 0)
40*ea6eeb1cSSasha Levin 				die("Failed reading event");
41*ea6eeb1cSSasha Levin 
42*ea6eeb1cSSasha Levin 			ioevent->fn(ioevent->fn_kvm, ioevent->fn_ptr);
43*ea6eeb1cSSasha Levin 		}
44*ea6eeb1cSSasha Levin 	}
45*ea6eeb1cSSasha Levin 
46*ea6eeb1cSSasha Levin done:
47*ea6eeb1cSSasha Levin 	tmp = 1;
48*ea6eeb1cSSasha Levin 	tmp = write(epoll_stop_fd, &tmp, sizeof(tmp));
49*ea6eeb1cSSasha Levin 
50*ea6eeb1cSSasha Levin 	return NULL;
51*ea6eeb1cSSasha Levin }
52*ea6eeb1cSSasha Levin 
53*ea6eeb1cSSasha Levin static int ioeventfd__start(void)
54*ea6eeb1cSSasha Levin {
55*ea6eeb1cSSasha Levin 	pthread_t thread;
56*ea6eeb1cSSasha Levin 
57*ea6eeb1cSSasha Levin 	if (!ioeventfd_avail)
58*ea6eeb1cSSasha Levin 		return -ENOSYS;
59*ea6eeb1cSSasha Levin 
60*ea6eeb1cSSasha Levin 	return pthread_create(&thread, NULL, ioeventfd__thread, NULL);
61*ea6eeb1cSSasha Levin }
62*ea6eeb1cSSasha Levin 
63*ea6eeb1cSSasha Levin int ioeventfd__init(struct kvm *kvm)
64*ea6eeb1cSSasha Levin {
65*ea6eeb1cSSasha Levin 	struct epoll_event epoll_event = {.events = EPOLLIN};
66*ea6eeb1cSSasha Levin 	int r;
67*ea6eeb1cSSasha Levin 
681d6fb3f2SSasha Levin 	ioeventfd_avail = kvm__supports_extension(kvm, KVM_CAP_IOEVENTFD);
69e1337781SSasha Levin 	if (!ioeventfd_avail)
70*ea6eeb1cSSasha Levin 		return -ENOSYS;
71e1337781SSasha Levin 
7237f3d50eSSasha Levin 	epoll_fd = epoll_create(IOEVENTFD_MAX_EVENTS);
7337f3d50eSSasha Levin 	if (epoll_fd < 0)
74*ea6eeb1cSSasha Levin 		return -errno;
75*ea6eeb1cSSasha Levin 
76*ea6eeb1cSSasha Levin 	epoll_stop_fd = eventfd(0, 0);
77*ea6eeb1cSSasha Levin 	epoll_event.data.fd = epoll_stop_fd;
78*ea6eeb1cSSasha Levin 
79*ea6eeb1cSSasha Levin 	r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, epoll_stop_fd, &epoll_event);
80*ea6eeb1cSSasha Levin 	if (r < 0)
81*ea6eeb1cSSasha Levin 		goto cleanup;
82*ea6eeb1cSSasha Levin 
83*ea6eeb1cSSasha Levin 	r = ioeventfd__start();
84*ea6eeb1cSSasha Levin 	if (r < 0)
85*ea6eeb1cSSasha Levin 		goto cleanup;
86*ea6eeb1cSSasha Levin 
87*ea6eeb1cSSasha Levin 	r = 0;
88*ea6eeb1cSSasha Levin 
89*ea6eeb1cSSasha Levin 	return r;
90*ea6eeb1cSSasha Levin 
91*ea6eeb1cSSasha Levin cleanup:
92*ea6eeb1cSSasha Levin 	close(epoll_stop_fd);
93*ea6eeb1cSSasha Levin 	close(epoll_fd);
94*ea6eeb1cSSasha Levin 
95*ea6eeb1cSSasha Levin 	return r;
9637f3d50eSSasha Levin }
9737f3d50eSSasha Levin 
98*ea6eeb1cSSasha Levin int ioeventfd__exit(struct kvm *kvm)
99*ea6eeb1cSSasha Levin {
100*ea6eeb1cSSasha Levin 	u64 tmp = 1;
101*ea6eeb1cSSasha Levin 	int r;
102*ea6eeb1cSSasha Levin 
103*ea6eeb1cSSasha Levin 	r = write(epoll_stop_fd, &tmp, sizeof(tmp));
104*ea6eeb1cSSasha Levin 	if (r < 0)
105*ea6eeb1cSSasha Levin 		return r;
106*ea6eeb1cSSasha Levin 
107*ea6eeb1cSSasha Levin 	r = read(epoll_stop_fd, &tmp, sizeof(tmp));
108*ea6eeb1cSSasha Levin 	if (r < 0)
109*ea6eeb1cSSasha Levin 		return r;
110*ea6eeb1cSSasha Levin 
111*ea6eeb1cSSasha Levin 	close(epoll_fd);
112*ea6eeb1cSSasha Levin 	close(epoll_stop_fd);
113*ea6eeb1cSSasha Levin 
114*ea6eeb1cSSasha Levin 	return 0;
115*ea6eeb1cSSasha Levin }
116*ea6eeb1cSSasha Levin 
117*ea6eeb1cSSasha Levin int ioeventfd__add_event(struct ioevent *ioevent)
11837f3d50eSSasha Levin {
11937f3d50eSSasha Levin 	struct kvm_ioeventfd kvm_ioevent;
12037f3d50eSSasha Levin 	struct epoll_event epoll_event;
12137f3d50eSSasha Levin 	struct ioevent *new_ioevent;
122*ea6eeb1cSSasha Levin 	int event, r;
12337f3d50eSSasha Levin 
124e1337781SSasha Levin 	if (!ioeventfd_avail)
125*ea6eeb1cSSasha Levin 		return -ENOSYS;
126e1337781SSasha Levin 
12737f3d50eSSasha Levin 	new_ioevent = malloc(sizeof(*new_ioevent));
12837f3d50eSSasha Levin 	if (new_ioevent == NULL)
129*ea6eeb1cSSasha Levin 		return -ENOMEM;
13037f3d50eSSasha Levin 
13137f3d50eSSasha Levin 	*new_ioevent = *ioevent;
13237f3d50eSSasha Levin 	event = new_ioevent->fd;
13337f3d50eSSasha Levin 
13437f3d50eSSasha Levin 	kvm_ioevent = (struct kvm_ioeventfd) {
13537f3d50eSSasha Levin 		.addr			= ioevent->io_addr,
13637f3d50eSSasha Levin 		.len			= ioevent->io_len,
13737f3d50eSSasha Levin 		.datamatch		= ioevent->datamatch,
13837f3d50eSSasha Levin 		.fd			= event,
13937f3d50eSSasha Levin 		.flags			= KVM_IOEVENTFD_FLAG_PIO | KVM_IOEVENTFD_FLAG_DATAMATCH,
14037f3d50eSSasha Levin 	};
14137f3d50eSSasha Levin 
142*ea6eeb1cSSasha Levin 	r = ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
143*ea6eeb1cSSasha Levin 	if (r) {
144*ea6eeb1cSSasha Levin 		r = -errno;
145*ea6eeb1cSSasha Levin 		goto cleanup;
146*ea6eeb1cSSasha Levin 	}
14737f3d50eSSasha Levin 
14837f3d50eSSasha Levin 	epoll_event = (struct epoll_event) {
14937f3d50eSSasha Levin 		.events			= EPOLLIN,
15037f3d50eSSasha Levin 		.data.ptr		= new_ioevent,
15137f3d50eSSasha Levin 	};
15237f3d50eSSasha Levin 
153*ea6eeb1cSSasha Levin 	r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, event, &epoll_event);
154*ea6eeb1cSSasha Levin 	if (r) {
155*ea6eeb1cSSasha Levin 		r = -errno;
156*ea6eeb1cSSasha Levin 		goto cleanup;
15737f3d50eSSasha Levin 	}
15837f3d50eSSasha Levin 
159*ea6eeb1cSSasha Levin 	list_add_tail(&new_ioevent->list, &used_ioevents);
160*ea6eeb1cSSasha Levin 
161*ea6eeb1cSSasha Levin 	return 0;
162*ea6eeb1cSSasha Levin 
163*ea6eeb1cSSasha Levin cleanup:
164*ea6eeb1cSSasha Levin 	free(new_ioevent);
165*ea6eeb1cSSasha Levin 	return r;
166*ea6eeb1cSSasha Levin }
167*ea6eeb1cSSasha Levin 
168*ea6eeb1cSSasha Levin int ioeventfd__del_event(u64 addr, u64 datamatch)
16937f3d50eSSasha Levin {
17037f3d50eSSasha Levin 	struct kvm_ioeventfd kvm_ioevent;
17137f3d50eSSasha Levin 	struct ioevent *ioevent;
17237f3d50eSSasha Levin 	u8 found = 0;
17337f3d50eSSasha Levin 
174e1337781SSasha Levin 	if (!ioeventfd_avail)
175*ea6eeb1cSSasha Levin 		return -ENOSYS;
176e1337781SSasha Levin 
17737f3d50eSSasha Levin 	list_for_each_entry(ioevent, &used_ioevents, list) {
17837f3d50eSSasha Levin 		if (ioevent->io_addr == addr) {
17937f3d50eSSasha Levin 			found = 1;
18037f3d50eSSasha Levin 			break;
18137f3d50eSSasha Levin 		}
18237f3d50eSSasha Levin 	}
18337f3d50eSSasha Levin 
18437f3d50eSSasha Levin 	if (found == 0 || ioevent == NULL)
185*ea6eeb1cSSasha Levin 		return -ENOENT;
18637f3d50eSSasha Levin 
18737f3d50eSSasha Levin 	kvm_ioevent = (struct kvm_ioeventfd) {
18837f3d50eSSasha Levin 		.addr			= ioevent->io_addr,
18937f3d50eSSasha Levin 		.len			= ioevent->io_len,
19037f3d50eSSasha Levin 		.datamatch		= ioevent->datamatch,
19137f3d50eSSasha Levin 		.flags			= KVM_IOEVENTFD_FLAG_PIO
19237f3d50eSSasha Levin 					| KVM_IOEVENTFD_FLAG_DEASSIGN
19337f3d50eSSasha Levin 					| KVM_IOEVENTFD_FLAG_DATAMATCH,
19437f3d50eSSasha Levin 	};
19537f3d50eSSasha Levin 
19637f3d50eSSasha Levin 	ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
19737f3d50eSSasha Levin 
19837f3d50eSSasha Levin 	epoll_ctl(epoll_fd, EPOLL_CTL_DEL, ioevent->fd, NULL);
19937f3d50eSSasha Levin 
20037f3d50eSSasha Levin 	list_del(&ioevent->list);
20137f3d50eSSasha Levin 
20237f3d50eSSasha Levin 	close(ioevent->fd);
20337f3d50eSSasha Levin 	free(ioevent);
20437f3d50eSSasha Levin 
205*ea6eeb1cSSasha Levin 	return 0;
20637f3d50eSSasha Levin }
207