From b88b03c84aa695b96a91329e2d01fffad551c34d Mon Sep 17 00:00:00 2001 From: libai Date: Thu, 27 Mar 2025 19:24:53 +0800 Subject: [PATCH] vdpa/iommufd:Implement DMA mapping through the iommufd interface Change the owner of memorylistener from the independent vDPA device to VDPAIOMMUFDContainer Signed-off-by: libai --- hw/virtio/vdpa-dev-iommufd.c | 137 +++++++++++++++++++++++++++ hw/virtio/vdpa-dev.c | 4 +- hw/virtio/vhost-vdpa.c | 13 +-- include/hw/virtio/vdpa-dev-iommufd.h | 1 + include/hw/virtio/vhost-vdpa.h | 7 ++ 5 files changed, 154 insertions(+), 8 deletions(-) diff --git a/hw/virtio/vdpa-dev-iommufd.c b/hw/virtio/vdpa-dev-iommufd.c index d72f56d52f..668c6a1cb1 100644 --- a/hw/virtio/vdpa-dev-iommufd.c +++ b/hw/virtio/vdpa-dev-iommufd.c @@ -9,11 +9,124 @@ #include #include #include "qapi/error.h" +#include "exec/target_page.h" +#include "exec/address-spaces.h" #include "hw/virtio/vdpa-dev-iommufd.h" static QLIST_HEAD(, VDPAIOMMUFDContainer) vdpa_container_list = QLIST_HEAD_INITIALIZER(vdpa_container_list); +static int vhost_vdpa_iommufd_container_dma_map(VDPAIOMMUFDContainer *container, hwaddr iova, + hwaddr size, void *vaddr, bool readonly) +{ + return iommufd_backend_map_dma(container->iommufd, container->ioas_id, iova, size, vaddr, readonly); + +} +static int vhost_vdpa_iommufd_container_dma_unmap(VDPAIOMMUFDContainer *container, + hwaddr iova, hwaddr size) +{ + return iommufd_backend_unmap_dma(container->iommufd, container->ioas_id, iova, size); +} + +static void vhost_vdpa_iommufd_container_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + VDPAIOMMUFDContainer *container = container_of(listener, VDPAIOMMUFDContainer, listener); + hwaddr iova; + Int128 llend, llsize; + void *vaddr; + int page_size = qemu_target_page_size(); + int page_mask = -page_size; + int ret; + + if (vhost_vdpa_listener_skipped_section(section, 0, ULLONG_MAX, page_mask)) { + return; + } + + if (unlikely((section->offset_within_address_space & ~page_mask) != + (section->offset_within_region & ~page_mask))) { + return; + } + + iova = ROUND_UP(section->offset_within_address_space, page_size); + llend = vhost_vdpa_section_end(section, page_mask); + if (int128_ge(int128_make64(iova), llend)) { + return; + } + + memory_region_ref(section->mr); + vaddr = memory_region_get_ram_ptr(section->mr) + + section->offset_within_region + + (iova - section->offset_within_address_space); + + llsize = int128_sub(llend, int128_make64(iova)); + + ret = vhost_vdpa_iommufd_container_dma_map(container, iova, int128_get64(llsize), + vaddr, section->readonly); + if (ret) { + qemu_log("vhost vdpa iommufd container dma map failed: %d\n", ret); + } +} + +static void vhost_vdpa_iommufd_container_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + VDPAIOMMUFDContainer *container = container_of(listener, VDPAIOMMUFDContainer, listener); + hwaddr iova; + Int128 llend, llsize; + int page_size = qemu_target_page_size(); + int page_mask = -page_size; + int ret; + + if (vhost_vdpa_listener_skipped_section(section, 0, ULLONG_MAX, page_mask)) { + return; + } + + if (unlikely((section->offset_within_address_space & ~page_mask) != + (section->offset_within_region & ~page_mask))) { + return; + } + + iova = ROUND_UP(section->offset_within_address_space, page_size); + llend = vhost_vdpa_section_end(section, page_mask); + + if (int128_ge(int128_make64(iova), llend)) { + return; + } + + llsize = int128_sub(llend, int128_make64(iova)); + /* + * The unmap ioctl doesn't accept a full 64-bit. need to check it + */ + if (int128_eq(llsize, int128_2_64())) { + llsize = int128_rshift(llsize, 1); + ret = vhost_vdpa_iommufd_container_dma_unmap(container, iova, int128_get64(llsize)); + + if (ret) { + qemu_log("vhost vdpa iommufd container unmap failed(0x%" HWADDR_PRIx ", " + "0x%" HWADDR_PRIx ") = %d (%m)", iova, int128_get64(llsize), ret); + } + iova += int128_get64(llsize); + } + ret = vhost_vdpa_iommufd_container_dma_unmap(container, iova, int128_get64(llsize)); + + if (ret) { + qemu_log("vhost vdpa iommufd container unmap failed(0x%" HWADDR_PRIx ", " + "0x%" HWADDR_PRIx ") = %d (%m)", iova, int128_get64(llsize), ret); + } + + memory_region_unref(section->mr); +} + +/* + * IOTLB API used by vhost vdpa iommufd container + */ +const MemoryListener vhost_vdpa_iommufd_container_listener = { + .name = "vhost-vdpa-iommufd-container", + .region_add = vhost_vdpa_iommufd_container_region_add, + .region_del = vhost_vdpa_iommufd_container_region_del, +}; + static int vhost_vdpa_container_connect_iommufd(VDPAIOMMUFDContainer *container) { IOMMUFDBackend *iommufd = container->iommufd; @@ -87,6 +200,7 @@ static VDPAIOMMUFDContainer *vhost_vdpa_create_container(VhostVdpaDevice *vdev) container = g_new0(VDPAIOMMUFDContainer, 1); container->iommufd = vdev->iommufd; + container->listener = vhost_vdpa_iommufd_container_listener; QLIST_INIT(&container->hwpt_list); QLIST_INSERT_HEAD(&vdpa_container_list, container, next); @@ -213,11 +327,27 @@ static void vhost_vdpa_container_detach_device(VDPAIOMMUFDContainer *container, } } +static int vhost_vdpa_container_get_dev_count(VDPAIOMMUFDContainer *container) +{ + IOMMUFDHWPT *hwpt; + VhostVdpaDevice *dev; + int dev_count = 0; + + QLIST_FOREACH(hwpt, &container->hwpt_list, next) { + QLIST_FOREACH(dev, &hwpt->device_list, next) { + dev_count++; + } + } + + return dev_count; +} + int vhost_vdpa_attach_container(VhostVdpaDevice *vdev) { VDPAIOMMUFDContainer *container = NULL; IOMMUFDBackend *iommufd = vdev->iommufd; bool new_container = false; + int dev_count = 0; int ret = 0; if (!iommufd) { @@ -251,6 +381,12 @@ int vhost_vdpa_attach_container(VhostVdpaDevice *vdev) goto unbind; } + /* register the container memory listener when attaching the first device */ + dev_count = vhost_vdpa_container_get_dev_count(container); + if (dev_count == 1) { + memory_listener_register(&container->listener, &address_space_memory); + } + return 0; unbind: @@ -288,6 +424,7 @@ void vhost_vdpa_detach_container(VhostVdpaDevice *vdev) return; } /* No HWPT in this container, destroy it */ + memory_listener_unregister(&container->listener); vhost_vdpa_container_disconnect_iommufd(container); vhost_vdpa_destroy_container(container); diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c index a6bd695724..b256ad540c 100644 --- a/hw/virtio/vdpa-dev.c +++ b/hw/virtio/vdpa-dev.c @@ -136,9 +136,9 @@ static void vhost_vdpa_device_realize(DeviceState *dev, Error **errp) strerror(-ret)); goto free_vqs; } + } else { + memory_listener_register(&v->vdpa.listener, &address_space_memory); } - - memory_listener_register(&v->vdpa.listener, &address_space_memory); v->config_size = vhost_vdpa_device_get_u32(v->vhostfd, VHOST_VDPA_GET_CONFIG_SIZE, errp); diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 4a8fc37851..b5fb89b98e 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -26,13 +26,14 @@ #include "qemu/main-loop.h" #include "trace.h" #include "qapi/error.h" +#include "hw/virtio/vdpa-dev-iommufd.h" /* * Return one past the end of the end of section. Be careful with uint64_t * conversions! */ -static Int128 vhost_vdpa_section_end(const MemoryRegionSection *section, - int page_mask) +Int128 vhost_vdpa_section_end(const MemoryRegionSection *section, + int page_mask) { Int128 llend = int128_make64(section->offset_within_address_space); llend = int128_add(llend, section->size); @@ -41,10 +42,10 @@ static Int128 vhost_vdpa_section_end(const MemoryRegionSection *section, return llend; } -static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section, - uint64_t iova_min, - uint64_t iova_max, - int page_mask) +bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section, + uint64_t iova_min, + uint64_t iova_max, + int page_mask) { Int128 llend; diff --git a/include/hw/virtio/vdpa-dev-iommufd.h b/include/hw/virtio/vdpa-dev-iommufd.h index dc14d9dd15..8e56647690 100644 --- a/include/hw/virtio/vdpa-dev-iommufd.h +++ b/include/hw/virtio/vdpa-dev-iommufd.h @@ -23,6 +23,7 @@ typedef struct IOMMUFDHWPT { } IOMMUFDHWPT; typedef struct VDPAIOMMUFDContainer { + MemoryListener listener; struct IOMMUFDBackend *iommufd; uint32_t ioas_id; QLIST_HEAD(, IOMMUFDHWPT) hwpt_list; diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h index ee255bc1bd..e32effc6e1 100644 --- a/include/hw/virtio/vhost-vdpa.h +++ b/include/hw/virtio/vhost-vdpa.h @@ -57,6 +57,13 @@ typedef struct vhost_vdpa { int vhost_vdpa_get_iova_range(int fd, struct vhost_vdpa_iova_range *iova_range); int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, unsigned idx); +Int128 vhost_vdpa_section_end(const MemoryRegionSection *section, + int page_mask); +bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section, + uint64_t iova_min, + uint64_t iova_max, + int page_mask); + int vhost_vdpa_dma_map(struct vhost_vdpa *v, uint32_t asid, hwaddr iova, hwaddr size, void *vaddr, bool readonly); int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, uint32_t asid, hwaddr iova, -- 2.41.0.windows.1