Linux block layer
 help / color / mirror / Atom feed
* [PATCH v2 00/12] virtio: support devices that own their virtqueue memory
@ 2026-08-18 21:14 Alexander Graf
  2026-08-18 21:14 ` [PATCH v2 11/12] virtio_pci: support VIRTIO_F_DMB Alexander Graf
  0 siblings, 1 reply; 2+ messages in thread
From: Alexander Graf @ 2026-08-18 21:14 UTC (permalink / raw)
  To: Jason Wang, Michael S. Tsirkin
  Cc: nh-open-source, Alex Williamson, David Airlie, Dmitry Osipenko,
	dri-devel, Eugenio Pérez, Feng Liu, Gerd Hoffmann,
	Halil Pasic, Jens Axboe, Jiri Pirko, linux-block, linux-kernel,
	nvdimm, Pankaj Gupta, Paolo Bonzini, Parav Pandit,
	Stefan Hajnoczi, virtualization, Xuan Zhuo, Yishai Hadas

Virtio drivers use guest memory to back virtqueues and their buffers.
That means a VMM needs to be able to map guest memory. That is ok in the
normal virt case. It gets icky with confidential computing (where we use
swiotlb as workaround) and it defeats the purpose of isolated vhost-user
backing devices, because they end up with full RAM access to the guest.

So instead, I'm proposing an extension to virtio which allows it to give
each virtio device its own dedicated memory region to communicate with the
host, called DMB (Device Memory Buffer). A trusted hypervisor can force
DMB to be present, which then enables safer, more isolated and resilient
communication between guest and host.

With DMB, the device provides a shared memory region that both parties
agree is the full memory map both have access to. All memory offsets
that previously would have been into guest RAM, are then offsets into
this shared memory buffer region. One nice property of this is that it
is a generic mechanism in the virtio transport layer, so higher level
drivers work unmodified.

I was exploring to use swiotlb instead to create individual pools. But
that approach has multiple downsides:

1. Swiotlb is an OS primitive which is not available in all Operating
Systems. DMB however lives in the virtio transport layer, which means we
can add support for it in any OS independent of generic layers. This
helps with Windows support.

2. We munge DMA space together. DMB provides a separate DMA space per
virtio device. This means we can for example implement a device in
vhost-user and give the implementing process only visibility to the DMB
region, not all of guest memory. That reduces the exposure the
vhost-user provider has, improving security.

3. Devices can opt-in. A hypervisor can choose to use standard virtio
semantics for self-implemented devices (e.g. NSM), while requiring DMB
for devices implemented by less trustworthy providers. The
non-trustworthy devices do not get any visibility into the trustworthy
ones, even with DMB in place for both.

I also looked at virtio-iommu. To restrict DMA visibility, virtio-iommu
allows the guest to open specific windows into guest memory to the
device, but it comes with its own bag of problems, such as dynamic
allocations and complicated device <-> iommu connections that need to be
represented reliably.

== Limitations ==

  - Only PCI is wired up.
  - Feature bit 44 and the two registers at offsets 0x40 and 0x42 of the
    PCI common configuration are provisional: the OASIS technical
    committee has the specification and has allocated none of them.

  https://lore.kernel.org/virtio-comment/20260818060255.6853-1-graf@amazon.com/

== Testing ==

I ran this against a device that offers DMB and not
VIRTIO_F_ACCESS_PLATFORM, with a 16 MiB region on each device. A kernel
without patch 10 finds the region and then hangs with nothing in the log;
with it, 512 MiB of O_DIRECT block reads and 137 MiB of loopback network
traffic go through the regions with no allocation failure.

Patches 1 to 4 do not need the rest of the series. They remove an API no
driver calls and a stale worked example in a vdpa comment. Patch 3 and
patch 4 carry Fixes:; patch 4's are older than this series, a failed
mapping arriving from a packed ring as -EIO, which fails an I/O that on a
split ring is only back-pressure.

I wrote this series with an AI coding assistant, which drafted the code,
the changelogs and this cover letter. I reviewed and reworked all of it,
and every commit carries an Assisted-by: trailer.

Alex

v1: https://lore.kernel.org/all/20260809182010.32931-1-graf@amazon.com/

v1 -> v2:

  - Remove the unused map sync API (Michael)
  - Drop the sync operations from virtio_map_ops (Michael)
  - Drop the stale mask expansion instead of correcting it (Michael)
  - Treat VIRTIO_F_DMB as implying VIRTIO_F_ACCESS_PLATFORM
  - Read the region's memory type and accept only a coherent one
  - Rewrite the region allocator over gen_pool
  - Return -ENOMEM instead of -EIO from a failed packed ring mapping
  - Drop the patch validating premapped addresses through the map
  - Drop the patch reporting a bounded pool's exhaustion as -ENOSPC
  - Drop the range withheld for a virtqueue's first descriptor chain
  - Drop the .rst and describe the region in the headers instead

Alexander Graf (12):
  virtio_ring: remove the unused map sync API
  virtio: drop the sync operations from virtio_map_ops
  vdpa: drop the VIRTIO_DEVICE_F_MASK example value
  virtio_ring: return -ENOMEM when a packed ring mapping fails
  virtio: add the VIRTIO_F_DMB feature bit
  virtio_pci: read the device memory buffer registers
  virtio_pci: create virtqueues with the device's mapping token
  virtio: add a device memory buffer region allocator
  virtio: locate the device memory buffer after feature negotiation
  virtio: treat VIRTIO_F_DMB as implying VIRTIO_F_ACCESS_PLATFORM
  virtio_pci: support VIRTIO_F_DMB
  virtio: expose device memory buffer occupancy over debugfs

 drivers/vdpa/vdpa.c                    |   7 +-
 drivers/vdpa/vdpa_user/iova_domain.c   |  20 -
 drivers/vdpa/vdpa_user/iova_domain.h   |   8 -
 drivers/vdpa/vdpa_user/vduse_dev.c     |  40 --
 drivers/virtio/Kconfig                 |  17 +
 drivers/virtio/Makefile                |   3 +-
 drivers/virtio/virtio.c                |  25 +-
 drivers/virtio/virtio_dmb.c            | 959 +++++++++++++++++++++++++
 drivers/virtio/virtio_dmb.h            |  28 +
 drivers/virtio/virtio_pci_modern.c     |  75 +-
 drivers/virtio/virtio_pci_modern_dev.c |  47 +-
 drivers/virtio/virtio_ring.c           |  93 +--
 include/linux/virtio.h                 |  11 +-
 include/linux/virtio_config.h          |  41 +-
 include/linux/virtio_pci_modern.h      |   2 +
 include/uapi/linux/virtio_config.h     |  24 +-
 include/uapi/linux/virtio_pci.h        |  19 +
 tools/virtio/linux/dma-mapping.h       |   7 -
 18 files changed, 1217 insertions(+), 209 deletions(-)
 create mode 100644 drivers/virtio/virtio_dmb.c
 create mode 100644 drivers/virtio/virtio_dmb.h


base-commit: fc02acf6ac0ccde0c805c2daa9148683cdd01ba8

^ permalink raw reply	[flat|nested] 2+ messages in thread

* [PATCH v2 11/12] virtio_pci: support VIRTIO_F_DMB
  2026-08-18 21:14 [PATCH v2 00/12] virtio: support devices that own their virtqueue memory Alexander Graf
@ 2026-08-18 21:14 ` Alexander Graf
  0 siblings, 0 replies; 2+ messages in thread
From: Alexander Graf @ 2026-08-18 21:14 UTC (permalink / raw)
  To: Michael S. Tsirkin, Jason Wang
  Cc: nh-open-source, Xuan Zhuo, Eugenio Pérez, virtualization,
	linux-kernel, Jens Axboe, linux-block, David Airlie,
	Gerd Hoffmann, Dmitry Osipenko, dri-devel, Pankaj Gupta, nvdimm,
	Stefan Hajnoczi, Paolo Bonzini

Let a modern virtio-pci device place its virtqueues and the buffers they
reference in a Device Memory Buffer of its own: accept VIRTIO_F_DMB from
vp_transport_features(), and implement the get_dmb_shm_id config op on
top of vp_modern_get_dmb_shm_id(). get_dmb_shm_id reports the shared
memory id of the region that holds the buffer.

Take the offer only for a dmb_mem_type of VIRTIO_DMB_MEM_TYPE_COHERENT,
the memory type that says a write by either side becomes visible to the
other with no cache maintenance by the driver. The driver uses plain
loads and stores on the region, so no other type will do. The field is
valid from the moment the device offers the feature, so a type the driver
does not support leaves the device driven as an ordinary one. The shmid
is not readable that early, so a region we fail to locate after the
accept fails virtio_features_ok() and probe sets the FAILED status bit.

Refuse a device whose common configuration is too short to hold
dmb_mem_type, which would put that read outside what vp_modern_probe()
mapped. The accept path measures the structure itself, because
vp_check_common_size() runs on the features the driver has already
accepted and so cannot cover a read that precedes it.

Two more things gate the accept. CONFIG_VIRTIO_DMB, so that a device
offering the feature to a kernel built without it is driven as an ordinary
device. And VIRTIO_F_VERSION_1, because virtio_features_ok() returns early
without it, which would leave the feature negotiated and the region never
built; this transport refuses such a device anyway.

Link: https://lore.kernel.org/virtio-comment/20260818060255.6853-1-graf@amazon.com/
Assisted-by: Kiro:claude-opus-5 checkpatch sparse
Signed-off-by: Alexander Graf <graf@amazon.com>
---

  - Explain in a comment how a transport feature survives negotiation
  - Say ACCESS_PLATFORM is implied rather than dismissing it

---
 drivers/virtio/virtio_pci_modern.c | 58 ++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c
index 565d37b630b3..c893e31e5159 100644
--- a/drivers/virtio/virtio_pci_modern.c
+++ b/drivers/virtio/virtio_pci_modern.c
@@ -364,6 +364,29 @@ static void vp_modern_avq_cleanup(struct virtio_device *vdev)
 	}
 }
 
+/*
+ * A device reports the memory type of its Device Memory Buffer region in
+ * dmb_mem_type, and the field is valid as soon as the device offers
+ * VIRTIO_F_DMB.  VIRTIO_DMB_MEM_TYPE_COHERENT says that a write by either side
+ * becomes visible to the other with no cache maintenance by the driver, which
+ * is what plain loads and stores on the region need.  A common configuration
+ * structure too short to hold the field reports no type at all, and reading it
+ * there would fall outside what vp_modern_probe() mapped.  This is the only
+ * place that measures the structure against the field, because the read
+ * happens before vp_check_common_size() runs.
+ */
+static bool vp_dmb_mem_type_supported(struct virtio_pci_device *vp_dev)
+{
+	size_t need = offsetofend(struct virtio_pci_modern_common_cfg,
+				  dmb_mem_type);
+
+	if (vp_dev->mdev.common_len < need)
+		return false;
+
+	return vp_modern_get_dmb_mem_type(&vp_dev->mdev) ==
+	       VIRTIO_DMB_MEM_TYPE_COHERENT;
+}
+
 static void vp_transport_features(struct virtio_device *vdev, u64 features)
 {
 	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
@@ -378,6 +401,30 @@ static void vp_transport_features(struct virtio_device *vdev, u64 features)
 
 	if (features & BIT_ULL(VIRTIO_F_ADMIN_VQ))
 		__virtio_set_bit(vdev, VIRTIO_F_ADMIN_VQ);
+
+	/*
+	 * vring_transport_features() clears every bit in the transport range
+	 * that the ring itself does not consume, and vp_finalize_features()
+	 * hands the features the device offered to this function afterwards.
+	 * A transport feature therefore survives negotiation only if the
+	 * transport sets it again here, the way VIRTIO_F_SR_IOV,
+	 * VIRTIO_F_RING_RESET and VIRTIO_F_ADMIN_VQ do above, so this is where
+	 * the driver accepts a Device Memory Buffer and declining refuses it.
+	 *
+	 * VIRTIO_F_VERSION_1 is required because the core locates and releases
+	 * the region from virtio_features_ok(), which returns before it gets
+	 * that far for a device without VERSION_1, so accepting the feature
+	 * without it would leave the feature negotiated and the region never
+	 * built.  VIRTIO_F_ACCESS_PLATFORM is not required alongside it,
+	 * because virtio_has_dma_quirk() already reads this feature as
+	 * carrying what that one asserts.  Whether the device asks for the
+	 * stronger barriers is its own business and does not bear on this one.
+	 */
+	if (IS_ENABLED(CONFIG_VIRTIO_DMB) &&
+	    (features & BIT_ULL(VIRTIO_F_DMB)) &&
+	    (features & BIT_ULL(VIRTIO_F_VERSION_1)) &&
+	    vp_dmb_mem_type_supported(vp_dev))
+		__virtio_set_bit(vdev, VIRTIO_F_DMB);
 }
 
 static int __vp_check_common_size_one_feature(struct virtio_device *vdev, u32 fbit,
@@ -878,6 +925,15 @@ static bool vp_get_shm_region(struct virtio_device *vdev,
 	return true;
 }
 
+static int vp_get_dmb_shm_id(struct virtio_device *vdev, u16 *id)
+{
+	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+
+	*id = vp_modern_get_dmb_shm_id(&vp_dev->mdev);
+
+	return 0;
+}
+
 /*
  * virtio_pci_admin_has_dev_parts - Checks whether the device parts
  * functionality is supported
@@ -1241,6 +1297,7 @@ static const struct virtio_config_ops virtio_pci_config_nodev_ops = {
 	.set_vq_affinity = vp_set_vq_affinity,
 	.get_vq_affinity = vp_get_vq_affinity,
 	.get_shm_region  = vp_get_shm_region,
+	.get_dmb_shm_id = vp_get_dmb_shm_id,
 	.disable_vq_and_reset = vp_modern_disable_vq_and_reset,
 	.enable_vq_after_reset = vp_modern_enable_vq_after_reset,
 };
@@ -1261,6 +1318,7 @@ static const struct virtio_config_ops virtio_pci_config_ops = {
 	.set_vq_affinity = vp_set_vq_affinity,
 	.get_vq_affinity = vp_get_vq_affinity,
 	.get_shm_region  = vp_get_shm_region,
+	.get_dmb_shm_id = vp_get_dmb_shm_id,
 	.disable_vq_and_reset = vp_modern_disable_vq_and_reset,
 	.enable_vq_after_reset = vp_modern_enable_vq_after_reset,
 };

^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-08-18 21:16 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-18 21:14 [PATCH v2 00/12] virtio: support devices that own their virtqueue memory Alexander Graf
2026-08-18 21:14 ` [PATCH v2 11/12] virtio_pci: support VIRTIO_F_DMB Alexander Graf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox