* [PATCH RFC v2 01/13] vhost-user: Consolidate chardev property definitions
2026-08-18 5:12 [PATCH RFC v2 00/13] vhost-user: isolated memory Connor Kite
@ 2026-08-18 5:12 ` Connor Kite
2026-08-18 5:12 ` [PATCH RFC v2 02/13] util/iova-tree: g_tree_foreach wrapper Connor Kite
` (11 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Connor Kite @ 2026-08-18 5:12 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Alex Bennée,
Viresh Kumar, Gerd Hoffmann, Mathieu Poirier, Manos Pitsidianakis,
Raphael Norwitz, Kevin Wolf, Hanna Reitz, Marc-André Lureau,
Paolo Bonzini, Fam Zheng, Stefan Hajnoczi, Milan Zamazal,
Akihiko Odaki, Dmitry Osipenko, qemu-block, virtio-fs,
Gonglei (Arei), zhenwei pi, Daniel P. Berrangé, Eric Blake,
Markus Armbruster, Jason Wang, Peter Xu, Eugenio Pérez,
Alyssa Ross, Demi Marie Obenour, Connor Kite,
20260817233147.2867623-1-connorkite
Move chardev qdev property definitions from children into
VHostUserBase. This reduces code duplication and allows children
to simply inherit the property during initialization.
Signed-off-by: Connor Kite <connorkite@gmail.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
hw/virtio/vhost-user-base.c | 9 +++++++++
hw/virtio/vhost-user-gpio.c | 4 ----
hw/virtio/vhost-user-i2c.c | 5 -----
hw/virtio/vhost-user-input.c | 5 -----
hw/virtio/vhost-user-rng.c | 5 -----
hw/virtio/vhost-user-rtc.c | 4 ----
hw/virtio/vhost-user-snd.c | 1 -
hw/virtio/vhost-user-spi.c | 5 -----
hw/virtio/vhost-user-test-device.c | 1 -
9 files changed, 9 insertions(+), 30 deletions(-)
diff --git a/hw/virtio/vhost-user-base.c b/hw/virtio/vhost-user-base.c
index 478ec68f09..90bd2903db 100644
--- a/hw/virtio/vhost-user-base.c
+++ b/hw/virtio/vhost-user-base.c
@@ -407,9 +407,16 @@ static void vub_device_unrealize(DeviceState *dev)
do_vhost_user_cleanup(vdev, vub);
}
+/*Define common qdev properties. Inherited by all children*/
+static const Property vub_properties[] = {
+ DEFINE_PROP_CHR("chardev", VHostUserBase, chardev)
+};
+
+
static void vub_class_init(ObjectClass *klass, const void *data)
{
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
vdc->realize = vub_device_realize;
vdc->unrealize = vub_device_unrealize;
@@ -417,6 +424,8 @@ static void vub_class_init(ObjectClass *klass, const void *data)
vdc->get_config = vub_get_config;
vdc->set_config = vub_set_config;
vdc->set_status = vub_set_status;
+
+ device_class_set_props(dc, vub_properties);
}
static const TypeInfo vub_types[] = {
diff --git a/hw/virtio/vhost-user-gpio.c b/hw/virtio/vhost-user-gpio.c
index d473f87077..33b8752282 100644
--- a/hw/virtio/vhost-user-gpio.c
+++ b/hw/virtio/vhost-user-gpio.c
@@ -14,9 +14,6 @@
#include "standard-headers/linux/virtio_ids.h"
#include "standard-headers/linux/virtio_gpio.h"
-static const Property vgpio_properties[] = {
- DEFINE_PROP_CHR("chardev", VHostUserBase, chardev),
-};
static void vgpio_realize(DeviceState *dev, Error **errp)
{
@@ -42,7 +39,6 @@ static void vu_gpio_class_init(ObjectClass *klass, const void *data)
VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass);
dc->vmsd = &vu_gpio_vmstate;
- device_class_set_props(dc, vgpio_properties);
device_class_set_parent_realize(dc, vgpio_realize,
&vubc->parent_realize);
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
diff --git a/hw/virtio/vhost-user-i2c.c b/hw/virtio/vhost-user-i2c.c
index 152b1f6740..3f5427b5b2 100644
--- a/hw/virtio/vhost-user-i2c.c
+++ b/hw/virtio/vhost-user-i2c.c
@@ -14,10 +14,6 @@
#include "qemu/error-report.h"
#include "standard-headers/linux/virtio_ids.h"
-static const Property vi2c_properties[] = {
- DEFINE_PROP_CHR("chardev", VHostUserBase, chardev),
-};
-
static void vi2c_realize(DeviceState *dev, Error **errp)
{
VHostUserBase *vub = VHOST_USER_BASE(dev);
@@ -42,7 +38,6 @@ static void vu_i2c_class_init(ObjectClass *klass, const void *data)
VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass);
dc->vmsd = &vu_i2c_vmstate;
- device_class_set_props(dc, vi2c_properties);
device_class_set_parent_realize(dc, vi2c_realize,
&vubc->parent_realize);
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
diff --git a/hw/virtio/vhost-user-input.c b/hw/virtio/vhost-user-input.c
index 5cfc5bbb56..8fe6349ffc 100644
--- a/hw/virtio/vhost-user-input.c
+++ b/hw/virtio/vhost-user-input.c
@@ -7,10 +7,6 @@
#include "qemu/osdep.h"
#include "hw/virtio/virtio-input.h"
-static const Property vinput_properties[] = {
- DEFINE_PROP_CHR("chardev", VHostUserBase, chardev),
-};
-
static void vinput_realize(DeviceState *dev, Error **errp)
{
VHostUserBase *vub = VHOST_USER_BASE(dev);
@@ -36,7 +32,6 @@ static void vhost_input_class_init(ObjectClass *klass, const void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &vmstate_vhost_input;
- device_class_set_props(dc, vinput_properties);
device_class_set_parent_realize(dc, vinput_realize,
&vubc->parent_realize);
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
diff --git a/hw/virtio/vhost-user-rng.c b/hw/virtio/vhost-user-rng.c
index 106c8f211a..664aee403d 100644
--- a/hw/virtio/vhost-user-rng.c
+++ b/hw/virtio/vhost-user-rng.c
@@ -20,10 +20,6 @@ static const VMStateDescription vu_rng_vmstate = {
.unmigratable = 1,
};
-static const Property vrng_properties[] = {
- DEFINE_PROP_CHR("chardev", VHostUserBase, chardev),
-};
-
static void vu_rng_base_realize(DeviceState *dev, Error **errp)
{
VHostUserBase *vub = VHOST_USER_BASE(dev);
@@ -43,7 +39,6 @@ static void vu_rng_class_init(ObjectClass *klass, const void *data)
VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass);
dc->vmsd = &vu_rng_vmstate;
- device_class_set_props(dc, vrng_properties);
device_class_set_parent_realize(dc, vu_rng_base_realize,
&vubc->parent_realize);
diff --git a/hw/virtio/vhost-user-rtc.c b/hw/virtio/vhost-user-rtc.c
index 88b0c70b90..6d80cb6263 100644
--- a/hw/virtio/vhost-user-rtc.c
+++ b/hw/virtio/vhost-user-rtc.c
@@ -19,9 +19,6 @@ static const VMStateDescription vu_rtc_vmstate = {
.unmigratable = 1,
};
-static const Property vrtc_properties[] = {
- DEFINE_PROP_CHR("chardev", VHostUserBase, chardev),
-};
static void vu_rtc_base_realize(DeviceState *dev, Error **errp)
{
@@ -42,7 +39,6 @@ static void vu_rtc_class_init(ObjectClass *klass, const void *data)
VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass);
dc->vmsd = &vu_rtc_vmstate;
- device_class_set_props(dc, vrtc_properties);
device_class_set_parent_realize(dc, vu_rtc_base_realize,
&vubc->parent_realize);
diff --git a/hw/virtio/vhost-user-snd.c b/hw/virtio/vhost-user-snd.c
index 7129b77d9c..3663450aed 100644
--- a/hw/virtio/vhost-user-snd.c
+++ b/hw/virtio/vhost-user-snd.c
@@ -34,7 +34,6 @@ static const VMStateDescription vu_snd_vmstate = {
};
static const Property vsnd_properties[] = {
- DEFINE_PROP_CHR("chardev", VHostUserBase, chardev),
DEFINE_PROP_BIT64("controls", VHostUserBase,
parent_obj.host_features, VIRTIO_SND_F_CTLS, false),
};
diff --git a/hw/virtio/vhost-user-spi.c b/hw/virtio/vhost-user-spi.c
index 707f96c250..fb6168d391 100644
--- a/hw/virtio/vhost-user-spi.c
+++ b/hw/virtio/vhost-user-spi.c
@@ -15,10 +15,6 @@
#include "standard-headers/linux/virtio_ids.h"
#include "standard-headers/linux/virtio_spi.h"
-static const Property vspi_properties[] = {
- DEFINE_PROP_CHR("chardev", VHostUserBase, chardev),
-};
-
static void vspi_realize(DeviceState *dev, Error **errp)
{
VHostUserBase *vub = VHOST_USER_BASE(dev);
@@ -44,7 +40,6 @@ static void vu_spi_class_init(ObjectClass *klass, const void *data)
VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass);
dc->vmsd = &vu_spi_vmstate;
- device_class_set_props(dc, vspi_properties);
device_class_set_parent_realize(dc, vspi_realize,
&vubc->parent_realize);
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
diff --git a/hw/virtio/vhost-user-test-device.c b/hw/virtio/vhost-user-test-device.c
index a2f963fdf6..91e23bf07a 100644
--- a/hw/virtio/vhost-user-test-device.c
+++ b/hw/virtio/vhost-user-test-device.c
@@ -30,7 +30,6 @@ static const VMStateDescription vud_vmstate = {
};
static const Property vud_properties[] = {
- DEFINE_PROP_CHR("chardev", VHostUserBase, chardev),
DEFINE_PROP_UINT16("virtio-id", VHostUserBase, virtio_id, 0),
DEFINE_PROP_UINT32("vq_size", VHostUserBase, vq_size, 64),
DEFINE_PROP_UINT32("num_vqs", VHostUserBase, num_vqs, 1),
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH RFC v2 02/13] util/iova-tree: g_tree_foreach wrapper
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 ` Connor Kite
2026-08-18 5:12 ` [PATCH RFC v2 03/13] hw/virtio: iova_tree_foreach wrapper Connor Kite
` (10 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Connor Kite @ 2026-08-18 5:12 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Alex Bennée,
Viresh Kumar, Gerd Hoffmann, Mathieu Poirier, Manos Pitsidianakis,
Raphael Norwitz, Kevin Wolf, Hanna Reitz, Marc-André Lureau,
Paolo Bonzini, Fam Zheng, Stefan Hajnoczi, Milan Zamazal,
Akihiko Odaki, Dmitry Osipenko, qemu-block, virtio-fs,
Gonglei (Arei), zhenwei pi, Daniel P. Berrangé, Eric Blake,
Markus Armbruster, Jason Wang, Peter Xu, Eugenio Pérez,
Alyssa Ross, Demi Marie Obenour, Connor Kite,
20260817233147.2867623-1-connorkite
Add a wrapper to iova-tree to call g_tree_foreach. Provides a means
to execute some function on each node of the iova-tree.
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Connor Kite <connorkite@gmail.com>
---
include/qemu/iova-tree.h | 21 +++++++++++++++++++++
util/iova-tree.c | 5 +++++
2 files changed, 26 insertions(+)
diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h
index 14e82a22d5..a1938e36a1 100644
--- a/include/qemu/iova-tree.h
+++ b/include/qemu/iova-tree.h
@@ -161,4 +161,25 @@ int iova_tree_alloc_map(IOVATree *tree, DMAMap *map, hwaddr iova_begin,
*/
void iova_tree_destroy(IOVATree *tree);
+/**
+ * iova_tree_foreach:
+ *
+ * @tree: the iova tree to traverse
+ * @func: the function called at every node. A true return value ends traversal.
+ * Arguments:
+ * gpointer key: key of current node
+ * gpointer value: value of current node
+ * gpointer data: data passed in at g_tree_foreach call
+ * Return: gboolean
+ * @data: an argument passed to func
+ *
+ * Wrapper to call g_tree_foreach on the underlying g_tree. g_tree_foreach will
+ * traverse through every node of the tree, barring an early return. At each
+ * node, g_tree_foreach calls func with pointers to the node key, node value,
+ * and a pointer to data passed in by the caller.
+ *
+ * Return: None
+ */
+void iova_tree_foreach(IOVATree *tree, GTraverseFunc func, gpointer data);
+
#endif
diff --git a/util/iova-tree.c b/util/iova-tree.c
index 5b0c95ff15..2239bb6ae1 100644
--- a/util/iova-tree.c
+++ b/util/iova-tree.c
@@ -303,3 +303,8 @@ int gpa_tree_insert(IOVATree *tree, const DMAMap *map)
return IOVA_OK;
}
+
+void iova_tree_foreach(IOVATree *tree, GTraverseFunc func, gpointer data)
+{
+ g_tree_foreach(tree->tree, func, data);
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH RFC v2 03/13] hw/virtio: iova_tree_foreach wrapper
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 ` Connor Kite
2026-08-18 5:12 ` [PATCH RFC v2 04/13] hw/virtio/vhost-shadow-virtqueue: used callback Connor Kite
` (9 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Connor Kite @ 2026-08-18 5:12 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Alex Bennée,
Viresh Kumar, Gerd Hoffmann, Mathieu Poirier, Manos Pitsidianakis,
Raphael Norwitz, Kevin Wolf, Hanna Reitz, Marc-André Lureau,
Paolo Bonzini, Fam Zheng, Stefan Hajnoczi, Milan Zamazal,
Akihiko Odaki, Dmitry Osipenko, qemu-block, virtio-fs,
Gonglei (Arei), zhenwei pi, Daniel P. Berrangé, Eric Blake,
Markus Armbruster, Jason Wang, Peter Xu, Eugenio Pérez,
Alyssa Ross, Demi Marie Obenour, Connor Kite,
20260817233147.2867623-1-connorkite
Add a wrapper to vhost-iova-tree that calls iova_tree_foreach on the
underlying iova_map. This provides a means to execute some function on
each node of the tree.
Signed-off-by: Connor Kite <connorkite@gmail.com>
---
hw/virtio/vhost-iova-tree.c | 15 +++++++++++++++
hw/virtio/vhost-iova-tree.h | 2 ++
2 files changed, 17 insertions(+)
diff --git a/hw/virtio/vhost-iova-tree.c b/hw/virtio/vhost-iova-tree.c
index fa4147b773..0a9b38b6f3 100644
--- a/hw/virtio/vhost-iova-tree.c
+++ b/hw/virtio/vhost-iova-tree.c
@@ -189,3 +189,18 @@ void vhost_iova_tree_remove_gpa(VhostIOVATree *iova_tree, DMAMap map)
iova_tree_remove(iova_tree->gpa_iova_map, map);
iova_tree_remove(iova_tree->iova_map, map);
}
+
+
+/**
+ * Traverse through the iova tree in sorted order
+ *
+ * @tree: The VhostIOVATree
+ * @func: Function called at every node. A true return value ends traversal
+ * @data: An argument passed to func
+ */
+void vhost_iova_tree_foreach(VhostIOVATree *tree,
+ GTraverseFunc func,
+ gpointer data)
+{
+ iova_tree_foreach(tree->iova_map, func, data);
+}
diff --git a/hw/virtio/vhost-iova-tree.h b/hw/virtio/vhost-iova-tree.h
index 08f63b61cd..c84fd652bd 100644
--- a/hw/virtio/vhost-iova-tree.h
+++ b/hw/virtio/vhost-iova-tree.h
@@ -29,5 +29,7 @@ const DMAMap *vhost_iova_tree_find_gpa(const VhostIOVATree *iova_tree,
int vhost_iova_tree_map_alloc_gpa(VhostIOVATree *iova_tree, DMAMap *map,
hwaddr taddr);
void vhost_iova_tree_remove_gpa(VhostIOVATree *iova_tree, DMAMap map);
+void vhost_iova_tree_foreach(VhostIOVATree *tree, GTraverseFunc func,
+ gpointer data);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH RFC v2 04/13] hw/virtio/vhost-shadow-virtqueue: used callback
2026-08-18 5:12 [PATCH RFC v2 00/13] vhost-user: isolated memory Connor Kite
` (2 preceding siblings ...)
2026-08-18 5:12 ` [PATCH RFC v2 03/13] hw/virtio: iova_tree_foreach wrapper Connor Kite
@ 2026-08-18 5:12 ` Connor Kite
2026-08-18 5:12 ` [PATCH RFC v2 05/13] hw/virtio/vhost-shadow-virtqueue: specified vring placement Connor Kite
` (8 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Connor Kite @ 2026-08-18 5:12 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Alex Bennée,
Viresh Kumar, Gerd Hoffmann, Mathieu Poirier, Manos Pitsidianakis,
Raphael Norwitz, Kevin Wolf, Hanna Reitz, Marc-André Lureau,
Paolo Bonzini, Fam Zheng, Stefan Hajnoczi, Milan Zamazal,
Akihiko Odaki, Dmitry Osipenko, qemu-block, virtio-fs,
Gonglei (Arei), zhenwei pi, Daniel P. Berrangé, Eric Blake,
Markus Armbruster, Jason Wang, Peter Xu, Eugenio Pérez,
Alyssa Ross, Demi Marie Obenour, Connor Kite,
20260817233147.2867623-1-connorkite
Adds a callback to shadow virtqueues in order to enable
non-default processing of used vring elements from the device.
Signed-off-by: Connor Kite <connorkite@gmail.com>
---
hw/virtio/vhost-shadow-virtqueue.c | 9 +++++++++
hw/virtio/vhost-shadow-virtqueue.h | 16 ++++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
index c8831d52be..496e7e58a3 100644
--- a/hw/virtio/vhost-shadow-virtqueue.c
+++ b/hw/virtio/vhost-shadow-virtqueue.c
@@ -675,6 +675,15 @@ static void vhost_svq_flush(VhostShadowVirtqueue *svq,
break;
}
+ if (svq->ops && svq->ops->used_callback) {
+ int r = svq->ops->used_callback(svq, elem, svq->ops_opaque);
+
+ if (r < 0) {
+ /* VQ or handler is broken. Do not set guest notifier */
+ return;
+ }
+ }
+
if (unlikely(i >= svq->vring.num)) {
qemu_log_mask(LOG_GUEST_ERROR,
"More than %u used buffers obtained in a %u size SVQ",
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
index ec16a1e838..fd68319fb7 100644
--- a/hw/virtio/vhost-shadow-virtqueue.h
+++ b/hw/virtio/vhost-shadow-virtqueue.h
@@ -56,8 +56,24 @@ typedef int (*VirtQueueAvailCallback)(VhostShadowVirtqueue *svq,
VirtQueueElement *elem,
void *vq_callback_opaque);
+/**
+ * Callback to handle a used buffer
+ *
+ * @svq: Shadow virtqueue
+ * @elem: Element placed in the queue by the device
+ * @vq_callback_opaque: Used to pass arguments to callback
+ *
+ * Returns 0 if the vq is running as expected. Returns a negative errno on
+ * failure. The callback should not add used elements to the vq shadowed
+ * by the svq as that is done separately after the callback returns.
+ */
+typedef int (*VirtQueueUsedCallback)(VhostShadowVirtqueue *svq,
+ VirtQueueElement *elem,
+ void *vq_callback_opaque);
+
typedef struct VhostShadowVirtqueueOps {
VirtQueueAvailCallback avail_handler;
+ VirtQueueUsedCallback used_callback;
} VhostShadowVirtqueueOps;
/* Shadow virtqueue to relay notifications */
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH RFC v2 05/13] hw/virtio/vhost-shadow-virtqueue: specified vring placement
2026-08-18 5:12 [PATCH RFC v2 00/13] vhost-user: isolated memory Connor Kite
` (3 preceding siblings ...)
2026-08-18 5:12 ` [PATCH RFC v2 04/13] hw/virtio/vhost-shadow-virtqueue: used callback Connor Kite
@ 2026-08-18 5:12 ` Connor Kite
2026-08-18 5:12 ` [PATCH RFC v2 06/13] vhost-user: add memory_isolation to VhostUserState Connor Kite
` (7 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Connor Kite @ 2026-08-18 5:12 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Alex Bennée,
Viresh Kumar, Gerd Hoffmann, Mathieu Poirier, Manos Pitsidianakis,
Raphael Norwitz, Kevin Wolf, Hanna Reitz, Marc-André Lureau,
Paolo Bonzini, Fam Zheng, Stefan Hajnoczi, Milan Zamazal,
Akihiko Odaki, Dmitry Osipenko, qemu-block, virtio-fs,
Gonglei (Arei), zhenwei pi, Daniel P. Berrangé, Eric Blake,
Markus Armbruster, Jason Wang, Peter Xu, Eugenio Pérez,
Alyssa Ross, Demi Marie Obenour, Connor Kite,
20260817233147.2867623-1-connorkite
By default svq vrings are placed in an anonymous memory map. As svqs
will be leveraged to enable memory isolation in vhost-user, it is useful
to be able to place the vrings in a shared isolation memory region.
Adds the option to specify vring placement by providing a vring base
address before starting the svq.
Signed-off-by: Connor Kite <connorkite@gmail.com>
---
hw/virtio/vhost-shadow-virtqueue.c | 69 ++++++++++++++++++++++++++++++++------
hw/virtio/vhost-shadow-virtqueue.h | 8 ++++-
2 files changed, 66 insertions(+), 11 deletions(-)
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
index 496e7e58a3..f54a61439a 100644
--- a/hw/virtio/vhost-shadow-virtqueue.c
+++ b/hw/virtio/vhost-shadow-virtqueue.c
@@ -812,6 +812,13 @@ size_t vhost_svq_device_area_size(const VhostShadowVirtqueue *svq)
return ROUND_UP(used_size, qemu_real_host_page_size());
}
+size_t vhost_svq_vring_total_size(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VhostShadowVirtqueue svq;
+ svq.vring.num = virtio_queue_get_num(vdev, virtio_get_queue_index(vq));
+ return vhost_svq_driver_area_size(&svq) + vhost_svq_device_area_size(&svq);
+}
+
/**
* Set a new file descriptor for the guest to kick the SVQ and notify for avail
*
@@ -842,6 +849,19 @@ void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd)
}
}
+/**
+ * Set vring base address if using fixed locations
+ *
+ * @svq: Shadow Virtqueue
+ * @addr: Points to new base address
+ */
+
+ void vhost_svq_set_base_addr(VhostShadowVirtqueue *svq, void *addr)
+ {
+ svq->base_addr = addr;
+ }
+
+
/**
* Start the shadow virtqueue operation.
*
@@ -849,8 +869,10 @@ void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd)
* @vdev: VirtIO device
* @vq: Virtqueue to shadow
* @iova_tree: Tree to perform descriptors translations
+ *
+ * Return 0 on success, -errno on failure
*/
-void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev,
+int vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev,
VirtQueue *vq, VhostIOVATree *iova_tree)
{
size_t desc_size;
@@ -868,14 +890,27 @@ void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev,
svq->vring.num = virtio_queue_get_num(vdev, virtio_get_queue_index(vq));
svq->num_free = svq->vring.num;
- svq->vring.desc = mmap(NULL, vhost_svq_driver_area_size(svq),
- PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
- -1, 0);
desc_size = sizeof(vring_desc_t) * svq->vring.num;
- svq->vring.avail = (void *)((char *)svq->vring.desc + desc_size);
- svq->vring.used = mmap(NULL, vhost_svq_device_area_size(svq),
- PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
- -1, 0);
+ if (svq->base_addr == NULL) {
+ svq->vring.desc = mmap(NULL, vhost_svq_driver_area_size(svq),
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
+ -1, 0);
+ svq->vring.avail = (void *)((char *)svq->vring.desc + desc_size);
+ svq->vring.used = mmap(NULL, vhost_svq_device_area_size(svq),
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
+ -1, 0);
+ } else {
+ svq->vring.desc = (void *)svq->base_addr;
+ svq->vring.avail = (void *)((char *)svq->vring.desc + desc_size);
+ svq->vring.used = (void *)((char *)svq->base_addr +
+ vhost_svq_driver_area_size(svq));
+
+ if ((uint64_t)svq->vring.used + vhost_svq_device_area_size(svq) - 1 <
+ (uint64_t)svq->vring.desc) {
+ error_report("Invalid shadow vring location");
+ return -ENOMEM;
+ }
+ }
svq->desc_state = g_new0(SVQDescState, svq->vring.num);
if (virtio_vdev_has_feature(svq->vdev, VIRTIO_F_IN_ORDER)) {
svq->batch_last.id = VIRTIO_RING_NOT_IN_BATCH;
@@ -884,6 +919,8 @@ void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev,
svq->desc_state[i].next = i + 1;
}
}
+
+ return 0;
}
/**
@@ -920,8 +957,19 @@ void vhost_svq_stop(VhostShadowVirtqueue *svq)
}
svq->vq = NULL;
g_free(svq->desc_state);
- munmap(svq->vring.desc, vhost_svq_driver_area_size(svq));
- munmap(svq->vring.used, vhost_svq_device_area_size(svq));
+
+ if (!svq->base_addr) {
+ munmap(svq->vring.desc, vhost_svq_driver_area_size(svq));
+ munmap(svq->vring.used, vhost_svq_device_area_size(svq));
+ } else{
+ if (svq->vring.desc) {
+ memset(svq->vring.desc, 0, vhost_svq_driver_area_size(svq));
+ }
+ if (svq->vring.used) {
+ memset(svq->vring.used, 0, vhost_svq_device_area_size(svq));
+ }
+ }
+
event_notifier_set_handler(&svq->hdev_call, NULL);
}
@@ -940,6 +988,7 @@ VhostShadowVirtqueue *vhost_svq_new(const VhostShadowVirtqueueOps *ops,
event_notifier_init_fd(&svq->svq_kick, VHOST_FILE_UNBIND);
svq->ops = ops;
svq->ops_opaque = ops_opaque;
+ svq->base_addr = NULL;
return svq;
}
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
index fd68319fb7..1e0cc9e5e4 100644
--- a/hw/virtio/vhost-shadow-virtqueue.h
+++ b/hw/virtio/vhost-shadow-virtqueue.h
@@ -150,6 +150,9 @@ typedef struct VhostShadowVirtqueue {
/* Size of SVQ vring free descriptors */
uint16_t num_free;
+
+ /* Location assigned to vrings if not in default anon memory map */
+ void *base_addr;
} VhostShadowVirtqueue;
bool vhost_svq_valid_features(uint64_t features, Error **errp);
@@ -169,8 +172,9 @@ void vhost_svq_get_vring_addr(const VhostShadowVirtqueue *svq,
struct vhost_vring_addr *addr);
size_t vhost_svq_driver_area_size(const VhostShadowVirtqueue *svq);
size_t vhost_svq_device_area_size(const VhostShadowVirtqueue *svq);
+size_t vhost_svq_vring_total_size(VirtIODevice *vdev, VirtQueue *vq);
-void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev,
+int vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev,
VirtQueue *vq, VhostIOVATree *iova_tree);
void vhost_svq_stop(VhostShadowVirtqueue *svq);
@@ -178,6 +182,8 @@ VhostShadowVirtqueue *vhost_svq_new(const VhostShadowVirtqueueOps *ops,
void *ops_opaque);
void vhost_svq_free(gpointer vq);
+void vhost_svq_set_base_addr(VhostShadowVirtqueue *svq, void *addr);
+
G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostShadowVirtqueue, vhost_svq_free);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH RFC v2 06/13] vhost-user: add memory_isolation to VhostUserState
2026-08-18 5:12 [PATCH RFC v2 00/13] vhost-user: isolated memory Connor Kite
` (4 preceding siblings ...)
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 ` Connor Kite
2026-08-18 5:12 ` [PATCH RFC v2 07/13] hw/virtio/vhost-user: create isolation region Connor Kite
` (6 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Connor Kite @ 2026-08-18 5:12 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Alex Bennée,
Viresh Kumar, Gerd Hoffmann, Mathieu Poirier, Manos Pitsidianakis,
Raphael Norwitz, Kevin Wolf, Hanna Reitz, Marc-André Lureau,
Paolo Bonzini, Fam Zheng, Stefan Hajnoczi, Milan Zamazal,
Akihiko Odaki, Dmitry Osipenko, qemu-block, virtio-fs,
Gonglei (Arei), zhenwei pi, Daniel P. Berrangé, Eric Blake,
Markus Armbruster, Jason Wang, Peter Xu, Eugenio Pérez,
Alyssa Ross, Demi Marie Obenour, Connor Kite,
20260817233147.2867623-1-connorkite
Add a memory_isolation bool to the VhostUserState struct. This
bool is set within vhost_user_init(), which takes a
memory_isolation bool as an argument.
Refactor all call locations of vhost_user_init to include the new
argument.
Signed-off-by: Connor Kite <connorkite@gmail.com>
---
backends/cryptodev-vhost-user.c | 2 +-
backends/vhost-user.c | 4 ++--
hw/block/vhost-user-blk.c | 3 ++-
hw/display/vhost-user-gpu.c | 3 ++-
hw/scsi/vhost-user-scsi.c | 3 ++-
hw/virtio/vhost-stub.c | 3 ++-
hw/virtio/vhost-user-base.c | 3 ++-
hw/virtio/vhost-user-fs.c | 3 ++-
hw/virtio/vhost-user-scmi.c | 3 ++-
hw/virtio/vhost-user-vsock.c | 3 ++-
hw/virtio/vhost-user.c | 5 ++++-
include/hw/virtio/vhost-user.h | 6 +++++-
include/system/vhost-user-backend.h | 3 ++-
net/passt.c | 14 ++++++++++----
net/vhost-user.c | 8 +++++---
15 files changed, 45 insertions(+), 21 deletions(-)
diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
index cc478d9902..fa1f9b76d3 100644
--- a/backends/cryptodev-vhost-user.c
+++ b/backends/cryptodev-vhost-user.c
@@ -213,7 +213,7 @@ static void cryptodev_vhost_user_init(
}
}
- if (!vhost_user_init(&s->vhost_user, &s->chr, errp)) {
+ if (!vhost_user_init(&s->vhost_user, &s->chr, false, errp)) {
return;
}
diff --git a/backends/vhost-user.c b/backends/vhost-user.c
index 380d825023..470eba1775 100644
--- a/backends/vhost-user.c
+++ b/backends/vhost-user.c
@@ -22,13 +22,13 @@
int
vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev,
- unsigned nvqs, Error **errp)
+ unsigned nvqs, bool memory_isolation, Error **errp)
{
int ret;
assert(!b->vdev && vdev);
- if (!vhost_user_init(&b->vhost_user, &b->chr, errp)) {
+ if (!vhost_user_init(&b->vhost_user, &b->chr, memory_isolation, errp)) {
return -1;
}
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
index 2e5b3ae1b1..4640d7da04 100644
--- a/hw/block/vhost-user-blk.c
+++ b/hw/block/vhost-user-blk.c
@@ -505,7 +505,8 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
return;
}
- if (!vhost_user_init(&s->vhost_user, &s->chardev, errp)) {
+ if (!vhost_user_init(&s->vhost_user, &s->chardev,
+ false, errp)) {
return;
}
diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c
index cd684d6363..6655585b43 100644
--- a/hw/display/vhost-user-gpu.c
+++ b/hw/display/vhost-user-gpu.c
@@ -663,7 +663,8 @@ vhost_user_gpu_device_realize(DeviceState *qdev, Error **errp)
VirtIODevice *vdev = VIRTIO_DEVICE(g);
vhost_dev_set_config_notifier(&g->vhost->dev, &config_ops);
- if (vhost_user_backend_dev_init(g->vhost, vdev, 2, errp) < 0) {
+ if (vhost_user_backend_dev_init(g->vhost, vdev, 2, false,
+ errp) < 0) {
return;
}
diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c
index 5070178dc2..05bc788304 100644
--- a/hw/scsi/vhost-user-scsi.c
+++ b/hw/scsi/vhost-user-scsi.c
@@ -276,7 +276,8 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp)
return;
}
- if (!vhost_user_init(&s->vhost_user, &vs->conf.chardev, errp)) {
+ if (!vhost_user_init(&s->vhost_user, &vs->conf.chardev,
+ false, errp)) {
goto free_virtio;
}
diff --git a/hw/virtio/vhost-stub.c b/hw/virtio/vhost-stub.c
index 7321dc9210..d1e1075a31 100644
--- a/hw/virtio/vhost-stub.c
+++ b/hw/virtio/vhost-stub.c
@@ -12,7 +12,8 @@ unsigned int vhost_get_free_memslots(void)
return UINT_MAX;
}
-bool vhost_user_init(VhostUserState *user, CharFrontend *chr, Error **errp)
+bool vhost_user_init(VhostUserState *user, CharFrontend *chr,
+ bool memory_isolation, Error **errp)
{
return false;
}
diff --git a/hw/virtio/vhost-user-base.c b/hw/virtio/vhost-user-base.c
index 90bd2903db..c10851492a 100644
--- a/hw/virtio/vhost-user-base.c
+++ b/hw/virtio/vhost-user-base.c
@@ -317,7 +317,8 @@ static void vub_device_realize(DeviceState *dev, Error **errp)
vub->vhost_user.supports_config = true;
}
- if (!vhost_user_init(&vub->vhost_user, &vub->chardev, errp)) {
+ if (!vhost_user_init(&vub->vhost_user, &vub->chardev,
+ false, errp)) {
return;
}
diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c
index 209993918a..34ccbdec10 100644
--- a/hw/virtio/vhost-user-fs.c
+++ b/hw/virtio/vhost-user-fs.c
@@ -238,7 +238,8 @@ static void vuf_device_realize(DeviceState *dev, Error **errp)
return;
}
- if (!vhost_user_init(&fs->vhost_user, &fs->conf.chardev, errp)) {
+ if (!vhost_user_init(&fs->vhost_user, &fs->conf.chardev,
+ false, errp)) {
return;
}
diff --git a/hw/virtio/vhost-user-scmi.c b/hw/virtio/vhost-user-scmi.c
index 02dc088ea9..bd0ec64daf 100644
--- a/hw/virtio/vhost-user-scmi.c
+++ b/hw/virtio/vhost-user-scmi.c
@@ -244,7 +244,8 @@ static void vu_scmi_device_realize(DeviceState *dev, Error **errp)
vdev->host_features |= (1ULL << VIRTIO_SCMI_F_P2A_CHANNELS);
- if (!vhost_user_init(&scmi->vhost_user, &scmi->chardev, errp)) {
+ if (!vhost_user_init(&scmi->vhost_user, &scmi->chardev,
+ false, errp)) {
return;
}
diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c
index c2cd376e73..44a90c76ad 100644
--- a/hw/virtio/vhost-user-vsock.c
+++ b/hw/virtio/vhost-user-vsock.c
@@ -106,7 +106,8 @@ static void vuv_device_realize(DeviceState *dev, Error **errp)
return;
}
- if (!vhost_user_init(&vsock->vhost_user, &vsock->conf.chardev, errp)) {
+ if (!vhost_user_init(&vsock->vhost_user, &vsock->conf.chardev,
+ false, errp)) {
return;
}
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 2881cec72d..1c003e4d9d 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -3121,13 +3121,16 @@ static void vhost_user_state_destroy(gpointer data)
vhost_user_host_notifier_remove(n, NULL, true);
}
-bool vhost_user_init(VhostUserState *user, CharFrontend *chr, Error **errp)
+bool vhost_user_init(VhostUserState *user, CharFrontend *chr,
+ bool memory_isolation,
+ Error **errp)
{
if (user->chr) {
error_setg(errp, "Cannot initialize vhost-user state");
return false;
}
user->chr = chr;
+ user->memory_isolation = memory_isolation;
user->memory_slots = 0;
user->notifiers = g_ptr_array_new_full(VIRTIO_QUEUE_MAX / 4,
&vhost_user_state_destroy);
diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h
index 06c360af18..0eca9b1668 100644
--- a/include/hw/virtio/vhost-user.h
+++ b/include/hw/virtio/vhost-user.h
@@ -66,6 +66,7 @@ typedef struct VhostUserHostNotifier {
* @chr: the character backend for the socket
* @notifiers: GPtrArray of @VhostUserHostnotifier
* @memory_slots:
+ * @memory_isolation: determines whether data is shared or copied
*/
typedef struct VhostUserState {
CharFrontend *chr;
@@ -73,12 +74,14 @@ typedef struct VhostUserState {
int memory_slots;
bool supports_config;
bool supports_inflight_migration;
+ bool memory_isolation;
} VhostUserState;
/**
* vhost_user_init() - initialise shared vhost_user state
* @user: allocated area for storing shared state
* @chr: the chardev for the vhost socket
+ * @memory_isolation: restrict direct access to guest memory from back-end
* @errp: error handle
*
* User can either directly g_new() space for the state or embed
@@ -87,7 +90,8 @@ typedef struct VhostUserState {
*
* Return: true on success, false on error while setting errp.
*/
-bool vhost_user_init(VhostUserState *user, CharFrontend *chr, Error **errp);
+bool vhost_user_init(VhostUserState *user, CharFrontend *chr,
+ bool memory_isolation, Error **errp);
/**
* vhost_user_cleanup() - cleanup state
diff --git a/include/system/vhost-user-backend.h b/include/system/vhost-user-backend.h
index 3184c8e799..c23f689182 100644
--- a/include/system/vhost-user-backend.h
+++ b/include/system/vhost-user-backend.h
@@ -41,7 +41,8 @@ struct VhostUserBackend {
};
int vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev,
- unsigned nvqs, Error **errp);
+ unsigned nvqs, bool memory_isolation,
+ Error **errp);
void vhost_user_backend_start(VhostUserBackend *b);
int vhost_user_backend_stop(VhostUserBackend *b);
diff --git a/net/passt.c b/net/passt.c
index 45440c399b..35ec6ad6de 100644
--- a/net/passt.c
+++ b/net/passt.c
@@ -433,7 +433,9 @@ static void passt_vhost_user_event(void *opaque, QEMUChrEvent event)
}
}
-static int net_passt_vhost_user_init(NetPasstState *s, Error **errp)
+static int net_passt_vhost_user_init(NetPasstState *s,
+ bool memory_isolation,
+ Error **errp)
{
Chardev *chr;
int sv[2];
@@ -457,7 +459,8 @@ static int net_passt_vhost_user_init(NetPasstState *s, Error **errp)
s->vhost_user = g_new0(struct VhostUserState, 1);
if (!qemu_chr_fe_init(&s->vhost_chr, chr, errp) ||
- !vhost_user_init(s->vhost_user, &s->vhost_chr, errp)) {
+ !vhost_user_init(s->vhost_user, &s->vhost_chr, memory_isolation,
+ errp)) {
goto err;
}
@@ -487,7 +490,8 @@ err:
return -1;
}
#else
-static int net_passt_vhost_user_init(NetPasstState *s, Error **errp)
+static int net_passt_vhost_user_init(NetPasstState *s, bool memory_isolation,
+ Error **errp)
{
error_setg(errp, "vhost-user support has not been built");
@@ -735,7 +739,9 @@ int net_init_passt(const Netdev *netdev, const char *name,
s->pidfile = pidfile;
if (netdev->u.passt.has_vhost_user && netdev->u.passt.vhost_user) {
- if (net_passt_vhost_user_init(s, errp) == -1) {
+ bool memory_isolation = false;
+
+ if (net_passt_vhost_user_init(s, memory_isolation, errp) == -1) {
qemu_del_net_client(nc);
return -1;
}
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 2d0fc49b4d..b6ee9ed297 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -370,7 +370,7 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event)
static int net_vhost_user_init(NetClientState *peer, const char *device,
const char *name, Chardev *chr,
- int queues)
+ int queues, bool memory_isolation)
{
Error *err = NULL;
NetClientState *nc, *nc0 = NULL;
@@ -390,7 +390,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
nc0 = nc;
s = DO_UPCAST(NetVhostUserState, nc, nc);
if (!qemu_chr_fe_init(&s->chr, chr, &err) ||
- !vhost_user_init(user, &s->chr, &err)) {
+ !vhost_user_init(user, &s->chr, memory_isolation, &err)) {
error_report_err(err);
goto err;
}
@@ -459,6 +459,7 @@ int net_init_vhost_user(const Netdev *netdev, const char *name,
int queues;
const NetdevVhostUserOptions *vhost_user_opts;
Chardev *chr;
+ bool memory_isolation = false;
assert(netdev->type == NET_CLIENT_DRIVER_VHOST_USER);
vhost_user_opts = &netdev->u.vhost_user;
@@ -476,5 +477,6 @@ int net_init_vhost_user(const Netdev *netdev, const char *name,
return -1;
}
- return net_vhost_user_init(peer, "vhost_user", name, chr, queues);
+ return net_vhost_user_init(peer, "vhost_user", name, chr, queues,
+ memory_isolation);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH RFC v2 07/13] hw/virtio/vhost-user: create isolation region
2026-08-18 5:12 [PATCH RFC v2 00/13] vhost-user: isolated memory Connor Kite
` (5 preceding siblings ...)
2026-08-18 5:12 ` [PATCH RFC v2 06/13] vhost-user: add memory_isolation to VhostUserState Connor Kite
@ 2026-08-18 5:12 ` Connor Kite
2026-08-18 5:12 ` [PATCH RFC v2 08/13] hw/virtio/vhost-user: send isolation regions to device Connor Kite
` (5 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Connor Kite @ 2026-08-18 5:12 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Alex Bennée,
Viresh Kumar, Gerd Hoffmann, Mathieu Poirier, Manos Pitsidianakis,
Raphael Norwitz, Kevin Wolf, Hanna Reitz, Marc-André Lureau,
Paolo Bonzini, Fam Zheng, Stefan Hajnoczi, Milan Zamazal,
Akihiko Odaki, Dmitry Osipenko, qemu-block, virtio-fs,
Gonglei (Arei), zhenwei pi, Daniel P. Berrangé, Eric Blake,
Markus Armbruster, Jason Wang, Peter Xu, Eugenio Pérez,
Alyssa Ross, Demi Marie Obenour, Connor Kite,
20260817233147.2867623-1-connorkite
If memory isolation mode is active for the vhost-user device adds
features to:
- Gather the size required for bounce buffers and vrings in shared
isolation region
- Allocate the required space in an anonymous file
- Create a vhost-iova-tree with space to map entire isolation region
- Map guest memory regions and shared vrings into the tree
- Release these resources upon backend cleanup
Signed-off-by: Connor Kite <connorkite@gmail.com>
---
hw/virtio/vhost-user.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 136 insertions(+)
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 1c003e4d9d..7e9233e174 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -18,6 +18,7 @@
#include "hw/virtio/vhost-backend.h"
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-net.h"
+#include "hw/virtio/vhost-iova-tree.h"
#include "chardev/char-fe.h"
#include "io/channel-socket.h"
#include "system/kvm.h"
@@ -25,6 +26,7 @@
#include "qemu/main-loop.h"
#include "qemu/uuid.h"
#include "qemu/sockets.h"
+#include "qemu/memfd.h"
#include "system/runstate.h"
#include "system/cryptodev.h"
#include "migration/postcopy-ram.h"
@@ -320,6 +322,17 @@ static VhostUserMsg m __attribute__ ((unused));
/* The version of the protocol we support */
#define VHOST_USER_VERSION (0x1)
+/* Memory region shared with back-end when memory-isolation is active */
+typedef struct {
+ void *shared_mem_addr; /* mapped shared memory */
+ VhostIOVATree *tree; /* controls mapping of regions into IOVA space */
+ void *vring_hva_addr; /* beginning of vring region in shared memory */
+ size_t vring_region_size; /* amount of shared memory reserved for vrings */
+ size_t size; /* size of the mapped shared memory */
+ int fd; /* descriptor of anonymous file backing shared iso region */
+ Int128 iso_iova_offset; /* translation from IOVA to hva of iso region */
+} IsolationModeCtx;
+
struct vhost_user {
struct vhost_dev *dev;
/* Shared between vhost devs of the same virtio device */
@@ -353,6 +366,9 @@ struct vhost_user {
* by the backend (see @features).
*/
uint64_t protocol_features;
+
+ /* Data specfic to isolated memory mode */
+ IsolationModeCtx iso_mem_ctx;
};
struct scrub_regions {
@@ -1112,6 +1128,125 @@ static int vhost_user_set_mem_table_postcopy(struct vhost_dev *dev,
return 0;
}
+static void cleanup_isolation_regions(struct vhost_dev *dev)
+{
+ struct vhost_user *u = dev->opaque;
+ if (u->iso_mem_ctx.shared_mem_addr) {
+ vhost_iova_tree_delete(u->iso_mem_ctx.tree);
+ qemu_memfd_free(u->iso_mem_ctx.shared_mem_addr,
+ u->iso_mem_ctx.size,
+ u->iso_mem_ctx.fd);
+ memset(&u->iso_mem_ctx, 0, sizeof(IsolationModeCtx));
+ }
+}
+
+__attribute__((unused))
+static int init_isolation_regions(struct vhost_dev *dev,
+ VhostUserMsg *msg,
+ int *fds, size_t *fd_num)
+{
+ Error *err = NULL;
+ struct vhost_user *u = dev->opaque;
+ uint32_t nregions = dev->mem->nregions;
+
+ g_autofree DMAMap *buffer_regions = g_new0(DMAMap, nregions);
+ size_t buffer_reg_size = 0;
+ size_t total_vring_size = 0;
+ size_t total_mmap_size;
+ char *reg_name;
+ uint64_t first_IOVA_addr;
+ uint64_t last_IOVA_addr;
+ DMAMap *map;
+ DMAMap vring_map;
+ int r;
+
+ msg->hdr.request = VHOST_USER_SET_MEM_TABLE;
+
+ /* In case of reset, clear old regions */
+ cleanup_isolation_regions(dev);
+
+ /* Gather information for bounce buffers to be mapped */
+ for (u_int32_t i = 0; i < nregions; i++) {
+ hwaddr size = ROUND_UP(dev->mem->regions[i].memory_size,
+ qemu_real_host_page_size());
+ buffer_regions[i].size = size - 1;
+ buffer_regions[i].perm = IOMMU_RW;
+
+ buffer_reg_size += size;
+ }
+
+ /* Get space required for all vrings */
+ for (int i = 0; i < dev->nvqs; i++) {
+ VirtQueue *vq = virtio_get_queue(dev->vdev, dev->vq_index + i);
+ total_vring_size += vhost_svq_vring_total_size(dev->vdev, vq);
+ }
+
+ total_mmap_size = buffer_reg_size + total_vring_size;
+ u->iso_mem_ctx.size = total_mmap_size;
+
+ /* Allocate and map an anonymous file to hold the isolation region */
+ reg_name = g_strconcat("iso_mem_", dev->vdev->name, NULL);
+ u->iso_mem_ctx.shared_mem_addr = qemu_memfd_alloc(reg_name,
+ total_mmap_size,
+ F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL,
+ &u->iso_mem_ctx.fd, &err);
+
+ assert(u->iso_mem_ctx.fd >= 0);
+ g_free(reg_name);
+
+ if (err) {
+ error_report_err(err);
+ cleanup_isolation_regions(dev);
+ return -1;
+ }
+
+ /* vhost-iova-tree enforces non-zero lower address */
+ first_IOVA_addr = qemu_real_host_page_size();
+ last_IOVA_addr = first_IOVA_addr + total_mmap_size - 1;
+ assert(last_IOVA_addr > first_IOVA_addr);
+
+ /* Use 128-bit operation in case of large negative offset */
+ u->iso_mem_ctx.iso_iova_offset =
+ int128_sub(int128_make64((uint64_t)u->iso_mem_ctx.shared_mem_addr),
+ int128_make64(first_IOVA_addr));
+
+ /*
+ * Instantiates iova tree sized to map bounce buffers and vrings to the
+ * isolation region in host va.
+ */
+ u->iso_mem_ctx.tree =
+ vhost_iova_tree_new(first_IOVA_addr, last_IOVA_addr);
+
+ /* Map vrings into IOVA tree */
+ vring_map.perm = IOMMU_RW;
+ vring_map.size = total_vring_size - 1;
+ r = vhost_iova_tree_map_alloc(u->iso_mem_ctx.tree, &vring_map,
+ (hwaddr)u->iso_mem_ctx.shared_mem_addr);
+
+ if (r != IOVA_OK) {
+ cleanup_isolation_regions(dev);
+ return r;
+ }
+
+ u->iso_mem_ctx.vring_hva_addr = (void *)int128_get64(
+ int128_add(int128_make64(vring_map.iova),
+ u->iso_mem_ctx.iso_iova_offset));
+ u->iso_mem_ctx.vring_region_size = total_vring_size;
+
+ for (int i = 0; i < nregions; i++) {
+ map = &buffer_regions[i];
+ r = vhost_iova_tree_map_alloc_gpa(u->iso_mem_ctx.tree, map,
+ dev->mem->regions[i].guest_phys_addr);
+
+ if (r != IOVA_OK) {
+ cleanup_isolation_regions(dev);
+ return r;
+ }
+ }
+
+ return 0;
+}
+
static int vhost_user_set_mem_table(struct vhost_dev *dev,
struct vhost_memory *mem)
{
@@ -2684,6 +2819,7 @@ static int vhost_user_backend_cleanup(struct vhost_dev *dev)
g_free(u->region_rb_offset);
u->region_rb_offset = NULL;
u->region_rb_len = 0;
+ cleanup_isolation_regions(dev);
g_free(u);
dev->opaque = 0;
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH RFC v2 08/13] hw/virtio/vhost-user: send isolation regions to device
2026-08-18 5:12 [PATCH RFC v2 00/13] vhost-user: isolated memory Connor Kite
` (6 preceding siblings ...)
2026-08-18 5:12 ` [PATCH RFC v2 07/13] hw/virtio/vhost-user: create isolation region Connor Kite
@ 2026-08-18 5:12 ` 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
` (4 subsequent siblings)
12 siblings, 1 reply; 15+ messages in thread
From: Connor Kite @ 2026-08-18 5:12 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Alex Bennée,
Viresh Kumar, Gerd Hoffmann, Mathieu Poirier, Manos Pitsidianakis,
Raphael Norwitz, Kevin Wolf, Hanna Reitz, Marc-André Lureau,
Paolo Bonzini, Fam Zheng, Stefan Hajnoczi, Milan Zamazal,
Akihiko Odaki, Dmitry Osipenko, qemu-block, virtio-fs,
Gonglei (Arei), zhenwei pi, Daniel P. Berrangé, Eric Blake,
Markus Armbruster, Jason Wang, Peter Xu, Eugenio Pérez,
Alyssa Ross, Demi Marie Obenour, Connor Kite,
20260817233147.2867623-1-connorkite
Adds features to fill a vhost_user_set_mem_table message with the
addresses of isolation memory regions corresponding to bounce buffers
and vrings.
Signed-off-by: Connor Kite <connorkite@gmail.com>
---
hw/virtio/vhost-user.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 117 insertions(+), 1 deletion(-)
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 7e9233e174..e1e5cba53d 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -1140,7 +1140,77 @@ static void cleanup_isolation_regions(struct vhost_dev *dev)
}
}
-__attribute__((unused))
+typedef struct {
+ VhostUserMsg *msg;
+ struct vhost_user *u;
+ int *fds;
+ size_t fds_size;
+ uint64_t vring_iova;
+ size_t vring_size;
+ bool vring_node_visited;
+ size_t *idx;
+} IOVATreeTraversalArgs;
+
+static gboolean vhost_user_fill_msg_reg_from_tree(gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ IOVATreeTraversalArgs *args = data;
+ struct vhost_user *u;
+ VhostUserMsg *msg = args->msg;
+ DMAMap *map = key;
+ uint64_t offset;
+
+ assert(args && key && value);
+ if (!args->vring_node_visited) {
+ args->vring_node_visited = true;
+ return false;
+ }
+
+ u = args->u;
+ assert(*args->idx < args->fds_size);
+
+ args->fds[*args->idx] = args->u->iso_mem_ctx.fd;
+
+ /*
+ * If the number of regions is fixed, it would be wasteful to use one for
+ * only the vrings. The first vhost_iova_tree element is always
+ * reserved for the vrings, so we can simply combine the first and second
+ * elements, which are contiguous in IOVA space, when sending regions to
+ * the backend.
+ */
+ if (*args->idx == 0) {
+ offset = u->iso_mem_ctx.vring_hva_addr - u->iso_mem_ctx.shared_mem_addr;
+ msg->payload.memory.regions[*args->idx].userspace_addr =
+ args->vring_iova;
+ /*
+ * The size from the iova tree is inclusive, so 1 is added to it.
+ * args->vring_size is exclusive, so no addition is required.
+ */
+ msg->payload.memory.regions[*args->idx].memory_size =
+ args->vring_size + map->size + 1;
+ msg->payload.memory.regions[*args->idx].guest_phys_addr =
+ args->vring_iova;
+ msg->payload.memory.regions[*args->idx].mmap_offset = offset;
+ } else {
+ /* Use 128 bit operation in unlikely case of negative iso_iova_offset */
+ offset = int128_get64(int128_add(int128_make64(map->iova),
+ u->iso_mem_ctx.iso_iova_offset)) -
+ (uint64_t)u->iso_mem_ctx.shared_mem_addr;
+
+ msg->payload.memory.regions[*args->idx].userspace_addr = map->iova;
+ msg->payload.memory.regions[*args->idx].memory_size = map->size + 1;
+ msg->payload.memory.regions[*args->idx].guest_phys_addr = map->iova;
+ msg->payload.memory.regions[*args->idx].mmap_offset = offset;
+ }
+
+ assert(offset + msg->payload.memory.regions[*args->idx].memory_size <=
+ u->iso_mem_ctx.size);
+ (*args->idx)++;
+
+ return false;
+}
+
static int init_isolation_regions(struct vhost_dev *dev,
VhostUserMsg *msg,
int *fds, size_t *fd_num)
@@ -1159,6 +1229,7 @@ static int init_isolation_regions(struct vhost_dev *dev,
DMAMap *map;
DMAMap vring_map;
int r;
+ IOVATreeTraversalArgs trav_args;
msg->hdr.request = VHOST_USER_SET_MEM_TABLE;
@@ -1244,6 +1315,27 @@ static int init_isolation_regions(struct vhost_dev *dev,
}
}
+ *fd_num = 0;
+ trav_args.idx = fd_num;
+ trav_args.fds = fds;
+ trav_args.msg = msg;
+ trav_args.u = u;
+ trav_args.vring_iova = vring_map.iova;
+ trav_args.vring_size = total_vring_size;
+ trav_args.fds_size = nregions;
+ trav_args.vring_node_visited = false;
+
+ vhost_iova_tree_foreach(u->iso_mem_ctx.tree,
+ vhost_user_fill_msg_reg_from_tree, &trav_args);
+
+ msg->payload.memory.nregions = *fd_num;
+
+ assert(*fd_num == nregions);
+
+ msg->hdr.size = sizeof(msg->payload.memory.nregions);
+ msg->hdr.size += sizeof(msg->payload.memory.padding);
+ msg->hdr.size += *fd_num * sizeof(VhostUserMemoryRegion);
+
return 0;
}
@@ -1251,6 +1343,7 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev,
struct vhost_memory *mem)
{
struct vhost_user *u = dev->opaque;
+ bool memory_isolation = u->user->memory_isolation;
int fds[VHOST_MEMORY_BASELINE_NREGIONS];
size_t fd_num = 0;
bool do_postcopy = u->postcopy_listen && u->postcopy_fd.handler;
@@ -1262,6 +1355,11 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev,
int ret;
if (do_postcopy) {
+ /* Postcopy is not supported with memory isolation yet */
+ if (memory_isolation) {
+ return -1;
+ }
+
/*
* Postcopy has enough differences that it's best done in it's own
* version
@@ -1278,6 +1376,24 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev,
msg.hdr.flags |= VHOST_USER_NEED_REPLY_MASK;
}
+ if (memory_isolation) {
+ ret = init_isolation_regions(dev, &msg, fds, &fd_num);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = vhost_user_write(dev, &msg, fds, fd_num);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (reply_supported) {
+ return process_message_reply(dev, &msg);
+ }
+
+ return 0;
+ }
+
if (config_mem_slots) {
ret = vhost_user_add_remove_regions(dev, &msg, reply_supported, false);
if (ret < 0) {
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH RFC v2 08/13] hw/virtio/vhost-user: send isolation regions to device
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
0 siblings, 0 replies; 15+ messages in thread
From: Akihiko Odaki @ 2026-08-18 11:31 UTC (permalink / raw)
To: Connor Kite, qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Alex Bennée,
Viresh Kumar, Gerd Hoffmann, Mathieu Poirier, Manos Pitsidianakis,
Raphael Norwitz, Kevin Wolf, Hanna Reitz, Marc-André Lureau,
Paolo Bonzini, Fam Zheng, Stefan Hajnoczi, Milan Zamazal,
Dmitry Osipenko, qemu-block, virtio-fs, Gonglei (Arei),
zhenwei pi, Daniel P. Berrangé, Eric Blake,
Markus Armbruster, Jason Wang, Peter Xu, Eugenio Pérez,
Alyssa Ross, Demi Marie Obenour,
20260817233147.2867623-1-connorkite
On 2026/08/18 14:12, Connor Kite wrote:
> Adds features to fill a vhost_user_set_mem_table message with the
> addresses of isolation memory regions corresponding to bounce buffers
> and vrings.
>
> Signed-off-by: Connor Kite <connorkite@gmail.com>
> ---
> hw/virtio/vhost-user.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 117 insertions(+), 1 deletion(-)
>
> diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
> index 7e9233e174..e1e5cba53d 100644
> --- a/hw/virtio/vhost-user.c
> +++ b/hw/virtio/vhost-user.c
> @@ -1140,7 +1140,77 @@ static void cleanup_isolation_regions(struct vhost_dev *dev)
> }
> }
>
> -__attribute__((unused))
> +typedef struct {
> + VhostUserMsg *msg;
> + struct vhost_user *u;
> + int *fds;
> + size_t fds_size;
> + uint64_t vring_iova;
> + size_t vring_size;
> + bool vring_node_visited;
> + size_t *idx;
> +} IOVATreeTraversalArgs;
> +
> +static gboolean vhost_user_fill_msg_reg_from_tree(gpointer key,
> + gpointer value,
> + gpointer data)
> +{
> + IOVATreeTraversalArgs *args = data;
> + struct vhost_user *u;
> + VhostUserMsg *msg = args->msg;
> + DMAMap *map = key;
> + uint64_t offset;
> +
> + assert(args && key && value);
> + if (!args->vring_node_visited) {
> + args->vring_node_visited = true;
> + return false;
> + }
> +
> + u = args->u;
> + assert(*args->idx < args->fds_size);
> +
> + args->fds[*args->idx] = args->u->iso_mem_ctx.fd;
> +
> + /*
> + * If the number of regions is fixed, it would be wasteful to use one for
> + * only the vrings. The first vhost_iova_tree element is always
> + * reserved for the vrings, so we can simply combine the first and second
> + * elements, which are contiguous in IOVA space, when sending regions to
> + * the backend.
> + */
> + if (*args->idx == 0) {
> + offset = u->iso_mem_ctx.vring_hva_addr - u->iso_mem_ctx.shared_mem_addr;
> + msg->payload.memory.regions[*args->idx].userspace_addr =
> + args->vring_iova;
> + /*
> + * The size from the iova tree is inclusive, so 1 is added to it.
> + * args->vring_size is exclusive, so no addition is required.
> + */
> + msg->payload.memory.regions[*args->idx].memory_size =
> + args->vring_size + map->size + 1;
> + msg->payload.memory.regions[*args->idx].guest_phys_addr =
> + args->vring_iova;
> + msg->payload.memory.regions[*args->idx].mmap_offset = offset;
> + } else {
> + /* Use 128 bit operation in unlikely case of negative iso_iova_offset */
> + offset = int128_get64(int128_add(int128_make64(map->iova),
> + u->iso_mem_ctx.iso_iova_offset)) -
> + (uint64_t)u->iso_mem_ctx.shared_mem_addr;
> +
> + msg->payload.memory.regions[*args->idx].userspace_addr = map->iova;
> + msg->payload.memory.regions[*args->idx].memory_size = map->size + 1;
> + msg->payload.memory.regions[*args->idx].guest_phys_addr = map->iova;
> + msg->payload.memory.regions[*args->idx].mmap_offset = offset;
> + }
> +
> + assert(offset + msg->payload.memory.regions[*args->idx].memory_size <=
> + u->iso_mem_ctx.size);
> + (*args->idx)++;
> +
> + return false;
> +}
> +
> static int init_isolation_regions(struct vhost_dev *dev,
> VhostUserMsg *msg,
> int *fds, size_t *fd_num)
> @@ -1159,6 +1229,7 @@ static int init_isolation_regions(struct vhost_dev *dev,
> DMAMap *map;
> DMAMap vring_map;
> int r;
> + IOVATreeTraversalArgs trav_args;
>
> msg->hdr.request = VHOST_USER_SET_MEM_TABLE;
>
> @@ -1244,6 +1315,27 @@ static int init_isolation_regions(struct vhost_dev *dev,
> }
> }
>
> + *fd_num = 0;
> + trav_args.idx = fd_num;
> + trav_args.fds = fds;
> + trav_args.msg = msg;
> + trav_args.u = u;
> + trav_args.vring_iova = vring_map.iova;
> + trav_args.vring_size = total_vring_size;
> + trav_args.fds_size = nregions;
> + trav_args.vring_node_visited = false;
> +
> + vhost_iova_tree_foreach(u->iso_mem_ctx.tree,
> + vhost_user_fill_msg_reg_from_tree, &trav_args);
> +
> + msg->payload.memory.nregions = *fd_num;
> +
> + assert(*fd_num == nregions);
> +
> + msg->hdr.size = sizeof(msg->payload.memory.nregions);
> + msg->hdr.size += sizeof(msg->payload.memory.padding);
> + msg->hdr.size += *fd_num * sizeof(VhostUserMemoryRegion);
> +
> return 0;
> }
>
> @@ -1251,6 +1343,7 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev,
> struct vhost_memory *mem)
> {
> struct vhost_user *u = dev->opaque;
> + bool memory_isolation = u->user->memory_isolation;
> int fds[VHOST_MEMORY_BASELINE_NREGIONS];
> size_t fd_num = 0;
> bool do_postcopy = u->postcopy_listen && u->postcopy_fd.handler;
> @@ -1262,6 +1355,11 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev,
> int ret;
>
> if (do_postcopy) {
> + /* Postcopy is not supported with memory isolation yet */
> + if (memory_isolation) {
> + return -1;
> + }
> +
> /*
> * Postcopy has enough differences that it's best done in it's own
> * version
> @@ -1278,6 +1376,24 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev,
> msg.hdr.flags |= VHOST_USER_NEED_REPLY_MASK;
> }
>
> + if (memory_isolation) {
> + ret = init_isolation_regions(dev, &msg, fds, &fd_num);
> + if (ret < 0) {
> + return ret;
> + }
> +
> + ret = vhost_user_write(dev, &msg, fds, fd_num);
> + if (ret < 0) {
> + return ret;
> + }
> +
> + if (reply_supported) {
> + return process_message_reply(dev, &msg);
> + }
> +
> + return 0;
> + }
> +
This bypasses vhost_user_fill_set_mem_table_msg() below and suppresses
the error saying: "Failed initializing vhost-user memory map, consider
using -object memory-backend-file share=on"
However, vhost-user doesn't work even with memory isolation in such a
case because vhost_user_no_private_memslots() always returns true and
that makes vhost_section() filter private memory out. This restriction
is unnecessary with memory isolation and can be removed instead of
restoring the error.
This also lacks handling of VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS,
and will overflow fds and VhostUserMemory::regions when
VHOST_USER_GET_MAX_MEM_SLOTS is larger than VHOST_MEMORY_BASELINE_NREGIONS.
Regards,
Akihiko Odaki
> if (config_mem_slots) {
> ret = vhost_user_add_remove_regions(dev, &msg, reply_supported, false);
> if (ret < 0) {
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH RFC v2 09/13] hw/virtio/vhost-user: add shadow virtqueues and eventfd intercepts
2026-08-18 5:12 [PATCH RFC v2 00/13] vhost-user: isolated memory Connor Kite
` (7 preceding siblings ...)
2026-08-18 5:12 ` [PATCH RFC v2 08/13] hw/virtio/vhost-user: send isolation regions to device Connor Kite
@ 2026-08-18 5:12 ` Connor Kite
2026-08-18 5:12 ` [PATCH RFC v2 10/13] hw/virtio/vhost-user: handle data movement with shadow vqs Connor Kite
` (3 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Connor Kite @ 2026-08-18 5:12 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Alex Bennée,
Viresh Kumar, Gerd Hoffmann, Mathieu Poirier, Manos Pitsidianakis,
Raphael Norwitz, Kevin Wolf, Hanna Reitz, Marc-André Lureau,
Paolo Bonzini, Fam Zheng, Stefan Hajnoczi, Milan Zamazal,
Akihiko Odaki, Dmitry Osipenko, qemu-block, virtio-fs,
Gonglei (Arei), zhenwei pi, Daniel P. Berrangé, Eric Blake,
Markus Armbruster, Jason Wang, Peter Xu, Eugenio Pérez,
Alyssa Ross, Demi Marie Obenour, Connor Kite,
20260817233147.2867623-1-connorkite
Adds shadow virtqueues that will eventually be used to transfer data
between device and host via bounce buffers when isolation mode is
active. The svqs are initalized, and eventfd assignments are
intercepted so that notifications come to svqs first before
the guest or backend receive them.
Signed-off-by: Connor Kite <connorkite@gmail.com>
---
hw/virtio/vhost-user.c | 136 ++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 119 insertions(+), 17 deletions(-)
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index e1e5cba53d..ace328f5eb 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -18,6 +18,7 @@
#include "hw/virtio/vhost-backend.h"
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-net.h"
+#include "hw/virtio/vhost-shadow-virtqueue.h"
#include "hw/virtio/vhost-iova-tree.h"
#include "chardev/char-fe.h"
#include "io/channel-socket.h"
@@ -331,6 +332,7 @@ typedef struct {
size_t size; /* size of the mapped shared memory */
int fd; /* descriptor of anonymous file backing shared iso region */
Int128 iso_iova_offset; /* translation from IOVA to hva of iso region */
+ GPtrArray *shadow_vqs; /* shadow vqs with vrings in iso region*/
} IsolationModeCtx;
struct vhost_user {
@@ -1128,15 +1130,38 @@ static int vhost_user_set_mem_table_postcopy(struct vhost_dev *dev,
return 0;
}
-static void cleanup_isolation_regions(struct vhost_dev *dev)
+static void vhost_user_svq_cleanup(struct vhost_user *u, bool reset)
+{
+ VhostShadowVirtqueue *svq;
+ for (int i = 0; i < u->iso_mem_ctx.shadow_vqs->len; i++) {
+ svq = g_ptr_array_index(u->iso_mem_ctx.shadow_vqs, i);
+ vhost_svq_stop(svq);
+ event_notifier_cleanup(&svq->hdev_call);
+ event_notifier_cleanup(&svq->hdev_kick);
+ }
+
+ if (!reset) {
+ g_ptr_array_free(u->iso_mem_ctx.shadow_vqs, true);
+ }
+}
+
+static void cleanup_isolation_regions(struct vhost_dev *dev, bool reset)
{
struct vhost_user *u = dev->opaque;
if (u->iso_mem_ctx.shared_mem_addr) {
+ vhost_user_svq_cleanup(u, reset);
vhost_iova_tree_delete(u->iso_mem_ctx.tree);
qemu_memfd_free(u->iso_mem_ctx.shared_mem_addr,
u->iso_mem_ctx.size,
u->iso_mem_ctx.fd);
+
+ GPtrArray *temp = u->iso_mem_ctx.shadow_vqs;
memset(&u->iso_mem_ctx, 0, sizeof(IsolationModeCtx));
+
+ if (!reset) {
+ u->iso_mem_ctx.shadow_vqs = temp;
+ }
+
}
}
@@ -1234,7 +1259,7 @@ static int init_isolation_regions(struct vhost_dev *dev,
msg->hdr.request = VHOST_USER_SET_MEM_TABLE;
/* In case of reset, clear old regions */
- cleanup_isolation_regions(dev);
+ cleanup_isolation_regions(dev, true);
/* Gather information for bounce buffers to be mapped */
for (u_int32_t i = 0; i < nregions; i++) {
@@ -1267,7 +1292,7 @@ static int init_isolation_regions(struct vhost_dev *dev,
if (err) {
error_report_err(err);
- cleanup_isolation_regions(dev);
+ cleanup_isolation_regions(dev, false);
return -1;
}
@@ -1295,7 +1320,7 @@ static int init_isolation_regions(struct vhost_dev *dev,
(hwaddr)u->iso_mem_ctx.shared_mem_addr);
if (r != IOVA_OK) {
- cleanup_isolation_regions(dev);
+ cleanup_isolation_regions(dev, false);
return r;
}
@@ -1310,7 +1335,7 @@ static int init_isolation_regions(struct vhost_dev *dev,
dev->mem->regions[i].guest_phys_addr);
if (r != IOVA_OK) {
- cleanup_isolation_regions(dev);
+ cleanup_isolation_regions(dev, false);
return r;
}
}
@@ -1738,11 +1763,49 @@ 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)
{
- int ret = vhost_set_vring_file(dev, VHOST_USER_SET_VRING_KICK, file);
+ struct vhost_user *u = dev->opaque;
+ int svq_idx = file->index - dev->vq_index;
+ VhostShadowVirtqueue *svq = NULL;
+ struct vhost_vring_file vr_file = *file;
+ int ret;
+
+ vhost_user_get_vq_index(dev, file->index); /* bounds checking */
+
+ if (u->user->memory_isolation) {
+ svq = g_ptr_array_index(u->iso_mem_ctx.shadow_vqs, svq_idx);
+ vhost_svq_set_svq_kick_fd(svq, file->fd);
+
+ if (file->fd != -1) {
+ if (!svq->hdev_kick.initialized) {
+ ret = event_notifier_init(&svq->hdev_kick, 0);
+ if (ret < 0) {
+ event_notifier_cleanup(&svq->hdev_kick);
+ error_report("Failed to create kick event notifier");
+ return ret;
+ }
+ }
+
+ vr_file.fd = event_notifier_get_fd(&svq->hdev_kick);
+ } else {
+ event_notifier_cleanup(&svq->hdev_kick);
+ }
+ }
+
+ ret = vhost_set_vring_file(dev, VHOST_USER_SET_VRING_KICK, &vr_file);
if (ret < 0) {
+ if (svq != NULL) {
+ event_notifier_cleanup(&svq->hdev_kick);
+ }
return ret;
}
@@ -1750,15 +1813,18 @@ static int vhost_user_set_vring_kick(struct vhost_dev *dev,
* Inject a kick in case the back-end only starts vring processing upon
* receiving a kick. The spec suggests this to improve compatibility.
*/
- if (file->fd != -1) {
+ if (vr_file.fd != -1) {
uint64_t val = 1;
ssize_t nwritten;
do {
- nwritten = write(file->fd, &val, sizeof(val));
+ nwritten = write(vr_file.fd, &val, sizeof(val));
} while (nwritten < 0 && errno == EINTR);
if (nwritten < 0 && errno != EAGAIN /* back-end can already read */) {
+ if (svq != NULL) {
+ event_notifier_cleanup(&svq->hdev_kick);
+ }
return -errno;
}
}
@@ -1769,7 +1835,35 @@ static int vhost_user_set_vring_kick(struct vhost_dev *dev,
static int vhost_user_set_vring_call(struct vhost_dev *dev,
struct vhost_vring_file *file)
{
- return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_CALL, file);
+ struct vhost_user *u = dev->opaque;
+ int svq_idx = file->index - dev->vq_index;
+ VhostShadowVirtqueue *svq = NULL;
+ struct vhost_vring_file vr_file = *file;
+ int ret;
+
+ vhost_user_get_vq_index(dev, file->index); /* bounds checking */
+
+ if (u->user->memory_isolation) {
+ svq = g_ptr_array_index(u->iso_mem_ctx.shadow_vqs, svq_idx);
+ vhost_svq_set_svq_call_fd(svq, file->fd);
+
+ if (file->fd != -1) {
+ if (!svq->hdev_call.initialized) {
+ ret = event_notifier_init(&svq->hdev_call, 0);
+ if (ret < 0) {
+ event_notifier_cleanup(&svq->hdev_call);
+ error_report("Failed to create call event notifier");
+ return ret;
+ }
+ }
+
+ vr_file.fd = event_notifier_get_fd(&svq->hdev_call);
+ } else {
+ event_notifier_cleanup(&svq->hdev_call);
+ }
+ }
+
+ return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_CALL, &vr_file);
}
static int vhost_user_set_vring_err(struct vhost_dev *dev,
@@ -2763,6 +2857,17 @@ static int vhost_user_postcopy_notifier(NotifierWithReturn *notifier,
return 0;
}
+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);
+ g_ptr_array_add(u->iso_mem_ctx.shadow_vqs, svq);
+ }
+}
+
static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque,
Error **errp)
{
@@ -2907,6 +3012,10 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque,
u->postcopy_notifier.notify = vhost_user_postcopy_notifier;
postcopy_add_notifier(&u->postcopy_notifier);
+ if (vus->memory_isolation) {
+ vhost_user_init_svq(dev, u);
+ }
+
return 0;
}
@@ -2935,20 +3044,13 @@ static int vhost_user_backend_cleanup(struct vhost_dev *dev)
g_free(u->region_rb_offset);
u->region_rb_offset = NULL;
u->region_rb_len = 0;
- cleanup_isolation_regions(dev);
+ cleanup_isolation_regions(dev, false);
g_free(u);
dev->opaque = 0;
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_memslots_limit(struct vhost_dev *dev)
{
struct vhost_user *u = dev->opaque;
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH RFC v2 10/13] hw/virtio/vhost-user: handle data movement with shadow vqs
2026-08-18 5:12 [PATCH RFC v2 00/13] vhost-user: isolated memory Connor Kite
` (8 preceding siblings ...)
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 5:12 ` Connor Kite
2026-08-18 5:12 ` [PATCH RFC v2 11/13] vhost-user: Add memory-isolation qdev property to vhost-user devices Connor Kite
` (2 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Connor Kite @ 2026-08-18 5:12 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Alex Bennée,
Viresh Kumar, Gerd Hoffmann, Mathieu Poirier, Manos Pitsidianakis,
Raphael Norwitz, Kevin Wolf, Hanna Reitz, Marc-André Lureau,
Paolo Bonzini, Fam Zheng, Stefan Hajnoczi, Milan Zamazal,
Akihiko Odaki, Dmitry Osipenko, qemu-block, virtio-fs,
Gonglei (Arei), zhenwei pi, Daniel P. Berrangé, Eric Blake,
Markus Armbruster, Jason Wang, Peter Xu, Eugenio Pérez,
Alyssa Ross, Demi Marie Obenour, Connor Kite,
20260817233147.2867623-1-connorkite
- 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
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH RFC v2 11/13] vhost-user: Add memory-isolation qdev property to vhost-user devices
2026-08-18 5:12 [PATCH RFC v2 00/13] vhost-user: isolated memory Connor Kite
` (9 preceding siblings ...)
2026-08-18 5:12 ` [PATCH RFC v2 10/13] hw/virtio/vhost-user: handle data movement with shadow vqs Connor Kite
@ 2026-08-18 5:12 ` Connor Kite
2026-08-18 5:12 ` [PATCH RFC v2 12/13] backends/cryptodev-vhost-user: add memory isolation bool Connor Kite
2026-08-18 5:12 ` [PATCH RFC v2 13/13] net/vhost-user: add memory isolation Connor Kite
12 siblings, 0 replies; 15+ messages in thread
From: Connor Kite @ 2026-08-18 5:12 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Alex Bennée,
Viresh Kumar, Gerd Hoffmann, Mathieu Poirier, Manos Pitsidianakis,
Raphael Norwitz, Kevin Wolf, Hanna Reitz, Marc-André Lureau,
Paolo Bonzini, Fam Zheng, Stefan Hajnoczi, Milan Zamazal,
Akihiko Odaki, Dmitry Osipenko, qemu-block, virtio-fs,
Gonglei (Arei), zhenwei pi, Daniel P. Berrangé, Eric Blake,
Markus Armbruster, Jason Wang, Peter Xu, Eugenio Pérez,
Alyssa Ross, Demi Marie Obenour, Connor Kite,
20260817233147.2867623-1-connorkite
In support of future work to add a memory isolation mode to vhost-user devices, a
qdev property, "memory-isolation", is added to all vhost-user devices. This is
simply a bool representing whether the mode is active or not.
Signed-off-by: Connor Kite <connorkite@gmail.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
hw/block/vhost-user-blk.c | 3 ++-
hw/display/vhost-user-gpu.c | 3 ++-
hw/scsi/vhost-user-scsi.c | 4 +++-
hw/virtio/vhost-user-base.c | 6 ++++--
hw/virtio/vhost-user-fs.c | 4 +++-
hw/virtio/vhost-user-scmi.c | 4 +++-
hw/virtio/vhost-user-vsock.c | 4 +++-
include/hw/virtio/vhost-user-base.h | 1 +
include/hw/virtio/vhost-user-blk.h | 1 +
include/hw/virtio/vhost-user-fs.h | 1 +
include/hw/virtio/vhost-user-scmi.h | 1 +
include/hw/virtio/vhost-user-vsock.h | 1 +
include/hw/virtio/virtio-gpu.h | 1 +
include/hw/virtio/virtio-scsi.h | 1 +
14 files changed, 27 insertions(+), 8 deletions(-)
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
index 4640d7da04..5c1a19eb50 100644
--- a/hw/block/vhost-user-blk.c
+++ b/hw/block/vhost-user-blk.c
@@ -506,7 +506,7 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
}
if (!vhost_user_init(&s->vhost_user, &s->chardev,
- false, errp)) {
+ s->memory_isolation, errp)) {
return;
}
@@ -632,6 +632,7 @@ static const VMStateDescription vmstate_vhost_user_blk = {
static const Property vhost_user_blk_properties[] = {
DEFINE_PROP_CHR("chardev", VHostUserBlk, chardev),
+ DEFINE_PROP_BOOL("memory-isolation", VHostUserBlk, memory_isolation, false),
DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues,
VHOST_USER_BLK_AUTO_NUM_QUEUES),
DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128),
diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c
index 6655585b43..d8f710ed65 100644
--- a/hw/display/vhost-user-gpu.c
+++ b/hw/display/vhost-user-gpu.c
@@ -663,7 +663,7 @@ vhost_user_gpu_device_realize(DeviceState *qdev, Error **errp)
VirtIODevice *vdev = VIRTIO_DEVICE(g);
vhost_dev_set_config_notifier(&g->vhost->dev, &config_ops);
- if (vhost_user_backend_dev_init(g->vhost, vdev, 2, false,
+ if (vhost_user_backend_dev_init(g->vhost, vdev, 2, g->memory_isolation,
errp) < 0) {
return;
}
@@ -698,6 +698,7 @@ static struct vhost_dev *vhost_user_gpu_get_vhost(VirtIODevice *vdev)
static const Property vhost_user_gpu_properties[] = {
VIRTIO_GPU_BASE_PROPERTIES(VhostUserGPU, parent_obj.conf),
+ DEFINE_PROP_BOOL("memory-isolation", VhostUserGPU, memory_isolation, false),
};
static void
diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c
index 05bc788304..6f162a7854 100644
--- a/hw/scsi/vhost-user-scsi.c
+++ b/hw/scsi/vhost-user-scsi.c
@@ -277,7 +277,7 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp)
}
if (!vhost_user_init(&s->vhost_user, &vs->conf.chardev,
- false, errp)) {
+ vs->conf.memory_isolation, errp)) {
goto free_virtio;
}
@@ -347,6 +347,8 @@ static void vhost_user_scsi_unrealize(DeviceState *dev)
static const Property vhost_user_scsi_properties[] = {
DEFINE_PROP_CHR("chardev", VirtIOSCSICommon, conf.chardev),
+ DEFINE_PROP_BOOL("memory-isolation", VirtIOSCSICommon,
+ conf.memory_isolation, false),
DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0),
DEFINE_PROP_UINT32("num_queues", VirtIOSCSICommon, conf.num_queues,
VIRTIO_SCSI_AUTO_NUM_QUEUES),
diff --git a/hw/virtio/vhost-user-base.c b/hw/virtio/vhost-user-base.c
index c10851492a..657a7d174a 100644
--- a/hw/virtio/vhost-user-base.c
+++ b/hw/virtio/vhost-user-base.c
@@ -318,7 +318,7 @@ static void vub_device_realize(DeviceState *dev, Error **errp)
}
if (!vhost_user_init(&vub->vhost_user, &vub->chardev,
- false, errp)) {
+ vub->memory_isolation, errp)) {
return;
}
@@ -410,7 +410,9 @@ static void vub_device_unrealize(DeviceState *dev)
/*Define common qdev properties. Inherited by all children*/
static const Property vub_properties[] = {
- DEFINE_PROP_CHR("chardev", VHostUserBase, chardev)
+ DEFINE_PROP_CHR("chardev", VHostUserBase, chardev),
+ DEFINE_PROP_BOOL("memory-isolation", VHostUserBase, memory_isolation,
+ false),
};
diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c
index 34ccbdec10..5529a8afb0 100644
--- a/hw/virtio/vhost-user-fs.c
+++ b/hw/virtio/vhost-user-fs.c
@@ -239,7 +239,7 @@ static void vuf_device_realize(DeviceState *dev, Error **errp)
}
if (!vhost_user_init(&fs->vhost_user, &fs->conf.chardev,
- false, errp)) {
+ fs->conf.memory_isolation, errp)) {
return;
}
@@ -411,6 +411,8 @@ static const VMStateDescription vuf_backend_vmstate = {
static const Property vuf_properties[] = {
DEFINE_PROP_CHR("chardev", VHostUserFS, conf.chardev),
+ DEFINE_PROP_BOOL("memory-isolation", VHostUserFS, conf.memory_isolation,
+ false),
DEFINE_PROP_STRING("tag", VHostUserFS, conf.tag),
DEFINE_PROP_UINT16("num-request-queues", VHostUserFS,
conf.num_request_queues, 1),
diff --git a/hw/virtio/vhost-user-scmi.c b/hw/virtio/vhost-user-scmi.c
index bd0ec64daf..b32dc09b0c 100644
--- a/hw/virtio/vhost-user-scmi.c
+++ b/hw/virtio/vhost-user-scmi.c
@@ -245,7 +245,7 @@ static void vu_scmi_device_realize(DeviceState *dev, Error **errp)
vdev->host_features |= (1ULL << VIRTIO_SCMI_F_P2A_CHANNELS);
if (!vhost_user_init(&scmi->vhost_user, &scmi->chardev,
- false, errp)) {
+ scmi->memory_isolation, errp)) {
return;
}
@@ -288,6 +288,8 @@ static const VMStateDescription vu_scmi_vmstate = {
static const Property vu_scmi_properties[] = {
DEFINE_PROP_CHR("chardev", VHostUserSCMI, chardev),
+ DEFINE_PROP_BOOL("memory-isolation", VHostUserSCMI, memory_isolation,
+ false),
};
static void vu_scmi_class_init(ObjectClass *klass, const void *data)
diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c
index 44a90c76ad..7b62290167 100644
--- a/hw/virtio/vhost-user-vsock.c
+++ b/hw/virtio/vhost-user-vsock.c
@@ -107,7 +107,7 @@ static void vuv_device_realize(DeviceState *dev, Error **errp)
}
if (!vhost_user_init(&vsock->vhost_user, &vsock->conf.chardev,
- false, errp)) {
+ vsock->conf.memory_isolation, errp)) {
return;
}
@@ -155,6 +155,8 @@ static void vuv_device_unrealize(DeviceState *dev)
static const Property vuv_properties[] = {
DEFINE_PROP_CHR("chardev", VHostUserVSock, conf.chardev),
+ DEFINE_PROP_BOOL("memory-isolation", VHostUserVSock, conf.memory_isolation,
+ false),
};
static void vuv_class_init(ObjectClass *klass, const void *data)
diff --git a/include/hw/virtio/vhost-user-base.h b/include/hw/virtio/vhost-user-base.h
index d5d2f081ae..29bc40736c 100644
--- a/include/hw/virtio/vhost-user-base.h
+++ b/include/hw/virtio/vhost-user-base.h
@@ -25,6 +25,7 @@ struct VHostUserBase {
uint32_t num_vqs;
uint32_t vq_size; /* can't exceed VIRTIO_QUEUE_MAX */
uint32_t config_size;
+ bool memory_isolation;
/* State tracking */
VhostUserState vhost_user;
struct vhost_virtqueue *vhost_vq;
diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h
index dee848cfd8..8126447a86 100644
--- a/include/hw/virtio/vhost-user-blk.h
+++ b/include/hw/virtio/vhost-user-blk.h
@@ -30,6 +30,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(VHostUserBlk, VHOST_USER_BLK)
struct VHostUserBlk {
VirtIODevice parent_obj;
CharFrontend chardev;
+ bool memory_isolation;
int32_t bootindex;
struct virtio_blk_config blkcfg;
uint16_t num_queues;
diff --git a/include/hw/virtio/vhost-user-fs.h b/include/hw/virtio/vhost-user-fs.h
index 5699ea03a1..bf71990b9c 100644
--- a/include/hw/virtio/vhost-user-fs.h
+++ b/include/hw/virtio/vhost-user-fs.h
@@ -28,6 +28,7 @@ typedef struct {
char *tag;
uint16_t num_request_queues;
uint16_t queue_size;
+ bool memory_isolation;
} VHostUserFSConf;
struct VHostUserFS {
diff --git a/include/hw/virtio/vhost-user-scmi.h b/include/hw/virtio/vhost-user-scmi.h
index 30ab0d7ab7..d346b42ed8 100644
--- a/include/hw/virtio/vhost-user-scmi.h
+++ b/include/hw/virtio/vhost-user-scmi.h
@@ -19,6 +19,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(VHostUserSCMI, VHOST_USER_SCMI);
struct VHostUserSCMI {
VirtIODevice parent;
CharFrontend chardev;
+ bool memory_isolation;
struct vhost_virtqueue *vhost_vqs;
struct vhost_dev vhost_dev;
VhostUserState vhost_user;
diff --git a/include/hw/virtio/vhost-user-vsock.h b/include/hw/virtio/vhost-user-vsock.h
index 5db6938b8b..6288b26fa4 100644
--- a/include/hw/virtio/vhost-user-vsock.h
+++ b/include/hw/virtio/vhost-user-vsock.h
@@ -21,6 +21,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(VHostUserVSock, VHOST_USER_VSOCK)
typedef struct {
CharFrontend chardev;
+ bool memory_isolation;
} VHostUserVSockConf;
struct VHostUserVSock {
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 220231ec9d..d0de1437f8 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -276,6 +276,7 @@ struct VhostUserGPU {
CharFrontend vhost_chr;
QemuDmaBuf *dmabuf[VIRTIO_GPU_MAX_SCANOUTS];
bool backend_blocked;
+ bool memory_isolation;
};
#define MAX_SLOTS 4096
diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h
index b6028bb5cd..47fdc3785c 100644
--- a/include/hw/virtio/virtio-scsi.h
+++ b/include/hw/virtio/virtio-scsi.h
@@ -59,6 +59,7 @@ struct VirtIOSCSIConf {
char *vhostfd;
char *wwpn;
CharFrontend chardev;
+ bool memory_isolation;
uint32_t boot_tpgt;
IOThread *iothread;
IOThreadVirtQueueMappingList *iothread_vq_mapping_list;
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH RFC v2 12/13] backends/cryptodev-vhost-user: add memory isolation bool
2026-08-18 5:12 [PATCH RFC v2 00/13] vhost-user: isolated memory Connor Kite
` (10 preceding siblings ...)
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 ` Connor Kite
2026-08-18 5:12 ` [PATCH RFC v2 13/13] net/vhost-user: add memory isolation Connor Kite
12 siblings, 0 replies; 15+ messages in thread
From: Connor Kite @ 2026-08-18 5:12 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Alex Bennée,
Viresh Kumar, Gerd Hoffmann, Mathieu Poirier, Manos Pitsidianakis,
Raphael Norwitz, Kevin Wolf, Hanna Reitz, Marc-André Lureau,
Paolo Bonzini, Fam Zheng, Stefan Hajnoczi, Milan Zamazal,
Akihiko Odaki, Dmitry Osipenko, qemu-block, virtio-fs,
Gonglei (Arei), zhenwei pi, Daniel P. Berrangé, Eric Blake,
Markus Armbruster, Jason Wang, Peter Xu, Eugenio Pérez,
Alyssa Ross, Demi Marie Obenour, Connor Kite,
20260817233147.2867623-1-connorkite
Add a memory_isolation property to CryptoDevBackendVhostUser and
add add it as an optional member of CryptodevVhostUserProperties
in qapi.
This is needed to be able to specify memory isolation for
cryptodev-vhost-user objects, and the bool will eventually be
passed to vhost_user_init() in a future patch.
Signed-off-by: Connor Kite <connorkite@gmail.com>
---
backends/cryptodev-vhost-user.c | 28 +++++++++++++++++++++++++++-
qapi/qom.json | 8 +++++++-
2 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
index fa1f9b76d3..be8631446b 100644
--- a/backends/cryptodev-vhost-user.c
+++ b/backends/cryptodev-vhost-user.c
@@ -49,6 +49,7 @@ struct CryptoDevBackendVhostUser {
CharFrontend chr;
char *chr_name;
bool opened;
+ bool memory_isolation;
CryptoDevBackendVhost *vhost_crypto[MAX_CRYPTO_QUEUE_NUM];
};
@@ -213,7 +214,7 @@ static void cryptodev_vhost_user_init(
}
}
- if (!vhost_user_init(&s->vhost_user, &s->chr, false, errp)) {
+ if (!vhost_user_init(&s->vhost_user, &s->chr, s->memory_isolation, errp)) {
return;
}
@@ -392,6 +393,27 @@ static void cryptodev_vhost_user_finalize(Object *obj)
g_free(s->chr_name);
}
+static void cryptodev_vhost_user_set_mem_isolation(Object *obj, bool value,
+ Error **errp)
+{
+ CryptoDevBackendVhostUser *s =
+ CRYPTODEV_BACKEND_VHOST_USER(obj);
+
+ if (s->opened) {
+ error_setg(errp, "Property 'memory-isolation' can no longer be set");
+ } else{
+ s->memory_isolation = value;
+ }
+}
+
+static bool cryptodev_vhost_user_get_mem_isolation(Object *obj, Error **errp)
+{
+ CryptoDevBackendVhostUser *s =
+ CRYPTODEV_BACKEND_VHOST_USER(obj);
+
+ return s->memory_isolation;
+}
+
static void
cryptodev_vhost_user_class_init(ObjectClass *oc, const void *data)
{
@@ -407,6 +429,10 @@ cryptodev_vhost_user_class_init(ObjectClass *oc, const void *data)
cryptodev_vhost_user_get_chardev,
cryptodev_vhost_user_set_chardev);
+ object_class_property_add_bool(oc, "memory-isolation",
+ cryptodev_vhost_user_get_mem_isolation,
+ cryptodev_vhost_user_set_mem_isolation);
+
}
static const TypeInfo cryptodev_vhost_user_info = {
diff --git a/qapi/qom.json b/qapi/qom.json
index c55776af7d..1024027195 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -350,11 +350,17 @@
# @chardev: the name of a Unix domain socket character device that
# connects to the vhost-user server
#
+# @memory-isolation: disable direct access from vhost-user back-end
+# to guest memory. The back-end will instead access data via
+# bounce buffers residing in the QEMU virtual address space.
+# (default: false) (Since 11.2)
+#
# Since: 2.12
##
{ 'struct': 'CryptodevVhostUserProperties',
'base': 'CryptodevBackendProperties',
- 'data': { 'chardev': 'str' },
+ 'data': { 'chardev': 'str',
+ '*memory-isolation': 'bool' },
'if': 'CONFIG_VHOST_CRYPTO' }
##
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH RFC v2 13/13] net/vhost-user: add memory isolation
2026-08-18 5:12 [PATCH RFC v2 00/13] vhost-user: isolated memory Connor Kite
` (11 preceding siblings ...)
2026-08-18 5:12 ` [PATCH RFC v2 12/13] backends/cryptodev-vhost-user: add memory isolation bool Connor Kite
@ 2026-08-18 5:12 ` Connor Kite
12 siblings, 0 replies; 15+ messages in thread
From: Connor Kite @ 2026-08-18 5:12 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Alex Bennée,
Viresh Kumar, Gerd Hoffmann, Mathieu Poirier, Manos Pitsidianakis,
Raphael Norwitz, Kevin Wolf, Hanna Reitz, Marc-André Lureau,
Paolo Bonzini, Fam Zheng, Stefan Hajnoczi, Milan Zamazal,
Akihiko Odaki, Dmitry Osipenko, qemu-block, virtio-fs,
Gonglei (Arei), zhenwei pi, Daniel P. Berrangé, Eric Blake,
Markus Armbruster, Jason Wang, Peter Xu, Eugenio Pérez,
Alyssa Ross, Demi Marie Obenour, Connor Kite,
20260817233147.2867623-1-connorkite
Add a memory-isolation property to NetdevPassOptions and
NetDevVhostUserOptions in net.json. This property will
be used for vhost-user devices to disable access to guest
memory from vhost-user backends.
Signed-off-by: Connor Kite <connorkite@gmail.com>
---
net/passt.c | 5 +++++
net/vhost-user.c | 3 +++
qapi/net.json | 16 ++++++++++++----
3 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/net/passt.c b/net/passt.c
index 35ec6ad6de..7bcd47ad32 100644
--- a/net/passt.c
+++ b/net/passt.c
@@ -741,6 +741,11 @@ int net_init_passt(const Netdev *netdev, const char *name,
if (netdev->u.passt.has_vhost_user && netdev->u.passt.vhost_user) {
bool memory_isolation = false;
+ if (netdev->u.passt.has_memory_isolation &&
+ netdev->u.passt.memory_isolation) {
+ memory_isolation = true;
+ }
+
if (net_passt_vhost_user_init(s, memory_isolation, errp) == -1) {
qemu_del_net_client(nc);
return -1;
diff --git a/net/vhost-user.c b/net/vhost-user.c
index b6ee9ed297..bd33decadf 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -477,6 +477,9 @@ int net_init_vhost_user(const Netdev *netdev, const char *name,
return -1;
}
+ memory_isolation = vhost_user_opts->has_memory_isolation ?
+ vhost_user_opts->memory_isolation : false;
+
return net_vhost_user_init(peer, "vhost_user", name, chr, queues,
memory_isolation);
}
diff --git a/qapi/net.json b/qapi/net.json
index 1a6382825c..add7cb5408 100644
--- a/qapi/net.json
+++ b/qapi/net.json
@@ -213,6 +213,9 @@
#
# @param: parameter to pass to passt command
#
+# @memory-isolation: isolate guest memory from vhost-user back-end
+# (default: false) (Since 11.2)
+#
# Since: 10.1
##
{ 'struct': 'NetdevPasstOptions',
@@ -250,7 +253,8 @@
'*ipv6': 'bool',
'*tcp-ports': ['PasstPortForward'],
'*udp-ports': ['PasstPortForward'],
- '*param': ['PasstParameter'] },
+ '*param': ['PasstParameter'],
+ '*memory-isolation': 'bool'},
'if': 'CONFIG_PASST' }
##
@@ -685,13 +689,17 @@
# @queues: number of queues to be created for multiqueue vhost-user
# (default: 1) (Since 2.5)
#
+# @memory-isolation: isolate guest memory from vhost-user back-end
+# (default: false) (Since 11.2)
+#
# Since: 2.1
##
{ 'struct': 'NetdevVhostUserOptions',
'data': {
- 'chardev': 'str',
- '*vhostforce': 'bool',
- '*queues': 'int' } }
+ 'chardev': 'str',
+ '*vhostforce': 'bool',
+ '*queues': 'int',
+ '*memory-isolation': 'bool' } }
##
# @NetdevVhostVDPAOptions:
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread