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