To support reusing old buffers during resize.

This patch implements copying a buffer from the detached vring to the vq
where the new vring is attached.

This process is similar to virtqueue_add_split(), but skips DMA. Use
the function virtqueue_update_split() provided by the previous patch to
update the state of the vq.

Signed-off-by: Xuan Zhuo <[email protected]>
---
 drivers/virtio/virtio_ring.c | 60 ++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index aa85058978cb..167442cfdb2a 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -703,6 +703,66 @@ static inline int virtqueue_add_split(struct virtqueue 
*_vq,
        return -ENOMEM;
 }
 
+static u32 vring_copy_desc_split(struct vring_virtqueue *vq, u32 i,
+                                struct vring_virtqueue_split *vring,
+                                u32 src)
+{
+       struct vring_desc_extra *extra = vq->split.desc_extra;
+       struct vring_desc *desc = vq->split.vring.desc;
+       u16 next;
+
+       desc[i].flags = vring->vring.desc[src].flags;
+       desc[i].addr  = vring->vring.desc[src].addr;
+       desc[i].len   = vring->vring.desc[src].len;
+
+       next = extra[i].next;
+
+       desc[i].next = cpu_to_virtio16(vq->vq.vdev, next);
+
+       extra[i].addr  = vring->desc_extra[src].addr;
+       extra[i].len   = vring->desc_extra[src].len;
+       extra[i].flags = vring->desc_extra[src].flags;
+
+       return next;
+}
+
+static int vring_copy_to_vq_split(struct vring_virtqueue *vq,
+                                 struct vring_virtqueue_split *vring,
+                                 u32 old_index)
+{
+       __virtio16 nextflag = cpu_to_virtio16(vq->vq.vdev, VRING_DESC_F_NEXT);
+       struct vring_desc_state_split *state;
+       u32 i, num = 1, old_idx;
+
+       old_idx = old_index;
+       while (vring->vring.desc[old_idx].flags & nextflag) {
+               old_idx = vring->desc_extra[old_idx].next;
+               ++num;
+       }
+
+       if (num > vq->vq.num_free)
+               return -ENOSPC;
+
+       i = vq->free_head;
+
+       old_idx = old_index;
+       while (vring->vring.desc[old_idx].flags & nextflag) {
+               i = vring_copy_desc_split(vq, i, vring, old_idx);
+
+               old_idx = vring->desc_extra[old_idx].next;
+       }
+
+       i = vring_copy_desc_split(vq, i, vring, old_idx);
+
+       state = &vring->desc_state[old_index];
+
+       virtqueue_update_split(vq, num, i, state->indir_desc, state->data);
+
+       state->data = NULL;
+
+       return num;
+}
+
 static bool virtqueue_kick_prepare_split(struct virtqueue *_vq)
 {
        struct vring_virtqueue *vq = to_vvq(_vq);
-- 
2.31.0

_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/virtualization

Reply via email to