summaryrefslogtreecommitdiff
path: root/hw/vhost.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/vhost.c')
-rw-r--r--hw/vhost.c112
1 files changed, 94 insertions, 18 deletions
diff --git a/hw/vhost.c b/hw/vhost.c
index 4e1cb47418..cee8aad4a1 100644
--- a/hw/vhost.c
+++ b/hw/vhost.c
@@ -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,17 +907,53 @@ 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)
{
int i, r;
+
+ hdev->started = true;
+
if (!vdev->binding->set_guest_notifiers) {
fprintf(stderr, "binding does not support guest notifiers\n");
r = -ENOSYS;
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;
@@ -895,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);
@@ -916,22 +990,22 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
}
}
- hdev->started = true;
-
return 0;
fail_log:
fail_vq:
while (--i >= 0) {
- vhost_virtqueue_cleanup(hdev,
+ vhost_virtqueue_stop(hdev,
vdev,
hdev->vqs + i,
i);
}
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:
+
+ hdev->started = false;
return r;
}
@@ -941,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);
@@ -950,7 +1024,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);