--- /dev/null
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include <linux/kernel.h>
+#include <linux/kvm.h>
+#include <linux/types.h>
+
+#include "kvm/ioeventfd.h"
+#include "kvm/kvm.h"
+#include "kvm/util.h"
+
+#define IOEVENTFD_MAX_EVENTS 20
+
+static struct epoll_event events[IOEVENTFD_MAX_EVENTS];
+static int epoll_fd;
+static LIST_HEAD(used_ioevents);
+
+void ioeventfd__init(void)
+{
+ epoll_fd = epoll_create(IOEVENTFD_MAX_EVENTS);
+ if (epoll_fd < 0)
+ die("Failed creating epoll fd");
+}
+
+void ioeventfd__add_event(struct ioevent *ioevent)
+{
+ struct kvm_ioeventfd kvm_ioevent;
+ struct epoll_event epoll_event;
+ struct ioevent *new_ioevent;
+ int event;
+
+ new_ioevent = malloc(sizeof(*new_ioevent));
+ if (new_ioevent == NULL)
+ die("Failed allocating memory for new ioevent");
+
+ *new_ioevent = *ioevent;
+ event = new_ioevent->fd;
+
+ kvm_ioevent = (struct kvm_ioeventfd) {
+ .addr = ioevent->io_addr,
+ .len = ioevent->io_len,
+ .datamatch = ioevent->datamatch,
+ .fd = event,
+ .flags = KVM_IOEVENTFD_FLAG_PIO | KVM_IOEVENTFD_FLAG_DATAMATCH,
+ };
+
+ if (ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent) != 0)
+ die("Failed creating new ioeventfd");
+
+ epoll_event = (struct epoll_event) {
+ .events = EPOLLIN,
+ .data.ptr = new_ioevent,
+ };
+
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, event, &epoll_event) != 0)
+ die("Failed assigning new event to the epoll fd");
+
+ list_add_tail(&new_ioevent->list, &used_ioevents);
+}
+
+void ioeventfd__del_event(u64 addr, u64 datamatch)
+{
+ struct kvm_ioeventfd kvm_ioevent;
+ struct ioevent *ioevent;
+ u8 found = 0;
+
+ list_for_each_entry(ioevent, &used_ioevents, list) {
+ if (ioevent->io_addr == addr) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found == 0 || ioevent == NULL)
+ return;
+
+ kvm_ioevent = (struct kvm_ioeventfd) {
+ .addr = ioevent->io_addr,
+ .len = ioevent->io_len,
+ .datamatch = ioevent->datamatch,
+ .flags = KVM_IOEVENTFD_FLAG_PIO
+ | KVM_IOEVENTFD_FLAG_DEASSIGN
+ | KVM_IOEVENTFD_FLAG_DATAMATCH,
+ };
+
+ ioctl(ioevent->fn_kvm->vm_fd, KVM_IOEVENTFD, &kvm_ioevent);
+
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, ioevent->fd, NULL);
+
+ list_del(&ioevent->list);
+
+ close(ioevent->fd);
+ free(ioevent);
+}
+
+static void *ioeventfd__thread(void *param)
+{
+ for (;;) {
+ int nfds, i;
+
+ nfds = epoll_wait(epoll_fd, events, IOEVENTFD_MAX_EVENTS, -1);
+ for (i = 0; i < nfds; i++) {
+ u64 tmp;
+ struct ioevent *ioevent;
+
+ ioevent = events[i].data.ptr;
+
+ if (read(ioevent->fd, &tmp, sizeof(tmp)) < 0)
+ die("Failed reading event");
+
+ ioevent->fn(ioevent->fn_kvm, ioevent->fn_ptr);
+ }
+ }
+
+ return NULL;
+}
+
+void ioeventfd__start(void)
+{
+ pthread_t thread;
+
+ if (pthread_create(&thread, NULL, ioeventfd__thread, NULL) != 0)
+ die("Failed starting ioeventfd thread");
+}