Re: [PATCH RFC 14/15] hw/virtio/vhost-user: handle data movement with shadow vqs

Connor Kite <[email protected]>
Newsgroups dev.linux.lists.virtio-fs,org.nongnu.qemu-devel
Message-ID <CA+spn3qLUh2CWoKMDV=jQrughZmZ9DgBE+gidbn1xmTxCqpNsQ@mail.gmail.com>
On Fri, Jul 24, 2026 at 8:20 AM Akihiko Odaki
<[email protected]> wrote:
>
>
> This copies data in the reversed direction.
>

Good catch!  This will be fixed going forward.

...


> > +    DMAMap needle;
> > +    hwaddr *iova_base;
>
> Please make it void *.
>

Got it!

> > +
> > +    for (int i = 0; i < elem->out_num; i++) {
> > +        needle.translated_addr = elem->out_addr[i];
> > +        needle.size = elem->out_sg[i].iov_len - 1;
> > +        map = vhost_iova_tree_find_gpa(svq->iova_tree, &needle);
>
> An error check is missing here. This translation does not seem to
> support IOMMU.
>

I have added in the error check.

Looking at vhost-vdpa, there is an
error when IOMMU is enabled at the same time as shadow virtqueues.
I'll need to dig into what, if any, work is needed to make svqs compatible
with IOMMU enabled.  For now, I can implement a similar check in
vhost_user_dev_start to that in vhost-vdpa.

> > +        offset = needle.translated_addr - map->translated_addr;
> > +        iova_base = (void *)(map->iova + offset);
> > +
> > +        elem->out_sg[i].iov_base = iova_base;
>
> elem->out_sg shouldn't be overwritten since virtqueue_unpop() will use
> it to unmap the element.
>
> > +        memcpy(iova_base, elem->out_sg[i].iov_base, needle.size + 1);
>
> This memcpy() is no-op. elem->out_sg[i].iov_base is already set to
> iova_base, so it's copying from iova_base to iova_base.
>

Roger!  There were a number of unnecessary adjustments to elem in
both handlers which should now be fixed.  As the IOVA and hva of the
isolation region are no longer the same (see Patch 11), there are some
additional changes required here, but the memcpy issues should also
be fixed for the next rev.

> > +    }
> > +
> > +    for (int i = 0; i < elem->in_num; i++) {
> > +        needle.translated_addr = elem->in_addr[i];
> > +        needle.size = elem->in_sg[i].iov_len - 1;
> > +        map = vhost_iova_tree_find_gpa(svq->iova_tree, &needle);
> > +        offset = needle.translated_addr - map->translated_addr;
> > +        iova_base = (void *)(map->iova + offset);
> > +
> > +        elem->in_sg[i].iov_base = iova_base;
> > +        memcpy(iova_base, elem->in_sg[i].iov_base, needle.size + 1);
> > +    }
> > +
> > +    vhost_svq_add(svq, elem->out_sg, elem->out_num, elem->out_addr,
> > +                  elem->in_sg, elem->in_num, elem->in_addr, elem);
>
> An error is ignored here too.
>

This is added now.

> > +
> > +    return 0;
> > +}
> > +
> > +

...

> > +    for (int i = 0; i < u->shadow_vqs->len; i++) {
> > +        VirtQueue *vq = virtio_get_queue(dev->vdev, dev->vq_index + i);
> > +        VhostShadowVirtqueue *svq = g_ptr_array_index(u->shadow_vqs, i);
> > +        svq->base_addr = (hwaddr *) vring_base;
> > +        vhost_svq_start(svq, dev->vdev, vq, u->iso_iova_tree);
>
> Calling vhost_svq_start() here overwrites the shadow vring index
> configured in vhost_virtqueue_start().
>

I might not understand what you are referring to here. If I understand
correctly,
vhost_virtqueue_start() does not touch any shadow vring indexes, so I
don't think
vhost_svq_start() could overwrite something configured there.

However, I see that the assignment to addr.index followed by
vhost_user_set_vring_addr
might create a disconnect between the vq index and that sent to the
backend.  It's possible
my ordering is just right as is, but I will confirm by assigning
virtio_get_queue_index(vq) to
addr.index

> > +
> > +        struct vhost_vring_addr addr = {
> > +            .index = dev->vq_index + i,
> > +            .desc_user_addr = vring_base,
> > +            .avail_user_addr = vring_base + sizeof(vring_desc_t) *
> > +                svq->vring.num,
>
> This assumes the ring is split, but VIRTIO_F_RING_PACKED is not rejected.
>

Currently, shadow virtqueue does not support packed vrings, so I can't here.  It
is a good idea to add a check and reject VIRTIO_F_RING_PACKED.

> > +            .used_user_addr = vring_base + vhost_svq_driver_area_size(svq)
> > +        };
> > +
> > +        vhost_user_set_vring_addr(dev, &addr);
>
>
> The initial ring startup is ordered incorrectly. Generic startup
> suppresses SET_VRING_ADDR, installs SET_VRING_KICK, and injects a kick;
> only the later backend-start hook creates the shadow ring and sends its
> address. SET_VRING_KICK starts the ring, so the backend can process
> before receiving valid addresses. The late SET_VRING_ADDR result is also
> ignored.

I think to avoid this issue, I need to assign svq vring locations when the mem
table is getting set, rather than on device start, as the svq
implementation seems to
expect.

It is likely that the isolation mode implementation will end up moving
up to vhost rather than
vhost-user.  If that's the case, some of the complexity around
tracking order of operations should
be simplified.

Best,
Connor
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.