From e3f4e34c01b717e9d006713d6a8ea3f0a0c0fb18 Mon Sep 17 00:00:00 2001 From: Jian Jun Chen Date: Fri, 10 Aug 2018 15:14:35 +0800 Subject: [PATCH] dm: virtio: implement vhost_vq_register_eventfd There are 2 eventfds for one virtqueue, one is for kick and the other is for notify. eventfd used for kick is associated with a PIO/MMIO region. eventfd used for notify is associated with a MSIx/INTx. The eventfd pair is registered to VHM thru VHM char dev. VHM irqfd currently only support MSIx. If INTx is used, vhost proxy uses mevent to poll the call fd from vhost then inject interrupt to guest. Tracked-On: #1329 Signed-off-by: Jian Jun Chen Acked-by: Yu Wang --- devicemodel/hw/pci/virtio/vhost.c | 124 +++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 2 deletions(-) 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