blob: 3a240e4f17df4bdf7c1890ea00aadde4b9e965f1 [file] [log] [blame]
#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");
}