virtio-pci: msix masking optimizations
cover virtio-pci: msix masking optimizations
This patchset implements two msix masking optimizations. It works fine for me but I did not have the time to do performance testing yet so I do not know whether it helps and which workloads.
Sending out now as I'll be on vacation for a week. Please review and comment.
Thanks!
Michael S. Tsirkin (8):
virtio: don't waste irqfds on control vqs
msix: add api to access msix message
kvm: add stub for update msi route
virtio-pci: cache msix messages
virtio: backend virtqueue notifier masking
virtio-net: set/clear vhost_started in reverse order
vhost: set started flag while start is in progress
vhost: backend masking support
hw/pci/msix.c | 2 +- hw/pci/msix.h | 1 + hw/vhost.c | 112 ++++++++++++++++++++++++++----- hw/vhost.h | 10 +++ hw/vhost_net.c | 27 +++++++- hw/vhost_net.h | 3 + hw/virtio-net.c | 22 +++++- hw/virtio-pci.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++-------- hw/virtio-pci.h | 2 + hw/virtio.h | 15 ++++- kvm-stub.c | 5 ++ 11 files changed, 350 insertions(+), 52 deletions(-)
Signature · 1 line
MST
PATCH 1/8 virtio: don't waste irqfds on control vqs
Pass nvqs to set_guest_notifiers. This makes it possible to save on irqfds by not allocating one for the control vq for virtio-net.
--- hw/vhost.c | 10 +++++++--- hw/virtio-pci.c | 19 ++++++++++++++----- hw/virtio-pci.h | 1 + hw/virtio.h | 2 +- 4 files changed, 23 insertions(+), 9 deletions(-)
hw/vhost.c +7 -3
@@ -879,7 +879,9 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) goto fail; } - r = vdev->binding->set_guest_notifiers (vdev->binding_opaque, true); + r = vdev->binding->set_guest_notifiers (vdev->binding_opaque, + hdev->nvqs, + true);if (r <0 ) {fprintf (stderr,"Error binding guest notifier: %d\n" , -r); goto fail_notifiers;
@@ -929,7 +931,7 @@ fail_vq: } fail_mem: fail_features: - vdev->binding->set_guest_notifiers (vdev->binding_opaque, false); + vdev->binding->set_guest_notifiers (vdev->binding_opaque, hdev->nvqs, false); fail_notifiers: fail:return r;
@@ -950,7 +952,9 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev)vhost_sync_dirty_bitmap (hdev, &hdev->mem_sections[i],0 , (hwaddr )~0x0ull ); } - r = vdev->binding->set_guest_notifiers (vdev->binding_opaque, false); + r = vdev->binding->set_guest_notifiers (vdev->binding_opaque, + hdev->nvqs, + false);if (r <0 ) {fprintf (stderr,"vhost guest notifier cleanup failed: %d\n" , r);fflush (stderr);
hw/virtio-pci.c +14 -5
@@ -535,7 +535,7 @@ static int kvm_virtio_pci_vector_use(PCIDevice *dev, unsigned vector,VirtIODevice *vdev = proxy->vdev;int ret, queue_no; -for (queue_no =0 ; queue_no <VIRTIO_PCI_QUEUE_MAX ; queue_no++) { +for (queue_no =0 ; queue_no < proxy->nvqs_with_notifiers; queue_no++) {if (!virtio_queue_get_num (vdev, queue_no)) {break ; }
@@ -565,7 +565,7 @@ static void kvm_virtio_pci_vector_release(PCIDevice *dev, unsigned vector)VirtIODevice *vdev = proxy->vdev;int queue_no; -for (queue_no =0 ; queue_no <VIRTIO_PCI_QUEUE_MAX ; queue_no++) { +for (queue_no =0 ; queue_no < proxy->nvqs_with_notifiers; queue_no++) {if (!virtio_queue_get_num (vdev, queue_no)) {break ; }
@@ -587,7 +587,7 @@ static void kvm_virtio_pci_vector_poll(PCIDevice *dev,EventNotifier *notifier;VirtQueue *vq; -for (queue_no =0 ; queue_no <VIRTIO_PCI_QUEUE_MAX ; queue_no++) { +for (queue_no =0 ; queue_no < proxy->nvqs_with_notifiers; queue_no++) {if (!virtio_queue_get_num (vdev, queue_no)) {break ; }
@@ -631,7 +631,7 @@ static bool virtio_pci_query_guest_notifiers(DeviceState *d)return msix_enabled (&proxy->pci_dev); } -static int virtio_pci_set_guest_notifiers (DeviceState *d,bool assign) +static int virtio_pci_set_guest_notifiers (DeviceState *d,int nvqs,bool assign) {VirtIOPCIProxy *proxy =to_virtio_pci_proxy (d);VirtIODevice *vdev = proxy->vdev;
@@ -639,6 +639,15 @@ static int virtio_pci_set_guest_notifiers(DeviceState *d, bool assign)bool with_irqfd =msix_enabled (&proxy->pci_dev) &&kvm_msi_via_irqfd_enabled (); + nvqs =MIN (nvqs,VIRTIO_PCI_QUEUE_MAX ); + +/* When deassigning, pass a consistent nvqs value +* to avoid leaking notifiers. +*/ +assert (assign || nvqs == proxy->nvqs_with_notifiers); + + proxy->nvqs_with_notifiers = nvqs; +/* Must unset vector notifier while guest notifier is still assigned */ if (proxy->vector_irqfd && !assign) {msix_unset_vector_notifiers (&proxy->pci_dev);
@@ -646,7 +655,7 @@ static int virtio_pci_set_guest_notifiers(DeviceState *d, bool assign) proxy->vector_irqfd =NULL ; } -for (n =0 ; n <VIRTIO_PCI_QUEUE_MAX ; n++) { +for (n =0 ; n < nvqs; n++) {if (!virtio_queue_get_num (vdev, n)) {break ; }
hw/virtio-pci.h +1 -0
@@ -51,6 +51,7 @@ typedef struct {bool ioeventfd_disabled;bool ioeventfd_started;VirtIOIRQFD *vector_irqfd; +int nvqs_with_notifiers; } VirtIOPCIProxy;void virtio_init_pci (VirtIOPCIProxy *proxy,VirtIODevice *vdev);
hw/virtio.h +1 -1
@@ -99,7 +99,7 @@ typedef struct {int (*load_done)(DeviceState *d,QEMUFile *f);unsigned (*get_features)(DeviceState *d);bool (*query_guest_notifiers)(DeviceState *d); -int (*set_guest_notifiers)(DeviceState *d,bool assigned); +int (*set_guest_notifiers)(DeviceState *d,int nvqs,bool assigned);int (*set_host_notifier)(DeviceState *d,int n,bool assigned);void (*vmstate_change)(DeviceState *d,bool running); } VirtIOBindings;
Signature · 1 line
MST
PATCH 2/8 msix: add api to access msix message
In reply to Michael S. TsirkinWill be used by virtio pci.
--- hw/pci/msix.c | 2 +- hw/pci/msix.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-)
hw/pci/msix.c +1 -1
@@ -27,7 +27,7 @@#define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8)#define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8) -static MSIMessage msix_get_message (PCIDevice *dev,unsigned vector) +MSIMessage msix_get_message (PCIDevice *dev,unsigned vector) {uint8_t *table_entry = dev->msix_table + vector *PCI_MSIX_ENTRY_SIZE ;MSIMessage msg;
hw/pci/msix.h +1 -0
@@ -5,6 +5,7 @@#include "hw/pci/pci.h" void msix_set_message (PCIDevice *dev,int vector,MSIMessage msg); +MSIMessage msix_get_message (PCIDevice *dev,unsigned int vector); int msix_init(PCIDevice *dev, unsigned short nentries, MemoryRegion *table_bar, uint8_t table_bar_nr, unsigned table_offset, MemoryRegion *pba_bar,
Signature · 1 line
MST
PATCH 4/8 virtio-pci: cache msix messages
In reply to Michael S. TsirkinSome guests mask a vector then unmask without changing it. Store vectors to avoid kvm system calls in this case.
--- hw/virtio-pci.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++---------- hw/virtio-pci.h | 1 + 2 files changed, 100 insertions(+), 20 deletions(-)
hw/virtio-pci.c +99 -20
@@ -487,8 +487,6 @@ static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy, unsigned int vector, MSIMessage msg) { -VirtQueue *vq =virtio_get_queue (proxy->vdev, queue_no); -EventNotifier *n =virtio_queue_get_guest_notifier (vq);VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];int ret;
@@ -500,18 +498,94 @@ static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy, irqfd->virq = ret; } irqfd->users++; +return 0 ; +} - ret =kvm_irqchip_add_irqfd_notifier (kvm_state, n, irqfd->virq); -if (ret <0 ) { -if (--irqfd->users ==0 ) { -kvm_irqchip_release_virq (kvm_state, irqfd->virq); +static void kvm_virtio_pci_vq_vector_release (VirtIOPCIProxy *proxy, +unsigned int vector) +{ +VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; +if (--irqfd->users ==0 ) { +kvm_irqchip_release_virq (kvm_state, irqfd->virq); + } +} + +static int kvm_virtio_pci_vector_use (VirtIOPCIProxy *proxy,int nvqs) +{ +PCIDevice *dev = &proxy->pci_dev; +VirtIODevice *vdev = proxy->vdev; +unsigned int vector; +int ret, queue_no; +MSIMessage msg; + +for (queue_no =0 ; queue_no < nvqs; queue_no++) { +if (!virtio_queue_get_num (vdev, queue_no)) { +break ; + } + vector =virtio_queue_vector (vdev, queue_no); +if (vector >=msix_nr_vectors_allocated (dev)) { +continue ; + } + msg =msix_get_message (dev, vector); + ret =kvm_virtio_pci_vq_vector_use (proxy, queue_no, vector, msg); +if (ret <0 ) { + goto undo; } -return ret; }return 0 ; + +undo: +while (--queue_no >=0 ) { + vector =virtio_queue_vector (vdev, queue_no); +if (vector >=msix_nr_vectors_allocated (dev)) { +continue ; + } +kvm_virtio_pci_vq_vector_release (proxy, vector); + } +return ret; } -static void kvm_virtio_pci_vq_vector_release (VirtIOPCIProxy *proxy, +static void kvm_virtio_pci_vector_release (VirtIOPCIProxy *proxy,int nvqs) +{ +PCIDevice *dev = &proxy->pci_dev; +VirtIODevice *vdev = proxy->vdev; +unsigned int vector; +int queue_no; + +for (queue_no =0 ; queue_no < nvqs; queue_no++) { +if (!virtio_queue_get_num (vdev, queue_no)) { +break ; + } + vector =virtio_queue_vector (vdev, queue_no); +if (vector >=msix_nr_vectors_allocated (dev)) { +continue ; + } +kvm_virtio_pci_vq_vector_release (proxy, vector); + } +} + +static int kvm_virtio_pci_vq_vector_unmask (VirtIOPCIProxy *proxy, +unsigned int queue_no, +unsigned int vector, +MSIMessage msg) +{ +VirtQueue *vq =virtio_get_queue (proxy->vdev, queue_no); +EventNotifier *n =virtio_queue_get_guest_notifier (vq); +VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; +int ret; + +if (irqfd->msg.data != msg.data || irqfd->msg.address != msg.address) { + ret =kvm_irqchip_update_msi_route (kvm_state, irqfd->virq, msg); +if (ret <0 ) { +return ret; + } + } + + ret =kvm_irqchip_add_irqfd_notifier (kvm_state, n, irqfd->virq); +return ret; +} + +static void kvm_virtio_pci_vq_vector_mask (VirtIOPCIProxy *proxy,unsigned int queue_no,unsigned int vector) {
@@ -522,13 +596,9 @@ static void kvm_virtio_pci_vq_vector_release(VirtIOPCIProxy *proxy, ret =kvm_irqchip_remove_irqfd_notifier (kvm_state, n, irqfd->virq);assert (ret ==0 ); - -if (--irqfd->users ==0 ) { -kvm_irqchip_release_virq (kvm_state, irqfd->virq); - } } -static int kvm_virtio_pci_vector_use (PCIDevice *dev,unsigned vector, +static int kvm_virtio_pci_vector_unmask (PCIDevice *dev,unsigned vector,MSIMessage msg) {VirtIOPCIProxy *proxy =container_of (dev, VirtIOPCIProxy, pci_dev);
@@ -542,7 +612,7 @@ static int kvm_virtio_pci_vector_use(PCIDevice *dev, unsigned vector,if (virtio_queue_vector (vdev, queue_no) != vector) {continue ; } - ret =kvm_virtio_pci_vq_vector_use (proxy, queue_no, vector, msg); + ret =kvm_virtio_pci_vq_vector_unmask (proxy, queue_no, vector, msg);if (ret <0 ) { goto undo; }
@@ -554,12 +624,12 @@ undo:if (virtio_queue_vector (vdev, queue_no) != vector) {continue ; } -kvm_virtio_pci_vq_vector_release (proxy, queue_no, vector); +kvm_virtio_pci_vq_vector_mask (proxy, queue_no, vector); }return ret; } -static void kvm_virtio_pci_vector_release (PCIDevice *dev,unsigned vector) +static void kvm_virtio_pci_vector_mask (PCIDevice *dev,unsigned vector) {VirtIOPCIProxy *proxy =container_of (dev, VirtIOPCIProxy, pci_dev);VirtIODevice *vdev = proxy->vdev;
@@ -572,7 +642,7 @@ static void kvm_virtio_pci_vector_release(PCIDevice *dev, unsigned vector)if (virtio_queue_vector (vdev, queue_no) != vector) {continue ; } -kvm_virtio_pci_vq_vector_release (proxy, queue_no, vector); +kvm_virtio_pci_vq_vector_mask (proxy, queue_no, vector); } }
@@ -651,6 +721,7 @@ static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign)/* Must unset vector notifier while guest notifier is still assigned */ if (proxy->vector_irqfd && !assign) {msix_unset_vector_notifiers (&proxy->pci_dev); +kvm_virtio_pci_vector_release (proxy, nvqs);g_free (proxy->vector_irqfd); proxy->vector_irqfd =NULL ; }
@@ -672,17 +743,25 @@ static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign) proxy->vector_irqfd =g_malloc0 (sizeof (*proxy->vector_irqfd) *msix_nr_vectors_allocated (&proxy->pci_dev)); + r =kvm_virtio_pci_vector_use (proxy, nvqs); +if (r <0 ) { + goto assign_error; + } r =msix_set_vector_notifiers (&proxy->pci_dev, - kvm_virtio_pci_vector_use, - kvm_virtio_pci_vector_release, + kvm_virtio_pci_vector_unmask, + kvm_virtio_pci_vector_mask, kvm_virtio_pci_vector_poll);if (r <0 ) { - goto assign_error; + goto notifiers_error; } }return 0 ; +notifiers_error: +assert (assign); +kvm_virtio_pci_vector_release (proxy, nvqs); + assign_error:/* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */ assert (assign);
hw/virtio-pci.h +1 -0
@@ -27,6 +27,7 @@#define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT)typedef struct { +MSIMessage msg;int virq;unsigned int users; }VirtIOIRQFD ;
Signature · 1 line
MST
PATCH 3/8 kvm: add stub for update msi route
In reply to Michael S. TsirkinWill be used by virtio-pci.
--- kvm-stub.c | 5 +++++ 1 file changed, 5 insertions(+)
kvm-stub.c +5 -0
@@ -131,6 +131,11 @@ void kvm_irqchip_release_virq(KVMState *s, int virq) { } +int kvm_irqchip_update_msi_route (KVMState *s,int virq,MSIMessage msg) +{ +return -ENOSYS ; +} +int kvm_irqchip_add_irqfd_notifier (KVMState *s,EventNotifier *n,int virq) {return -ENOSYS ;
Signature · 1 line
MST
PATCH 5/8 virtio: backend virtqueue notifier masking
In reply to Michael S. Tsirkinsome backends (notably vhost) can mask events
at their source in a way that is more efficient than masking through kvm.
Specifically
- masking in kvm uses rcu write side so it has high latency
- in kvm on unmask we always send an interrupt masking at source does not have these issues.
Add such support in virtio.h and use in virtio-pci.
--- hw/virtio-pci.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++------- hw/virtio.h | 13 ++++++++++ 2 files changed, 83 insertions(+), 9 deletions(-)
hw/virtio-pci.c +70 -9
@@ -510,6 +510,31 @@ static void kvm_virtio_pci_vq_vector_release(VirtIOPCIProxy *proxy, } } +static int kvm_virtio_pci_irqfd_use (VirtIOPCIProxy *proxy, +unsigned int queue_no, +unsigned int vector) +{ +VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; +VirtQueue *vq =virtio_get_queue (proxy->vdev, queue_no); +EventNotifier *n =virtio_queue_get_guest_notifier (vq); +int ret; + ret =kvm_irqchip_add_irqfd_notifier (kvm_state, n, irqfd->virq); +return ret; +} + +static void kvm_virtio_pci_irqfd_release (VirtIOPCIProxy *proxy, +unsigned int queue_no, +unsigned int vector) +{ +VirtQueue *vq =virtio_get_queue (proxy->vdev, queue_no); +EventNotifier *n =virtio_queue_get_guest_notifier (vq); +VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; +int ret; + + ret =kvm_irqchip_remove_irqfd_notifier (kvm_state, n, irqfd->virq); +assert (ret ==0 ); +} +static int kvm_virtio_pci_vector_use (VirtIOPCIProxy *proxy,int nvqs) {PCIDevice *dev = &proxy->pci_dev;
@@ -531,6 +556,16 @@ static int kvm_virtio_pci_vector_use(VirtIOPCIProxy *proxy, int nvqs)if (ret <0 ) { goto undo; } +/* If guest supports masking, set up irqfd now. +* Otherwise, delay until unmasked in the frontend. +*/ +if (proxy->vdev->guest_notifier_mask) { + ret =kvm_virtio_pci_irqfd_use (proxy, queue_no, vector); +if (ret <0 ) { +kvm_virtio_pci_vq_vector_release (proxy, vector); + goto undo; + } + } }return 0 ;
@@ -540,6 +575,9 @@ undo:if (vector >=msix_nr_vectors_allocated (dev)) {continue ; } +if (proxy->vdev->guest_notifier_mask) { +kvm_virtio_pci_irqfd_release (proxy, vector, queue_no); + }kvm_virtio_pci_vq_vector_release (proxy, vector); }return ret;
@@ -560,6 +598,12 @@ static void kvm_virtio_pci_vector_release(VirtIOPCIProxy *proxy, int nvqs)if (vector >=msix_nr_vectors_allocated (dev)) {continue ; } +/* If guest supports masking, clean up irqfd now. +* Otherwise, it was cleaned when masked in the frontend. +*/ +if (proxy->vdev->guest_notifier_mask) { +kvm_virtio_pci_irqfd_release (proxy, vector, queue_no); + }kvm_virtio_pci_vq_vector_release (proxy, vector); } }
@@ -581,7 +625,19 @@ static int kvm_virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy, } } - ret =kvm_irqchip_add_irqfd_notifier (kvm_state, n, irqfd->virq); +/* If guest supports masking, irqfd is already setup, unmask it. +* Otherwise, set it up now. +*/ +if (proxy->vdev->guest_notifier_mask) { + proxy->vdev->guest_notifier_mask (proxy->vdev, queue_no, false); +/* Test after unmasking to avoid losing events. */ +if (proxy->vdev->guest_notifier_pending && + proxy->vdev->guest_notifier_pending (proxy->vdev, queue_no)) { +event_notifier_set (n); + } + }else { + ret =kvm_virtio_pci_irqfd_use (proxy, queue_no, vector); + }return ret; }
@@ -589,13 +645,14 @@ static void kvm_virtio_pci_vq_vector_mask(VirtIOPCIProxy *proxy, unsigned int queue_no, unsigned int vector) { - VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no); -EventNotifier *n =virtio_queue_get_guest_notifier (vq); -VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; -int ret; - - ret =kvm_irqchip_remove_irqfd_notifier (kvm_state, n, irqfd->virq); -assert (ret ==0 ); +/* If guest supports masking, keep irqfd but mask it. +* Otherwise, clean it up now. +*/ +if (proxy->vdev->guest_notifier_mask) { + proxy->vdev->guest_notifier_mask (proxy->vdev, queue_no, true); + }else { +kvm_virtio_pci_irqfd_release (proxy, vector, queue_no); + } } static int kvm_virtio_pci_vector_unmask(PCIDevice *dev, unsigned vector,
@@ -668,7 +725,11 @@ static void kvm_virtio_pci_vector_poll(PCIDevice *dev, } vq =virtio_get_queue (vdev, queue_no); notifier =virtio_queue_get_guest_notifier (vq); -if (event_notifier_test_and_clear (notifier)) { +if (vdev->guest_notifier_pending) { +if (vdev->guest_notifier_pending (vdev, queue_no)) { +msix_set_pending (dev, vector); + } + }else if (event_notifier_test_and_clear (notifier)) {msix_set_pending (dev, vector); } }
hw/virtio.h +13 -0
@@ -126,6 +126,19 @@ struct VirtIODevicevoid (*set_config)(VirtIODevice *vdev,const uint8_t *config);void (*reset)(VirtIODevice *vdev);void (*set_status)(VirtIODevice *vdev,uint8_t val); +/* Test and clear event pending status. +* Should be called after unmask to avoid losing events. +* If backend does not support masking, +* must check in frontend instead. +*/ +bool (*guest_notifier_pending)(VirtIODevice *vdev,int n); +/* Mask/unmask events from this vq. Any events reported +* while masked will become pending. +* If backend does not support masking, +* must mask in frontend instead. +*/ +void (*guest_notifier_mask)(VirtIODevice *vdev,int n,bool mask); +VirtQueue *vq;const VirtIOBindings *binding;DeviceState *binding_opaque;
Signature · 1 line
MST
PATCH 8/8 vhost: backend masking support
In reply to Michael S. TsirkinSupport backend guest notifier masking in vhost-net:
create eventfd at device init, when masked, make vhost use that as eventfd instead of sending an interrupt.
--- hw/vhost.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++-------- hw/vhost.h | 10 ++++++ hw/vhost_net.c | 27 ++++++++++++++-- hw/vhost_net.h | 3 ++ hw/virtio-net.c | 18 +++++++++++ 5 files changed, 137 insertions(+), 16 deletions(-)
hw/vhost.c +82 -13
@@ -612,7 +612,7 @@ static void vhost_log_stop(MemoryListener *listener,/* FIXME: implement */ } -static int vhost_virtqueue_init (struct vhost_dev *dev, +static int vhost_virtqueue_start (struct vhost_dev *dev,struct VirtIODevice *vdev,struct vhost_virtqueue *vq,unsigned idx)
@@ -681,16 +681,11 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, goto fail_kick; } - file.fd =event_notifier_get_fd (virtio_queue_get_guest_notifier (vvq)); - r =ioctl (dev->control,VHOST_SET_VRING_CALL , &file); -if (r) { - r = -errno; - goto fail_call; - } +/* Clear and discard previous events if any. */ +event_notifier_test_and_clear (&vq->masked_notifier);return 0 ; -fail_call: fail_kick: fail_alloc: cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx),
@@ -708,7 +703,7 @@ fail_alloc_desc:return r; } -static void vhost_virtqueue_cleanup (struct vhost_dev *dev, +static void vhost_virtqueue_stop (struct vhost_dev *dev,struct VirtIODevice *vdev,struct vhost_virtqueue *vq,unsigned idx)
@@ -746,11 +741,39 @@ static void vhost_eventfd_del(MemoryListener *listener, { } +static int vhost_virtqueue_init (struct vhost_dev *dev, +struct vhost_virtqueue *vq,int n) +{ +struct vhost_vring_file file = { + .index = n, + }; +int r =event_notifier_init (&vq->masked_notifier,0 ); +if (r <0 ) { +return r; + } + + file.fd =event_notifier_get_fd (&vq->masked_notifier); + r =ioctl (dev->control,VHOST_SET_VRING_CALL , &file); +if (r) { + r = -errno; + goto fail_call; + } +return 0 ; +fail_call: +event_notifier_cleanup (&vq->masked_notifier); +return r; +} + +static void vhost_virtqueue_cleanup (struct vhost_virtqueue *vq) +{ +event_notifier_cleanup (&vq->masked_notifier); +} +int vhost_dev_init (struct vhost_dev *hdev,int devfd,const char *devpath,bool force) {uint64_t features; -int r; +int i, r;if (devfd >=0 ) { hdev->control = devfd; }else {
@@ -768,6 +791,13 @@ int vhost_dev_init(struct vhost_dev *hdev, int devfd, const char *devpath,if (r <0 ) { goto fail; } + +for (i =0 ; i < hdev->nvqs; ++i) { + r =vhost_virtqueue_init (hdev, hdev->vqs + i, i); +if (r <0 ) { + goto fail_vq; + } + } hdev->features = features; hdev->memory_listener = (MemoryListener ) {
@@ -795,6 +825,10 @@ int vhost_dev_init(struct vhost_dev *hdev, int devfd, const char *devpath,memory_listener_register (&hdev->memory_listener, &address_space_memory); hdev->force = force;return 0 ; +fail_vq: +while (--i >=0 ) { +vhost_virtqueue_cleanup (hdev->vqs + i); + } fail: r = -errno;close (hdev->control);
@@ -803,6 +837,10 @@ fail:void vhost_dev_cleanup (struct vhost_dev *hdev) { +int i; +for (i =0 ; i < hdev->nvqs; ++i) { +vhost_virtqueue_cleanup (hdev->vqs + i); + }memory_listener_unregister (&hdev->memory_listener);g_free (hdev->mem);g_free (hdev->mem_sections);
@@ -869,6 +907,37 @@ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) } } +/* Test and clear event pending status. +* Should be called after unmask to avoid losing events. +*/ +bool vhost_virtqueue_pending (struct vhost_dev *hdev,int n) +{ +struct vhost_virtqueue *vq = hdev->vqs + n; +assert (hdev->started); +return event_notifier_test_and_clear (&vq->masked_notifier); +} + +/* Mask/unmask events from this vq. */ +void vhost_virtqueue_mask (struct vhost_dev *hdev,VirtIODevice *vdev,int n, +bool mask) +{ +struct VirtQueue *vvq =virtio_get_queue (vdev, n); +int r; + +assert (hdev->started); + +struct vhost_vring_file file = { + .index = n, + }; +if (mask) { + file.fd =event_notifier_get_fd (&hdev->vqs[n].masked_notifier); + }else { + file.fd =event_notifier_get_fd (virtio_queue_get_guest_notifier (vvq)); + } + r =ioctl (hdev->control,VHOST_SET_VRING_CALL , &file); +assert (r >=0 ); +} +/* Host notifiers must be enabled at this point. */ int vhost_dev_start (struct vhost_dev *hdev,VirtIODevice *vdev) {
@@ -900,7 +969,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) goto fail_mem; }for (i =0 ; i < hdev->nvqs; ++i) { - r =vhost_virtqueue_init (hdev, + r =vhost_virtqueue_start (hdev, vdev, hdev->vqs + i, i);
@@ -925,7 +994,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) fail_log: fail_vq:while (--i >=0 ) { -vhost_virtqueue_cleanup (hdev, +vhost_virtqueue_stop (hdev, vdev, hdev->vqs + i, i);
@@ -946,7 +1015,7 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev)int i, r;for (i =0 ; i < hdev->nvqs; ++i) { -vhost_virtqueue_cleanup (hdev, +vhost_virtqueue_stop (hdev, vdev, hdev->vqs + i, i);
hw/vhost.h +10 -0
@@ -18,6 +18,7 @@ struct vhost_virtqueue {void *ring;unsigned long long ring_phys;unsigned ring_size; +EventNotifier masked_notifier; };typedef unsigned long vhost_log_chunk_t ;
@@ -53,4 +54,13 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev);int vhost_dev_enable_notifiers (struct vhost_dev *hdev,VirtIODevice *vdev);void vhost_dev_disable_notifiers (struct vhost_dev *hdev,VirtIODevice *vdev); +/* Test and clear masked event pending status. +* Should be called after unmask to avoid losing events. +*/ +bool vhost_virtqueue_pending (struct vhost_dev *hdev,int n); + +/* Mask/unmask events from this vq. +*/ +void vhost_virtqueue_mask (struct vhost_dev *hdev,VirtIODevice *vdev,int n, +bool mask);#endif
hw/vhost_net.c +24 -3
@@ -109,6 +109,9 @@ struct vhost_net *vhost_net_init(NetClientState *backend, int devfd, (1 <<VHOST_NET_F_VIRTIO_NET_HDR ); net->backend = r; + net->dev.nvqs =2 ; + net->dev.vqs = net->vqs; + r =vhost_dev_init (&net->dev, devfd,"/dev/vhost-net" , force);if (r <0 ) { goto fail;
@@ -143,9 +146,6 @@ int vhost_net_start(struct vhost_net *net,struct vhost_vring_file file = { };int r; - net->dev.nvqs =2 ; - net->dev.vqs = net->vqs; - r =vhost_dev_enable_notifiers (&net->dev, dev);if (r <0 ) { goto fail_notifiers;
@@ -200,6 +200,17 @@ void vhost_net_cleanup(struct vhost_net *net)vhost_dev_cleanup (&net->dev); g_free(net); } + +bool vhost_net_virtqueue_pending (VHostNetState *net,int idx) +{ +return vhost_virtqueue_pending (&net->dev, idx); +} + +void vhost_net_virtqueue_mask (VHostNetState *net,VirtIODevice *dev, +int idx,bool mask) +{ +vhost_virtqueue_mask (&net->dev, dev, idx, mask); +}#else struct vhost_net *vhost_net_init (NetClientState *backend,int devfd,bool force)
@@ -234,4 +245,14 @@ unsigned vhost_net_get_features(struct vhost_net *net, unsigned features)void vhost_net_ack_features (struct vhost_net *net,unsigned features) { } + +bool vhost_net_virtqueue_pending (VHostNetState *net,int idx) +{ +return -ENOSYS ; +} + +void vhost_net_virtqueue_mask (VHostNetState *net,VirtIODevice *dev, +int idx,bool mask) +{ +}#endif
hw/vhost_net.h +3 -0
@@ -17,4 +17,7 @@ void vhost_net_cleanup(VHostNetState *net);unsigned vhost_net_get_features (VHostNetState *net,unsigned features);void vhost_net_ack_features (VHostNetState *net,unsigned features); +bool vhost_net_virtqueue_pending (VHostNetState *net,int n); +void vhost_net_virtqueue_mask (VHostNetState *net,VirtIODevice *dev, +int idx,bool mask);#endif
hw/virtio-net.c +18 -0
@@ -1010,6 +1010,22 @@ static NetClientInfo net_virtio_info = { .link_status_changed = virtio_net_set_link_status, }; +static bool virtio_net_guest_notifier_pending (VirtIODevice *vdev,int idx) +{ +VirtIONet *n =to_virtio_net (vdev); +assert (n->vhost_started); +return vhost_net_virtqueue_pending (tap_get_vhost_net (n->nic->nc.peer), idx); +} + +static void virtio_net_guest_notifier_mask (VirtIODevice *vdev,int idx, +bool mask) +{ +VirtIONet *n =to_virtio_net (vdev); +assert (n->vhost_started); +vhost_net_virtqueue_mask (tap_get_vhost_net (n->nic->nc.peer), + vdev, idx, mask); +} +VirtIODevice *virtio_net_init (DeviceState *dev,NICConf *conf,virtio_net_conf *net) {
@@ -1026,6 +1042,8 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, n->vdev.bad_features = virtio_net_bad_features; n->vdev.reset = virtio_net_reset; n->vdev.set_status = virtio_net_set_status; + n->vdev.guest_notifier_mask = virtio_net_guest_notifier_mask; + n->vdev.guest_notifier_pending = virtio_net_guest_notifier_pending; n->rx_vq =virtio_add_queue (&n->vdev,256 , virtio_net_handle_rx);if (net->tx &&strcmp (net->tx,"timer" ) &&strcmp (net->tx,"bh" )) {
Signature · 1 line
MST
PATCH 6/8 virtio-net: set/clear vhost_started in reverse order
In reply to Michael S. TsirkinAs vhost started is cleared last thing on stop, set it first things on start. This makes it possible to use vhost_started while start is in progress which is used by follow-up patches.
--- hw/virtio-net.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
hw/virtio-net.c +2 -2
@@ -126,12 +126,12 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)if (!vhost_net_query (tap_get_vhost_net (n->nic->nc.peer), &n->vdev)) {return ; } + n->vhost_started =1 ; r =vhost_net_start (tap_get_vhost_net (n->nic->nc.peer), &n->vdev);if (r <0 ) {error_report ("unable to start vhost net: %d: " "falling back on userspace virtio" , -r); - }else { - n->vhost_started =1 ; + n->vhost_started =0 ; } }else {vhost_net_stop (tap_get_vhost_net (n->nic->nc.peer), &n->vdev);
Signature · 1 line
MST
PATCH 7/8 vhost: set started flag while start is in progress
In reply to Michael S. TsirkinThis makes it possible to use started flag for sanity checking of callbacks that happen during start/stop.
--- hw/vhost.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
hw/vhost.c +5 -2
@@ -873,6 +873,9 @@ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)int vhost_dev_start (struct vhost_dev *hdev,VirtIODevice *vdev) {int i, r; + + hdev->started = true; +if (!vdev->binding->set_guest_notifiers) {fprintf (stderr,"binding does not support guest notifiers\n" ); r = -ENOSYS ;
@@ -918,8 +921,6 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) } } - hdev->started = true; -return 0 ; fail_log: fail_vq:
@@ -934,6 +935,8 @@ fail_features: vdev->binding->set_guest_notifiers (vdev->binding_opaque, hdev->nvqs, false); fail_notifiers: fail: + + hdev->started = false;return r; }
Signature · 1 line
MST
Re: [Qemu-devel] [PATCH 1/8] virtio: don't waste irqfds on control vqs
In reply to Michael S. TsirkinQuoted 11 lines · Michael S. Tsirkin
Pass nvqs to
set_guest_notifiers. This makes it possible to save on irqfds by not allocating one for the control vq for virtio-net.Signed-off-by Michael S. Tsirkin <[email protected]>--- hw/vhost.c | 10 +++++++--- hw/virtio-pci.c | 19 ++++++++++++++----- hw/virtio-pci.h | 1 + hw/virtio.h | 2 +- 4 files changed, 23 insertions(+), 9 deletions(-)
Good idea!
This patch assumes only the first n virtqueues need irqfd. Depending on the device's virtqueue ordering this may not work (e.g. control vq first followed by data vqs). But this is a theoretical point, let's wait until we hit it and need something fancier.
Stefan