@@ -83,6 +83,10 @@
/* Flags track per-device state like workarounds for quirks in older guests. */
#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG (1 << 0)
+/* Performance improves when virtqueue kick processing is decoupled from the
+ * vcpu thread using ioeventfd for some devices. */
+#define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << 1)
+
/* QEMU doesn't strictly need write barriers since everything runs in
* lock-step. We'll leave the calls to wmb() in though to make it obvious for
* KVM or if kqemu gets SMP support.
@@ -179,12 +183,108 @@ static int virtio_pci_load_queue(void * opaque, int n, QEMUFile *f)
return 0;
}
+static int virtio_pci_set_host_notifier_ioeventfd(VirtIOPCIProxy *proxy, int n, bool assign)
+{
+ VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
+ EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
+ int r;
+ if (assign) {
+ r = event_notifier_init(notifier, 1);
+ if (r < 0) {
+ return r;
+ }
+ r = kvm_set_ioeventfd_pio_word(event_notifier_get_fd(notifier),
+ proxy->addr + VIRTIO_PCI_QUEUE_NOTIFY,
+ n, assign);
+ if (r < 0) {
+ event_notifier_cleanup(notifier);
+ }
+ } else {
+ r = kvm_set_ioeventfd_pio_word(event_notifier_get_fd(notifier),
+ proxy->addr + VIRTIO_PCI_QUEUE_NOTIFY,
+ n, assign);
+ if (r < 0) {
+ return r;
+ }
+ event_notifier_cleanup(notifier);
+ }
+ return r;
+}
+
+static void virtio_pci_host_notifier_read(void *opaque)
+{
+ VirtQueue *vq = opaque;
+ EventNotifier *n = virtio_queue_get_host_notifier(vq);
+ if (event_notifier_test_and_clear(n)) {
+ virtio_queue_notify_vq(vq);
+ }
+}
+
+static void virtio_pci_set_host_notifier_fd_handler(VirtIOPCIProxy *proxy, int n, bool assign)
+{
+ VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
+ EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
+ if (assign) {
+ qemu_set_fd_handler(event_notifier_get_fd(notifier),
+ virtio_pci_host_notifier_read, NULL, vq);
+ } else {
+ qemu_set_fd_handler(event_notifier_get_fd(notifier),
+ NULL, NULL, NULL);
+ }
+}
+
+static int virtio_pci_set_host_notifiers(VirtIOPCIProxy *proxy, bool assign)
+{
+ int n, r;
+
+ for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
+ if (!virtio_queue_get_num(proxy->vdev, n)) {
+ continue;
+ }
+
+ if (assign) {
+ r = virtio_pci_set_host_notifier_ioeventfd(proxy, n, true);
+ if (r < 0) {
+ goto assign_error;
+ }
+
+ virtio_pci_set_host_notifier_fd_handler(proxy, n, true);
+ } else {
+ virtio_pci_set_host_notifier_fd_handler(proxy, n, false);
+ virtio_pci_set_host_notifier_ioeventfd(proxy, n, false);
+ }
+ }
+ return 0;
+
+assign_error:
+ proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
+ while (--n >= 0) {
+ virtio_pci_set_host_notifier_fd_handler(proxy, n, false);
+ virtio_pci_set_host_notifier_ioeventfd(proxy, n, false);
+ }
+ return r;
+}
+
+static void virtio_pci_reset_vdev(VirtIOPCIProxy *proxy)
+{
+ /* Poke virtio device so it deassigns its host notifiers (if any) */
+ virtio_set_status(proxy->vdev, 0);
+
+ /* Now safely deassign our own host notifiers */
+ if (proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) {
+ virtio_pci_set_host_notifiers(proxy, false);
+ }
+
+ virtio_reset(proxy->vdev);
+ msix_unuse_all_vectors(&proxy->pci_dev);
+}
+
static void virtio_pci_reset(DeviceState *d)
{
VirtIOPCIProxy *proxy = container_of(d, VirtIOPCIProxy, pci_dev.qdev);
- virtio_reset(proxy->vdev);
+ virtio_pci_reset_vdev(proxy);
msix_reset(&proxy->pci_dev);
- proxy->flags = 0;
+ proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
}
static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
@@ -209,11 +309,10 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
case VIRTIO_PCI_QUEUE_PFN:
pa = (target_phys_addr_t)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT;
if (pa == 0) {
- virtio_reset(proxy->vdev);
- msix_unuse_all_vectors(&proxy->pci_dev);
- }
- else
+ virtio_pci_reset_vdev(proxy);
+ } else {
virtio_queue_set_addr(vdev, vdev->queue_sel, pa);
+ }
break;
case VIRTIO_PCI_QUEUE_SEL:
if (val < VIRTIO_PCI_QUEUE_MAX)
@@ -223,10 +322,16 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
virtio_queue_notify(vdev, val);
break;
case VIRTIO_PCI_STATUS:
- virtio_set_status(vdev, val & 0xFF);
- if (vdev->status == 0) {
- virtio_reset(proxy->vdev);
- msix_unuse_all_vectors(&proxy->pci_dev);
+ if ((val & VIRTIO_CONFIG_S_DRIVER_OK) &&
+ !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) &&
+ (proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD)) {
+ virtio_pci_set_host_notifiers(proxy, true);
+ }
+
+ if (val & 0xFF) {
+ virtio_set_status(vdev, val & 0xFF);
+ } else {
+ virtio_pci_reset_vdev(proxy);
}
/* Linux before 2.6.34 sets the device as OK without enabling
@@ -480,30 +585,12 @@ assign_error:
static int virtio_pci_set_host_notifier(void *opaque, int n, bool assign)
{
VirtIOPCIProxy *proxy = opaque;
- VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
- EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
- int r;
- if (assign) {
- r = event_notifier_init(notifier, 1);
- if (r < 0) {
- return r;
- }
- r = kvm_set_ioeventfd_pio_word(event_notifier_get_fd(notifier),
- proxy->addr + VIRTIO_PCI_QUEUE_NOTIFY,
- n, assign);
- if (r < 0) {
- event_notifier_cleanup(notifier);
- }
+ if (proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) {
+ virtio_pci_set_host_notifier_fd_handler(proxy, n, !assign);
+ return 0;
} else {
- r = kvm_set_ioeventfd_pio_word(event_notifier_get_fd(notifier),
- proxy->addr + VIRTIO_PCI_QUEUE_NOTIFY,
- n, assign);
- if (r < 0) {
- return r;
- }
- event_notifier_cleanup(notifier);
+ return virtio_pci_set_host_notifier_ioeventfd(proxy, n, assign);
}
- return r;
}
static const VirtIOBindings virtio_pci_bindings = {
@@ -702,6 +789,8 @@ static PCIDeviceInfo virtio_info[] = {
.qdev.props = (Property[]) {
DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, block),
+ DEFINE_PROP_UINT32("flags", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD),
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features),
DEFINE_PROP_END_OF_LIST(),
@@ -714,6 +803,8 @@ static PCIDeviceInfo virtio_info[] = {
.exit = virtio_net_exit_pci,
.romfile = "pxe-virtio.bin",
.qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("flags", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD),
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3),
DEFINE_VIRTIO_NET_FEATURES(VirtIOPCIProxy, host_features),
DEFINE_NIC_PROPERTIES(VirtIOPCIProxy, nic),
@@ -582,6 +582,11 @@ void virtio_queue_notify(VirtIODevice *vdev, int n)
}
}
+void virtio_queue_notify_vq(VirtQueue *vq)
+{
+ virtio_queue_notify(vq->vdev, vq - vq->vdev->vq);
+}
+
uint16_t virtio_queue_vector(VirtIODevice *vdev, int n)
{
return n < VIRTIO_PCI_QUEUE_MAX ? vdev->vq[n].vector :
@@ -219,5 +219,6 @@ void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx);
VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n);
EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq);
EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq);
+void virtio_queue_notify_vq(VirtQueue *vq);
void virtio_irq(VirtQueue *vq);
#endif