vfio是通过eventfd来将中断提供给user space,当上层通过ioctl(VFIO_DEVICE_SET_IRQS)来设置中断后,在kernel中vfio_pci_ioctl->vfio_pci_set_irqs_ioctl->vfio_pci_set_msi_trigger,假定这时中断的flag包含VFIO_IRQ_SET_DATA_EVENTFD 。
则vfio_msi_set_block->vfio_msi_set_vector_signal
在vfio_msi_set_vector_signal 中最终要的就是
ret = request_irq(irq, vfio_msihandler, 0,
vdev->ctx[vector].name, trigger);
可见是通过request_irq 申请中断的,其中断函数是vfio_msihandler
static irqreturn_t vfio_msihandler(int irq, void *arg)
{
struct eventfd_ctx *trigger = arg;
eventfd_signal(trigger, 1);
return IRQ_HANDLED;
}
这里仅仅是通过eventfd 发送signal
于此同时在qemu中
static int vfio_set_trigger_eventfd(VFIOINTp *intp,
eventfd_user_side_handler_t handler)
{
VFIODevice *vbasedev = &intp->vdev->vbasedev;
struct vfio_irq_set *irq_set;
int argsz, ret;
int32_t *pfd;
argsz = sizeof(*irq_set) + sizeof(*pfd);
irq_set = g_malloc0(argsz);
irq_set->argsz = argsz;
irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
irq_set->index = intp->pin;
irq_set->start = 0;
irq_set->count = 1;
pfd = (int32_t *)&irq_set->data;
*pfd = event_notifier_get_fd(intp->interrupt);
qemu_set_fd_handler(*pfd, (IOHandler *)handler, NULL, intp);
ret = ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
g_free(irq_set);
if (ret < 0) {
error_report("vfio: Failed to set trigger eventfd: %m");
qemu_set_fd_handler(*pfd, NULL, NULL, NULL);
}
return ret;
}
在通过VFIO_DEVICE_SET_IRQS 设定中断之前会通过 *pfd = event_notifier_get_fd(intp->interrupt); 得到和这个中断绑定的fd,然后通过qemu_set_fd_handler 给这个fd 设定一个handler
ret = vfio_set_trigger_eventfd(intp, vfio_intp_interrupt);
可见handler为vfio_intp_interrupt 在event_notifier_test_and_clear中又会调用event_notifier_test_and_clear来等待kernel通过eventfd_signal(trigger, 1);发送的中断.
int event_notifier_test_and_clear(EventNotifier *e)
{
int value;
ssize_t len;
char buffer[512];
/* Drain the notify pipe. For eventfd, only 8 bytes will be read. */
value = 0;
do {
len = read(e->rfd, buffer, sizeof(buffer));
value |= (len > 0);
} while ((len == -1 && errno == EINTR) || len == sizeof(buffer));
return value;
}
则vfio_msi_set_block->vfio_msi_set_vector_signal
在vfio_msi_set_vector_signal 中最终要的就是
ret = request_irq(irq, vfio_msihandler, 0,
vdev->ctx[vector].name, trigger);
可见是通过request_irq 申请中断的,其中断函数是vfio_msihandler
static irqreturn_t vfio_msihandler(int irq, void *arg)
{
struct eventfd_ctx *trigger = arg;
eventfd_signal(trigger, 1);
return IRQ_HANDLED;
}
这里仅仅是通过eventfd 发送signal
于此同时在qemu中
static int vfio_set_trigger_eventfd(VFIOINTp *intp,
eventfd_user_side_handler_t handler)
{
VFIODevice *vbasedev = &intp->vdev->vbasedev;
struct vfio_irq_set *irq_set;
int argsz, ret;
int32_t *pfd;
argsz = sizeof(*irq_set) + sizeof(*pfd);
irq_set = g_malloc0(argsz);
irq_set->argsz = argsz;
irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
irq_set->index = intp->pin;
irq_set->start = 0;
irq_set->count = 1;
pfd = (int32_t *)&irq_set->data;
*pfd = event_notifier_get_fd(intp->interrupt);
qemu_set_fd_handler(*pfd, (IOHandler *)handler, NULL, intp);
ret = ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
g_free(irq_set);
if (ret < 0) {
error_report("vfio: Failed to set trigger eventfd: %m");
qemu_set_fd_handler(*pfd, NULL, NULL, NULL);
}
return ret;
}
在通过VFIO_DEVICE_SET_IRQS 设定中断之前会通过 *pfd = event_notifier_get_fd(intp->interrupt); 得到和这个中断绑定的fd,然后通过qemu_set_fd_handler 给这个fd 设定一个handler
ret = vfio_set_trigger_eventfd(intp, vfio_intp_interrupt);
可见handler为vfio_intp_interrupt 在event_notifier_test_and_clear中又会调用event_notifier_test_and_clear来等待kernel通过eventfd_signal(trigger, 1);发送的中断.
int event_notifier_test_and_clear(EventNotifier *e)
{
int value;
ssize_t len;
char buffer[512];
/* Drain the notify pipe. For eventfd, only 8 bytes will be read. */
value = 0;
do {
len = read(e->rfd, buffer, sizeof(buffer));
value |= (len > 0);
} while ((len == -1 && errno == EINTR) || len == sizeof(buffer));
return value;
}