On error, virtio blk dirties guest memory but doesn't want to tell guest
about it. Add virtqueue_fill_partial to cover this use case.  This gets
two parameters: host_len is >= the amount of guest memory actually
written, guest_len is how much we guarantee to guest.

Cc: Paolo Bonzini <pbonz...@redhat.com>
Signed-off-by: Michael S. Tsirkin <m...@redhat.com>
---
 include/hw/virtio/virtio.h |  3 +++
 hw/virtio/virtio.c         | 25 ++++++++++++++++++++-----
 2 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index e3adb1d..9957aae 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -135,6 +135,9 @@ void virtio_del_queue(VirtIODevice *vdev, int n);
 void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem,
                     unsigned int len);
 void virtqueue_flush(VirtQueue *vq, unsigned int count);
+void virtqueue_fill_partial(VirtQueue *vq, const VirtQueueElement *elem,
+                            unsigned int host_len, unsigned int guest_len,
+                            unsigned int idx);
 void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
                     unsigned int len, unsigned int idx);
 
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 159e5c6..111b0db 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -241,17 +241,26 @@ int virtio_queue_empty(VirtQueue *vq)
     return vring_avail_idx(vq) == vq->last_avail_idx;
 }
 
-void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
-                    unsigned int len, unsigned int idx)
+/*
+ * Some devices dirty guest memory but don't want to tell guest about it. In
+ * that case, use virtqueue_fill_partial: host_len is >= the amount of guest
+ * memory actually written, guest_len is how much we guarantee to guest.
+ * If you know exactly how much was written, use virtqueue_fill instead.
+ */
+void virtqueue_fill_partial(VirtQueue *vq, const VirtQueueElement *elem,
+                            unsigned int host_len, unsigned int guest_len,
+                            unsigned int idx)
 {
     unsigned int offset;
     int i;
 
-    trace_virtqueue_fill(vq, elem, len, idx);
+    assert(host_len >= guest_len);
+
+    trace_virtqueue_fill(vq, elem, guest_len, idx);
 
     offset = 0;
     for (i = 0; i < elem->in_num; i++) {
-        size_t size = MIN(len - offset, elem->in_sg[i].iov_len);
+        size_t size = MIN(host_len - offset, elem->in_sg[i].iov_len);
 
         cpu_physical_memory_unmap(elem->in_sg[i].iov_base,
                                   elem->in_sg[i].iov_len,
@@ -269,7 +278,13 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement 
*elem,
 
     /* Get a pointer to the next entry in the used ring. */
     vring_used_ring_id(vq, idx, elem->index);
-    vring_used_ring_len(vq, idx, len);
+    vring_used_ring_len(vq, idx, guest_len);
+}
+
+void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
+                    unsigned int len, unsigned int idx)
+{
+    virtqueue_fill_partial(vq, elem, len, len, idx);
 }
 
 void virtqueue_flush(VirtQueue *vq, unsigned int count)
-- 
MST

Reply via email to