Development discussions about virtio-fs
 help / color / mirror / Atom feed
From: Connor Kite <connorkite@gmail.com>
To: qemu-devel@nongnu.org
Cc: "Michael S. Tsirkin" <mst@redhat.com>,
	"Stefano Garzarella" <sgarzare@redhat.com>,
	"Alex Bennée" <alex.bennee@linaro.org>,
	"Viresh Kumar" <viresh.kumar@linaro.org>,
	"Gerd Hoffmann" <kraxel@redhat.com>,
	"Mathieu Poirier" <mathieu.poirier@linaro.org>,
	"Manos Pitsidianakis" <manos.pitsidianakis@linaro.org>,
	"Raphael Norwitz" <rnorwitz@nvidia.com>,
	"Kevin Wolf" <kwolf@redhat.com>,
	"Hanna Reitz" <hreitz@redhat.com>,
	"Marc-André Lureau" <marcandre.lureau@redhat.com>,
	"Paolo Bonzini" <pbonzini@redhat.com>,
	"Fam Zheng" <fam@euphon.net>,
	"Stefan Hajnoczi" <stefanha@redhat.com>,
	"Milan Zamazal" <mzamazal@redhat.com>,
	"Akihiko Odaki" <odaki@rsg.ci.i.u-tokyo.ac.jp>,
	"Dmitry Osipenko" <dmitry.osipenko@collabora.com>,
	qemu-block@nongnu.org, virtio-fs@lists.linux.dev,
	"Gonglei (Arei)" <arei.gonglei@huawei.com>,
	"zhenwei pi" <zhenwei.pi@linux.dev>,
	"Daniel P. Berrangé" <berrange@redhat.com>,
	"Eric Blake" <eblake@redhat.com>,
	"Markus Armbruster" <armbru@redhat.com>,
	"Jason Wang" <jasowangio@gmail.com>,
	"Peter Xu" <peterx@redhat.com>,
	"Eugenio Pérez" <eperezma@redhat.com>,
	"Alyssa Ross" <hi@alyssa.is>,
	"Demi Marie Obenour" <demiobenour@gmail.com>,
	"Connor Kite" <connorkite@gmail.com>,
	20260817233147.2867623-1-connorkite@gmail.com
Subject: [PATCH RFC v2 10/13] hw/virtio/vhost-user: handle data movement with shadow vqs
Date: Mon, 17 Aug 2026 22:12:25 -0700	[thread overview]
Message-ID: <20260817-vhost-user-isolated-memory-v2-10-948aae960abb@gmail.com> (raw)
In-Reply-To: <20260817-vhost-user-isolated-memory-v2-0-948aae960abb@gmail.com>

- Add start logic for shadow virtqueues, which sets vring addresses.
- Update logic for sending vring addresses to backend to point
  to the shadow vrings when isolation mode is active.
- Implement handlers for intercepted avail and used descriptors. These
  handlers copy buffer contents between bounce buffers in the isolation
  region and the buffers made available by the guest
- Implement logic to stop svqs

Signed-off-by: Connor Kite <connorkite@gmail.com>
---
 hw/virtio/vhost-shadow-virtqueue.c |   2 +-
 hw/virtio/vhost-shadow-virtqueue.h |   1 +
 hw/virtio/vhost-user.c             | 292 ++++++++++++++++++++++++++++++++++++-
 3 files changed, 286 insertions(+), 9 deletions(-)

diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
index f54a61439a..80b0828d7c 100644
--- a/hw/virtio/vhost-shadow-virtqueue.c
+++ b/hw/virtio/vhost-shadow-virtqueue.c
@@ -350,7 +350,7 @@ int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
 }
 
 /* Convenience wrapper to add a guest's element to SVQ */
-static int vhost_svq_add_element(VhostShadowVirtqueue *svq,
+int vhost_svq_add_element(VhostShadowVirtqueue *svq,
                                  VirtQueueElement *elem)
 {
     return vhost_svq_add(svq, elem->out_sg, elem->out_num, elem->out_addr,
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
index 1e0cc9e5e4..10a8db6c39 100644
--- a/hw/virtio/vhost-shadow-virtqueue.h
+++ b/hw/virtio/vhost-shadow-virtqueue.h
@@ -183,6 +183,7 @@ VhostShadowVirtqueue *vhost_svq_new(const VhostShadowVirtqueueOps *ops,
 
 void vhost_svq_free(gpointer vq);
 void vhost_svq_set_base_addr(VhostShadowVirtqueue *svq, void *addr);
+int vhost_svq_add_element(VhostShadowVirtqueue *svq, VirtQueueElement *elem);
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostShadowVirtqueue, vhost_svq_free);
 
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index ace328f5eb..ca3aaf7a07 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -1364,6 +1364,231 @@ static int init_isolation_regions(struct vhost_dev *dev,
     return 0;
 }
 
+static inline int vhost_user_get_reg_idx(struct vhost_dev *dev, hwaddr gpa)
+{
+    int i;
+    struct vhost_memory_region *reg;
+
+    for (i = 0; i < dev->mem->nregions; i++) {
+        reg = dev->mem->regions + i;
+
+        if (gpa >= reg->guest_phys_addr &&
+            reg->guest_phys_addr + reg->memory_size > gpa) {
+            return i;
+        }
+    }
+
+    return -1;
+}
+
+static int vhost_user_svq_handle_used(VhostShadowVirtqueue *svq,
+                                      VirtQueueElement *elem,
+                                      void *opaque)
+{
+    uint64_t offset;
+    void *dst;
+    void *src;
+    size_t len, rem;
+    void *reg_last_addr;
+    void *reg_first_addr;
+    struct vhost_dev *dev = opaque;
+    struct vhost_user *u = dev->opaque;
+    void *shmem_addr = u->iso_mem_ctx.shared_mem_addr;
+    size_t shmem_size = u->iso_mem_ctx.size;
+    const DMAMap *map;
+    DMAMap needle;
+    int reg_idx;
+
+    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);
+
+        if (!map) {
+            return -EFAULT;
+        }
+
+        offset = needle.translated_addr - map->translated_addr;
+        src = (void *)int128_get64(int128_add(int128_make64(map->iova +
+                       offset), u->iso_mem_ctx.iso_iova_offset));
+        dst = elem->in_sg[i].iov_base;
+        len = elem->in_sg[i].iov_len;
+
+        /* Confirm that buffer range is fully within iso region */
+        if ((uint64_t)src + len - 1 > (uint64_t)shmem_addr + shmem_size - 1 ||
+            (uint64_t)src + len <= (uint64_t)src ||
+            (uint64_t)src < (uint64_t)shmem_addr) {
+
+            return -EFAULT;
+        }
+
+        reg_idx = vhost_user_get_reg_idx(dev, needle.translated_addr);
+        if (reg_idx < 0) {
+            return -EFAULT;
+        }
+
+        reg_first_addr = (void *)dev->mem->regions[reg_idx].userspace_addr;
+        reg_last_addr = (void *)(dev->mem->regions[reg_idx].userspace_addr +
+                        dev->mem->regions[reg_idx].memory_size - 1);
+
+        /* Confirm that hva from elem matches expected vhost memory region */
+        if (dst < reg_first_addr || dst > reg_last_addr) {
+            return -EFAULT;
+        }
+
+        /*
+         * copy buffer contents from shared memory into guest memory. If
+         * the buffer extends across region boundaries, it must be split and
+         * copied to the correct regions.
+         */
+        while ((uint64_t)reg_last_addr - (uint64_t)dst + 1 < len) {
+            rem = ((uint64_t)dst + len - 1) - (uint64_t)reg_last_addr;
+            len -= rem;
+            memcpy(dst, src, len);
+
+            reg_idx++;
+            if (reg_idx >= dev->mem->nregions) {
+                return -EFAULT;
+            }
+
+            src = (void *)((uint64_t)src + len);
+            dst = (void *)dev->mem->regions[reg_idx].userspace_addr;
+            len = rem;
+            reg_first_addr = (void *)dev->mem->regions[reg_idx].userspace_addr;
+            reg_last_addr = (void *)(dev->mem->regions[reg_idx].userspace_addr +
+                            dev->mem->regions[reg_idx].memory_size - 1);
+        }
+
+        memcpy(dst, src, len);
+    }
+
+    return 0;
+}
+
+static int vhost_user_svq_handle_avail(VhostShadowVirtqueue *svq,
+                                       VirtQueueElement *elem,
+                                       void *opaque)
+{
+    hwaddr offset;
+    const DMAMap *map;
+    DMAMap needle;
+    void *dst;
+    void *src;
+    void *reg_first_addr;
+    void *reg_last_addr;
+    size_t len, rem;
+    struct vhost_dev *dev = opaque;
+    struct vhost_user *u = dev->opaque;
+    void *shmem_addr = u->iso_mem_ctx.shared_mem_addr;
+    size_t shmem_size = u->iso_mem_ctx.size;
+    int reg_idx;
+
+    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);
+
+        if (!map) {
+            return -EFAULT;
+        }
+
+        offset = needle.translated_addr - map->translated_addr;
+        dst = (void *)int128_get64(int128_add(int128_make64(map->iova +
+                       offset), u->iso_mem_ctx.iso_iova_offset));
+        src = elem->out_sg[i].iov_base;
+        len = elem->out_sg[i].iov_len;
+
+        /* Confirm that buffer range is fully within iso region */
+        if ((uint64_t)dst + len - 1 > (uint64_t)shmem_addr + shmem_size - 1 ||
+            (uint64_t)dst + len <= (uint64_t)dst ||
+            (uint64_t)dst < (uint64_t)shmem_addr) {
+
+            return -EFAULT;
+        }
+
+        reg_idx = vhost_user_get_reg_idx(dev, needle.translated_addr);
+        if (reg_idx < 0) {
+            return -EFAULT;
+        }
+
+        reg_first_addr = (void *)dev->mem->regions[reg_idx].userspace_addr;
+        reg_last_addr = (void *)(dev->mem->regions[reg_idx].userspace_addr +
+                        dev->mem->regions[reg_idx].memory_size - 1);
+
+        /* Confirm that hva from elem matches expected vhost memory region */
+        if (src < reg_first_addr || src > reg_last_addr) {
+            return -EFAULT;
+        }
+
+        /*
+         * copy buffer contents from guest memory into shared memory. If
+         * the buffer extends across region boundaries, it must be split and
+         * copied from the correct regions.
+         */
+        while ((uint64_t)reg_last_addr - (uint64_t)src + 1 < len) {
+            rem = ((uint64_t)src + len - 1) - (uint64_t)reg_last_addr;
+            len -= rem;
+            memcpy(dst, src, len);
+
+            reg_idx++;
+            if (reg_idx >= dev->mem->nregions) {
+                return -EFAULT;
+            }
+
+            dst = (void *)((uint64_t)dst + len);
+            src = (void *)dev->mem->regions[reg_idx].userspace_addr;
+            len = rem;
+
+            reg_first_addr = (void *)dev->mem->regions[reg_idx].userspace_addr;
+            reg_last_addr = (void *)(dev->mem->regions[reg_idx].userspace_addr +
+                            dev->mem->regions[reg_idx].memory_size - 1);
+        }
+
+        memcpy(dst, src, len);
+    }
+
+    vhost_svq_add_element(svq, elem);
+
+    return 0;
+}
+
+static int vhost_user_get_vq_index(struct vhost_dev *dev, int idx)
+{
+    assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
+
+    return idx;
+}
+
+static int vhost_user_svqs_vring_map(struct vhost_dev *dev)
+{
+    int ret;
+    struct vhost_user *u = dev->opaque;
+    void *vring_base = u->iso_mem_ctx.vring_hva_addr;
+    uint64_t vring_last = (uint64_t)vring_base +
+                          u->iso_mem_ctx.vring_region_size - 1;
+
+    for (int i = 0; i < u->iso_mem_ctx.shadow_vqs->len; i++) {
+        vhost_user_get_vq_index(dev, dev->vq_index + i); /* bounds checking */
+
+        VirtQueue *vq = virtio_get_queue(dev->vdev, dev->vq_index + i);
+        VhostShadowVirtqueue *svq =
+            g_ptr_array_index(u->iso_mem_ctx.shadow_vqs, i);
+
+        assert((uint64_t)vring_base +
+               vhost_svq_vring_total_size(dev->vdev, vq) - 1 <= vring_last);
+
+        vhost_svq_set_base_addr(svq, vring_base);
+        ret = vhost_svq_start(svq, dev->vdev, vq, u->iso_mem_ctx.tree);
+        if (ret < 0) {
+            return ret;
+        }
+        vring_base = (void *)((uint64_t)vring_base +
+                     vhost_svq_vring_total_size(dev->vdev, vq));
+    }
+
+    return 0;
+}
+
 static int vhost_user_set_mem_table(struct vhost_dev *dev,
                                     struct vhost_memory *mem)
 {
@@ -1407,6 +1632,11 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev,
             return ret;
         }
 
+        ret = vhost_user_svqs_vring_map(dev);
+        if (ret < 0) {
+            return ret;
+        }
+
         ret = vhost_user_write(dev, &msg, fds, fd_num);
         if (ret < 0) {
             return ret;
@@ -1763,13 +1993,6 @@ static int vhost_set_vring_file(struct vhost_dev *dev,
     return 0;
 }
 
-static int vhost_user_get_vq_index(struct vhost_dev *dev, int idx)
-{
-    assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
-
-    return idx;
-}
-
 static int vhost_user_set_vring_kick(struct vhost_dev *dev,
                                      struct vhost_vring_file *file)
 {
@@ -1875,6 +2098,8 @@ static int vhost_user_set_vring_err(struct vhost_dev *dev,
 static int vhost_user_set_vring_addr(struct vhost_dev *dev,
                                      struct vhost_vring_addr *addr)
 {
+    struct vhost_user *u = dev->opaque;
+    ptrdiff_t offset = u->iso_mem_ctx.iso_iova_offset;
     VhostUserMsg msg = {
         .hdr.request = VHOST_USER_SET_VRING_ADDR,
         .hdr.flags = VHOST_USER_VERSION,
@@ -1882,6 +2107,25 @@ static int vhost_user_set_vring_addr(struct vhost_dev *dev,
         .hdr.size = sizeof(msg.payload.addr),
     };
 
+    if (u->user->memory_isolation) {
+        int svq_idx;
+        VhostShadowVirtqueue *svq;
+        struct vhost_vring_addr svq_addr;
+
+        vhost_user_get_vq_index(dev, addr->index); /* bounds checking */
+        svq_idx = addr->index - dev->vq_index;
+        svq = g_ptr_array_index(u->iso_mem_ctx.shadow_vqs,
+                                                      svq_idx);
+
+        svq_addr.avail_user_addr = (uint64_t)(uintptr_t)svq->vring.avail -
+                                   offset;
+        svq_addr.desc_user_addr = (uint64_t)(uintptr_t)svq->vring.desc - offset;
+        svq_addr.used_user_addr = (uint64_t)(uintptr_t)svq->vring.used - offset;
+        svq_addr.index = addr->index;
+
+        msg.payload.addr = svq_addr;
+    }
+
     /*
      * wait for a reply if logging is enabled to make sure
      * backend is actually logging changes
@@ -2857,13 +3101,18 @@ static int vhost_user_postcopy_notifier(NotifierWithReturn *notifier,
     return 0;
 }
 
+static const VhostShadowVirtqueueOps vhost_user_svq_ops = {
+    .avail_handler = vhost_user_svq_handle_avail,
+    .used_callback = vhost_user_svq_handle_used
+};
+
 static void vhost_user_init_svq(struct vhost_dev *dev, struct vhost_user *u)
 {
     /*Modified from vhost-vdpa*/
     u->iso_mem_ctx.shadow_vqs = g_ptr_array_new_full(dev->nvqs, vhost_svq_free);
     for (int i = 0; i < dev->nvqs; i++) {
         VhostShadowVirtqueue *svq;
-        svq = vhost_svq_new(NULL, NULL);
+        svq = vhost_svq_new(&vhost_user_svq_ops, dev);
         g_ptr_array_add(u->iso_mem_ctx.shadow_vqs, svq);
     }
 }
@@ -3562,8 +3811,35 @@ void vhost_user_async_close(DeviceState *d,
     }
 }
 
+static void vhost_user_svqs_stop(struct vhost_dev *dev)
+{
+    struct vhost_user *u = dev->opaque;
+    for (int i = 0; i < u->iso_mem_ctx.shadow_vqs->len; i++) {
+        vhost_svq_stop(g_ptr_array_index(u->iso_mem_ctx.shadow_vqs, i));
+    }
+}
+
 static int vhost_user_dev_start(struct vhost_dev *dev, bool started)
 {
+    struct vhost_user *u = dev->opaque;
+    if (u->user->memory_isolation) {
+        if (vhost_dev_has_iommu(dev)) {
+            error_report("Memory isolation is not supported with IOMMU enabled."
+                         "Please disable one and try again");
+            return -1;
+        }
+
+        if (virtio_vdev_has_feature(dev->vdev, VIRTIO_F_RING_PACKED)) {
+            error_report("Memory isolation is not supported with packed"
+                         "vrings.  Please use split vrings");
+            return -1;
+        }
+
+        if (!started) {
+            vhost_user_svqs_stop(dev);
+        }
+    }
+
     if (!vhost_user_has_protocol_feature(dev, VHOST_USER_PROTOCOL_F_STATUS)) {
         return 0;
     }

-- 
2.43.0


  parent reply	other threads:[~2026-08-18  5:12 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-18  5:12 [PATCH RFC v2 00/13] vhost-user: isolated memory Connor Kite
2026-08-18  5:12 ` [PATCH RFC v2 01/13] vhost-user: Consolidate chardev property definitions Connor Kite
2026-08-18  5:12 ` [PATCH RFC v2 02/13] util/iova-tree: g_tree_foreach wrapper Connor Kite
2026-08-18  5:12 ` [PATCH RFC v2 03/13] hw/virtio: iova_tree_foreach wrapper Connor Kite
2026-08-18  5:12 ` [PATCH RFC v2 04/13] hw/virtio/vhost-shadow-virtqueue: used callback Connor Kite
2026-08-19  6:23   ` Akihiko Odaki
2026-08-20  1:06     ` Connor Kite
2026-08-20  5:15       ` Akihiko Odaki
2026-08-20 23:42         ` Connor Kite
2026-08-21  7:21           ` Akihiko Odaki
2026-08-18  5:12 ` [PATCH RFC v2 05/13] hw/virtio/vhost-shadow-virtqueue: specified vring placement Connor Kite
2026-08-18  5:12 ` [PATCH RFC v2 06/13] vhost-user: add memory_isolation to VhostUserState Connor Kite
2026-08-18  5:12 ` [PATCH RFC v2 07/13] hw/virtio/vhost-user: create isolation region Connor Kite
2026-08-19  7:36   ` Akihiko Odaki
2026-08-21  2:57     ` Connor Kite
2026-08-18  5:12 ` [PATCH RFC v2 08/13] hw/virtio/vhost-user: send isolation regions to device Connor Kite
2026-08-18 11:31   ` Akihiko Odaki
2026-08-18  5:12 ` [PATCH RFC v2 09/13] hw/virtio/vhost-user: add shadow virtqueues and eventfd intercepts Connor Kite
2026-08-18 12:34   ` Akihiko Odaki
2026-08-20 20:38     ` Connor Kite
2026-08-18  5:12 ` Connor Kite [this message]
2026-08-19  7:37   ` [PATCH RFC v2 10/13] hw/virtio/vhost-user: handle data movement with shadow vqs Akihiko Odaki
2026-08-18  5:12 ` [PATCH RFC v2 11/13] vhost-user: Add memory-isolation qdev property to vhost-user devices Connor Kite
2026-08-18  5:12 ` [PATCH RFC v2 12/13] backends/cryptodev-vhost-user: add memory isolation bool Connor Kite
2026-08-20  9:00   ` Markus Armbruster
2026-08-18  5:12 ` [PATCH RFC v2 13/13] net/vhost-user: add memory isolation Connor Kite
2026-08-19  7:37   ` Akihiko Odaki
2026-08-20  1:22     ` Connor Kite
2026-08-20  9:02   ` Markus Armbruster
2026-08-21  0:39     ` Connor Kite
2026-08-21  5:54       ` Markus Armbruster

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260817-vhost-user-isolated-memory-v2-10-948aae960abb@gmail.com \
    --to=connorkite@gmail.com \
    --cc=20260817233147.2867623-1-connorkite@gmail.com \
    --cc=alex.bennee@linaro.org \
    --cc=arei.gonglei@huawei.com \
    --cc=armbru@redhat.com \
    --cc=berrange@redhat.com \
    --cc=demiobenour@gmail.com \
    --cc=dmitry.osipenko@collabora.com \
    --cc=eblake@redhat.com \
    --cc=eperezma@redhat.com \
    --cc=fam@euphon.net \
    --cc=hi@alyssa.is \
    --cc=hreitz@redhat.com \
    --cc=jasowangio@gmail.com \
    --cc=kraxel@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=manos.pitsidianakis@linaro.org \
    --cc=marcandre.lureau@redhat.com \
    --cc=mathieu.poirier@linaro.org \
    --cc=mst@redhat.com \
    --cc=mzamazal@redhat.com \
    --cc=odaki@rsg.ci.i.u-tokyo.ac.jp \
    --cc=pbonzini@redhat.com \
    --cc=peterx@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=rnorwitz@nvidia.com \
    --cc=sgarzare@redhat.com \
    --cc=stefanha@redhat.com \
    --cc=viresh.kumar@linaro.org \
    --cc=virtio-fs@lists.linux.dev \
    --cc=zhenwei.pi@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox