This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 2e869ca85b730035daf6e49bbbef07c15f99d496 Author: Bowen Wang <[email protected]> AuthorDate: Mon Jun 22 22:56:41 2026 +0800 drivers/virtio/virtio-mmio: leave buffers unmapped except ADDRENV user ones The host (QEMU) can access arbitrary guest memory over MMIO, so buffers need no bouncing: leave map, unmap and free_largest NULL so virtio_map_buf returns the buffer as is (zero copy). With CONFIG_ARCH_ADDRENV a user buffer is only virtually contiguous, while the virtqueue translates its first byte and the device accesses len bytes from that physical address. Add virtio_addrenv_map/unmap/ free_largest, which bounce a user address through the kernel heap and pass a kernel address through, and use them in that configuration. free_largest is a fixed 64 KiB so it never takes the heap lock, which fails in interrupt and panic context. Signed-off-by: Bowen Wang <[email protected]> --- drivers/virtio/virtio-mmio.c | 19 ++++++- drivers/virtio/virtio.c | 116 ++++++++++++++++++++++++++++++++++++++++++ include/nuttx/virtio/virtio.h | 11 ++++ 3 files changed, 144 insertions(+), 2 deletions(-) diff --git a/drivers/virtio/virtio-mmio.c b/drivers/virtio/virtio-mmio.c index 46ea346dae2..cc64581401a 100644 --- a/drivers/virtio/virtio-mmio.c +++ b/drivers/virtio/virtio-mmio.c @@ -262,8 +262,23 @@ static const struct virtio_dispatch g_virtio_mmio_dispatch = static const struct virtio_memory_ops g_virtio_mmio_mmops = { - virtio_mmio_alloc_buf, /* Alloc */ - virtio_mmio_free_buf, /* Free */ + .alloc = virtio_mmio_alloc_buf, + .free = virtio_mmio_free_buf, + + /* The host (QEMU) can access arbitrary guest memory, so buffers are used + * as is (zero copy), except user buffers of a per-process address + * environment, which are not physically contiguous. + */ + +#ifdef CONFIG_ARCH_ADDRENV + .free_largest = virtio_addrenv_free_largest, + .map = virtio_addrenv_map, + .unmap = virtio_addrenv_unmap, +#else + .free_largest = NULL, + .map = NULL, + .unmap = NULL, +#endif }; /**************************************************************************** diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index d7623df5583..bc8682b3b4e 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -26,6 +26,9 @@ #include <nuttx/debug.h> +#include <string.h> + +#include <nuttx/arch.h> #include <nuttx/nuttx.h> #include <nuttx/mutex.h> #include <nuttx/kmalloc.h> @@ -41,6 +44,14 @@ #include "virtio-serial.h" #include "virtio-snd.h" +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Largest buffer virtio_addrenv_map() is asked to bounce at once */ + +#define VIRTIO_ADDRENV_MAP_MAX (64 * 1024) + /**************************************************************************** * Private Types ****************************************************************************/ @@ -356,3 +367,108 @@ int virtio_unregister_device(FAR struct virtio_device *device) nxmutex_unlock(&g_virtio_bus.lock); return ret; } + +#ifdef CONFIG_ARCH_ADDRENV + +/**************************************************************************** + * Name: virtio_addrenv_free_largest + * + * Description: + * Return the largest buffer to pass to virtio_addrenv_map() at once, for + * a transport whose device reaches guest physical memory directly. + * + * Input Parameters: + * vdev - The virtio device + * + * Returned Value: + * The largest mappable length in bytes. + * + ****************************************************************************/ + +size_t virtio_addrenv_free_largest(FAR struct virtio_device *vdev) +{ + /* A fixed limit: walking the heap here would take the heap lock on + * every request, which fails in interrupt and panic context. + */ + + return VIRTIO_ADDRENV_MAP_MAX; +} + +/**************************************************************************** + * Name: virtio_addrenv_map + * + * Description: + * Map a buffer for a transport whose device reaches guest physical memory + * directly. A user address is only virtually contiguous, while the device + * accesses len bytes from the physical address of its first byte, so it + * is bounced through the kernel heap. A kernel address is linearly + * mapped and used as is. + * + * Input Parameters: + * vdev - The virtio device + * buf - The buffer to map + * len - The buffer length in bytes + * dir - The transfer direction + * + * Returned Value: + * The address to give the device, or NULL when out of memory. + * + ****************************************************************************/ + +FAR void *virtio_addrenv_map(FAR struct virtio_device *vdev, FAR void *buf, + size_t len, enum virtio_dma_map dir) +{ + FAR void *bounce; + + if (!up_addrenv_user_vaddr((uintptr_t)buf)) + { + return buf; + } + + bounce = kmm_malloc(len); + if (bounce != NULL && dir != VIRTIO_DMA_FROM_DEVICE) + { + memcpy(bounce, buf, len); + } + + return bounce; +} + +/**************************************************************************** + * Name: virtio_addrenv_unmap + * + * Description: + * Release a buffer mapped by virtio_addrenv_map(), copying the data the + * device wrote back to the user buffer. Must run in the address + * environment the buffer was mapped in. + * + * Input Parameters: + * vdev - The virtio device + * buf - The buffer passed to virtio_addrenv_map() + * len - The buffer length in bytes + * dir - The transfer direction passed to virtio_addrenv_map() + * mapped - The address virtio_addrenv_map() returned + * + * Returned Value: + * None + * + ****************************************************************************/ + +void virtio_addrenv_unmap(FAR struct virtio_device *vdev, FAR void *buf, + size_t len, enum virtio_dma_map dir, + FAR void *mapped) +{ + if (mapped == buf) + { + return; + } + + if (dir != VIRTIO_DMA_TO_DEVICE) + { + memcpy(buf, mapped, len); + } + + kmm_free(mapped); +} + +#endif /* CONFIG_ARCH_ADDRENV */ diff --git a/include/nuttx/virtio/virtio.h b/include/nuttx/virtio/virtio.h index abc768a29bc..0dc68531ba6 100644 --- a/include/nuttx/virtio/virtio.h +++ b/include/nuttx/virtio/virtio.h @@ -229,6 +229,17 @@ int virtio_unregister_device(FAR struct virtio_device *device); void virtio_register_drivers(void); +/* Buffer map ops for MMIO/PCI with per-process address environments */ + +#ifdef CONFIG_ARCH_ADDRENV +size_t virtio_addrenv_free_largest(FAR struct virtio_device *vdev); +FAR void *virtio_addrenv_map(FAR struct virtio_device *vdev, FAR void *buf, + size_t len, enum virtio_dma_map dir); +void virtio_addrenv_unmap(FAR struct virtio_device *vdev, FAR void *buf, + size_t len, enum virtio_dma_map dir, + FAR void *mapped); +#endif + #ifdef __cplusplus } #endif
