xref: /kvmtool/ioeventfd.c (revision 1d6fb3f2bb4c192a5662a0cd623295d92723ebe4)
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];
1937f3d50eSSasha Levin static int	epoll_fd;
2037f3d50eSSasha Levin static LIST_HEAD(used_ioevents);
21e1337781SSasha Levin static bool	ioeventfd_avail;
2237f3d50eSSasha Levin 
23e1337781SSasha Levin void ioeventfd__init(struct kvm *kvm)
2437f3d50eSSasha Levin {
25*1d6fb3f2SSasha Levin 	ioeventfd_avail = kvm__supports_extension(kvm, KVM_CAP_IOEVENTFD);
26e1337781SSasha Levin 	if (!ioeventfd_avail)
27e1337781SSasha Levin 		return;
28e1337781SSasha Levin 
2937f3d50eSSasha Levin 	epoll_fd = epoll_create(IOEVENTFD_MAX_EVENTS);
3037f3d50eSSasha Levin 	if (epoll_fd < 0)
3137f3d50eSSasha Levin 		die("Failed creating epoll fd");
3237f3d50eSSasha Levin }
3337f3d50eSSasha Levin 
3437f3d50eSSasha Levin void ioeventfd__add_event(struct ioevent *ioevent)
3537f3d50eSSasha Levin {
3637f3d50eSSasha Levin 	struct kvm_ioeventfd kvm_ioevent;
3737f3d50eSSasha Levin 	struct epoll_event epoll_event;
3837f3d50eSSasha Levin 	struct ioevent *new_ioevent;
3937f3d50eSSasha Levin 	int event;
4037f3d50eSSasha Levin 
41e1337781SSasha Levin 	if (!ioeventfd_avail)
42e1337781SSasha Levin 		return;
43e1337781SSasha Levin 
4437f3d50eSSasha Levin 	new_ioevent = malloc(sizeof(*new_ioevent));
4537f3d50eSSasha Levin 	if (new_ioevent == NULL)
4637f3d50eSSasha Levin 		die("Failed allocating memory for new ioevent");
4737f3d50eSSasha Levin 
4837f3d50eSSasha Levin 	*new_ioevent = *ioevent;
4937f3d50eSSasha Levin 	event = new_ioevent->fd;
5037f3d50eSSasha Levin 
5137f3d50eSSasha Levin 	kvm_ioevent = (struct kvm_ioeventfd) {
5237f3d50eSSasha Levin 		.addr			= ioevent->io_addr,
5337f3d50eSSasha Levin 		.len			= ioevent->io_len,
5437f3d50eSSasha Levin 		.datamatch		= ioevent->datamatch,
5537f3d50eSSasha Levin 		.fd			= event,
5637f3d50eSSasha Levin 		.flags			= KVM_IOEVENTFD_FLAG_PIO | KVM_IOEVENTFD_FLAG_DATAMATCH,
5737f3d50eSSasha Levin 	};
5837f3d50eSSasha Levin 
5937f3d50eSSasha Levin 	if (ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent) != 0)
6037f3d50eSSasha Levin 		die("Failed creating new ioeventfd");
6137f3d50eSSasha Levin 
6237f3d50eSSasha Levin 	epoll_event = (struct epoll_event) {
6337f3d50eSSasha Levin 		.events			= EPOLLIN,
6437f3d50eSSasha Levin 		.data.ptr		= new_ioevent,
6537f3d50eSSasha Levin 	};
6637f3d50eSSasha Levin 
6737f3d50eSSasha Levin 	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, event, &epoll_event) != 0)
6837f3d50eSSasha Levin 		die("Failed assigning new event to the epoll fd");
6937f3d50eSSasha Levin 
7037f3d50eSSasha Levin 	list_add_tail(&new_ioevent->list, &used_ioevents);
7137f3d50eSSasha Levin }
7237f3d50eSSasha Levin 
7337f3d50eSSasha Levin void ioeventfd__del_event(u64 addr, u64 datamatch)
7437f3d50eSSasha Levin {
7537f3d50eSSasha Levin 	struct kvm_ioeventfd kvm_ioevent;
7637f3d50eSSasha Levin 	struct ioevent *ioevent;
7737f3d50eSSasha Levin 	u8 found = 0;
7837f3d50eSSasha Levin 
79e1337781SSasha Levin 	if (!ioeventfd_avail)
80e1337781SSasha Levin 		return;
81e1337781SSasha Levin 
8237f3d50eSSasha Levin 	list_for_each_entry(ioevent, &used_ioevents, list) {
8337f3d50eSSasha Levin 		if (ioevent->io_addr == addr) {
8437f3d50eSSasha Levin 			found = 1;
8537f3d50eSSasha Levin 			break;
8637f3d50eSSasha Levin 		}
8737f3d50eSSasha Levin 	}
8837f3d50eSSasha Levin 
8937f3d50eSSasha Levin 	if (found == 0 || ioevent == NULL)
9037f3d50eSSasha Levin 		return;
9137f3d50eSSasha Levin 
9237f3d50eSSasha Levin 	kvm_ioevent = (struct kvm_ioeventfd) {
9337f3d50eSSasha Levin 		.addr			= ioevent->io_addr,
9437f3d50eSSasha Levin 		.len			= ioevent->io_len,
9537f3d50eSSasha Levin 		.datamatch		= ioevent->datamatch,
9637f3d50eSSasha Levin 		.flags			= KVM_IOEVENTFD_FLAG_PIO
9737f3d50eSSasha Levin 					| KVM_IOEVENTFD_FLAG_DEASSIGN
9837f3d50eSSasha Levin 					| KVM_IOEVENTFD_FLAG_DATAMATCH,
9937f3d50eSSasha Levin 	};
10037f3d50eSSasha Levin 
10137f3d50eSSasha Levin 	ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
10237f3d50eSSasha Levin 
10337f3d50eSSasha Levin 	epoll_ctl(epoll_fd, EPOLL_CTL_DEL, ioevent->fd, NULL);
10437f3d50eSSasha Levin 
10537f3d50eSSasha Levin 	list_del(&ioevent->list);
10637f3d50eSSasha Levin 
10737f3d50eSSasha Levin 	close(ioevent->fd);
10837f3d50eSSasha Levin 	free(ioevent);
10937f3d50eSSasha Levin }
11037f3d50eSSasha Levin 
11137f3d50eSSasha Levin static void *ioeventfd__thread(void *param)
11237f3d50eSSasha Levin {
11337f3d50eSSasha Levin 	for (;;) {
11437f3d50eSSasha Levin 		int nfds, i;
11537f3d50eSSasha Levin 
11637f3d50eSSasha Levin 		nfds = epoll_wait(epoll_fd, events, IOEVENTFD_MAX_EVENTS, -1);
11737f3d50eSSasha Levin 		for (i = 0; i < nfds; i++) {
11837f3d50eSSasha Levin 			u64 tmp;
11937f3d50eSSasha Levin 			struct ioevent *ioevent;
12037f3d50eSSasha Levin 
12137f3d50eSSasha Levin 			ioevent = events[i].data.ptr;
12237f3d50eSSasha Levin 
12337f3d50eSSasha Levin 			if (read(ioevent->fd, &tmp, sizeof(tmp)) < 0)
12437f3d50eSSasha Levin 				die("Failed reading event");
12537f3d50eSSasha Levin 
12637f3d50eSSasha Levin 			ioevent->fn(ioevent->fn_kvm, ioevent->fn_ptr);
12737f3d50eSSasha Levin 		}
12837f3d50eSSasha Levin 	}
12937f3d50eSSasha Levin 
13037f3d50eSSasha Levin 	return NULL;
13137f3d50eSSasha Levin }
13237f3d50eSSasha Levin 
13337f3d50eSSasha Levin void ioeventfd__start(void)
13437f3d50eSSasha Levin {
13537f3d50eSSasha Levin 	pthread_t thread;
13637f3d50eSSasha Levin 
137e1337781SSasha Levin 	if (!ioeventfd_avail)
138e1337781SSasha Levin 		return;
139e1337781SSasha Levin 
14037f3d50eSSasha Levin 	if (pthread_create(&thread, NULL, ioeventfd__thread, NULL) != 0)
14137f3d50eSSasha Levin 		die("Failed starting ioeventfd thread");
14237f3d50eSSasha Levin }
143