diff --git a/devicemodel/hw/pci/virtio/vhost.c b/devicemodel/hw/pci/virtio/vhost.c index 8dfc59d47..849e91856 100644 --- a/devicemodel/hw/pci/virtio/vhost.c +++ b/devicemodel/hw/pci/virtio/vhost.c @@ -153,12 +153,132 @@ vhost_eventfd_test_and_clear(int fd) return -1; } +static void +vhost_vq_notify(int fd __attribute__((unused)), + enum ev_type t __attribute__((unused)), + void *arg) +{ + struct vhost_vq *vq = arg; + struct virtio_vq_info *vqi; + struct vhost_dev *vdev; + + vdev = vq->dev; + vqi = &vdev->base->queues[vdev->vq_idx + vq->idx]; + vq_interrupt(vdev->base, vqi); +} + static int vhost_vq_register_eventfd(struct vhost_dev *vdev, int idx, bool is_register) { - /* to be implemented */ - return -1; + struct acrn_ioeventfd ioeventfd = {0}; + struct acrn_irqfd irqfd = {0}; + struct virtio_base *base; + struct vhost_vq *vq; + struct virtio_vq_info *vqi; + struct pcibar *bar; + int rc = -1; + + /* this interface is called only by vhost_vq_start, + * parameters have been checked there + */ + base = vdev->base; + vqi = &vdev->base->queues[vdev->vq_idx + idx]; + vq = &vdev->vqs[idx]; + + if (!is_register) { + ioeventfd.flags = ACRN_IOEVENTFD_FLAG_DEASSIGN; + irqfd.flags = ACRN_IRQFD_FLAG_DEASSIGN; + } + + /* register ioeventfd for kick */ + if (base->device_caps & ACRN_VIRTIO_F_VERSION_1) { + /* + * in the current implementation, if virtio 1.0 with pio + * notity, its bar idx should be set to non-zero + */ + if (base->modern_pio_bar_idx) { + bar = &vdev->base->dev->bar[base->modern_pio_bar_idx]; + ioeventfd.data = vdev->vq_idx + idx; + ioeventfd.addr = bar->addr; + ioeventfd.len = 2; + ioeventfd.flags |= (ACRN_IOEVENTFD_FLAG_DATAMATCH | + ACRN_IOEVENTFD_FLAG_PIO); + } else if (base->modern_mmio_bar_idx) { + bar = &vdev->base->dev->bar[base->modern_mmio_bar_idx]; + ioeventfd.data = 0; + ioeventfd.addr = bar->addr + VIRTIO_CAP_NOTIFY_OFFSET + + (vdev->vq_idx + idx) * + VIRTIO_MODERN_NOTIFY_OFF_MULT; + ioeventfd.len = 2; + /* no additional flag bit should be set for MMIO */ + } else { + WPRINTF("invalid virtio 1.0 parameters, 0x%lx\n", + base->device_caps); + return -1; + } + } else { + bar = &vdev->base->dev->bar[base->legacy_pio_bar_idx]; + ioeventfd.data = vdev->vq_idx + idx; + ioeventfd.addr = bar->addr + VIRTIO_CR_QNOTIFY; + ioeventfd.len = 2; + ioeventfd.flags |= (ACRN_IOEVENTFD_FLAG_DATAMATCH | + ACRN_IOEVENTFD_FLAG_PIO); + } + + ioeventfd.fd = vq->kick_fd; + DPRINTF("[ioeventfd: %d][0x%lx@%d][flags: 0x%x][data: 0x%lx]\n", + ioeventfd.fd, ioeventfd.addr, ioeventfd.len, + ioeventfd.flags, ioeventfd.data); + rc = vm_ioeventfd(vdev->base->dev->vmctx, &ioeventfd); + if (rc < 0) { + WPRINTF("vm_ioeventfd failed rc = %d, errno = %d\n", + rc, errno); + return -1; + } + + if (pci_msix_enabled(base->dev)) { + /* register irqfd for notify */ + struct msix_table_entry *mte; + struct acrn_msi_entry msi; + + mte = &vdev->base->dev->msix.table[vqi->msix_idx]; + msi.msi_addr = mte->addr; + msi.msi_data = mte->msg_data; + irqfd.fd = vq->call_fd; + /* no additional flag bit should be set */ + irqfd.msi = msi; + DPRINTF("[irqfd: %d][MSIX: %d]\n", irqfd.fd, vqi->msix_idx); + rc = vm_irqfd(vdev->base->dev->vmctx, &irqfd); + } else { + /* + * irqfd only supports MSIx now. For non-MSIx, call_fd is polled + * by dm then inject interrupts to guest + */ + if (is_register) { + vq->mevp = mevent_add(vq->call_fd, EVF_READ, + vhost_vq_notify, vq); + if (!vq->mevp) { + WPRINTF("mevent_add failed\n"); + rc = -1; + } + } else if (vq->mevp) { + mevent_delete(vq->mevp); + vq->mevp = NULL; + } + } + + if (rc < 0) { + WPRINTF("vm_irqfd failed rc = %d, errno = %d\n", rc, errno); + /* unregister ioeventfd */ + if (is_register) { + ioeventfd.flags |= ACRN_IOEVENTFD_FLAG_DEASSIGN; + vm_ioeventfd(vdev->base->dev->vmctx, &ioeventfd); + } + return -1; + } + + return 0; } static int