diff options
Diffstat (limited to 'hw/virtio/vhost-vdpa.c')
| -rw-r--r-- | hw/virtio/vhost-vdpa.c | 122 |
1 files changed, 102 insertions, 20 deletions
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 297505f331..ec38189492 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -209,6 +209,21 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, vaddr, section->readonly); llsize = int128_sub(llend, int128_make64(iova)); + if (v->shadow_vqs_enabled) { + DMAMap mem_region = { + .translated_addr = (hwaddr)(uintptr_t)vaddr, + .size = int128_get64(llsize) - 1, + .perm = IOMMU_ACCESS_FLAG(true, section->readonly), + }; + + int r = vhost_iova_tree_map_alloc(v->iova_tree, &mem_region); + if (unlikely(r != IOVA_OK)) { + error_report("Can't allocate a mapping (%d)", r); + goto fail; + } + + iova = mem_region.iova; + } vhost_vdpa_iotlb_batch_begin_once(v); ret = vhost_vdpa_dma_map(v, iova, int128_get64(llsize), @@ -261,6 +276,20 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, llsize = int128_sub(llend, int128_make64(iova)); + if (v->shadow_vqs_enabled) { + const DMAMap *result; + const void *vaddr = memory_region_get_ram_ptr(section->mr) + + section->offset_within_region + + (iova - section->offset_within_address_space); + DMAMap mem_region = { + .translated_addr = (hwaddr)(uintptr_t)vaddr, + .size = int128_get64(llsize) - 1, + }; + + result = vhost_iova_tree_find_iova(v->iova_tree, &mem_region); + iova = result->iova; + vhost_iova_tree_remove(v->iova_tree, &mem_region); + } vhost_vdpa_iotlb_batch_begin_once(v); ret = vhost_vdpa_dma_unmap(v, iova, int128_get64(llsize)); if (ret) { @@ -370,7 +399,7 @@ static int vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v, shadow_vqs = g_ptr_array_new_full(hdev->nvqs, vhost_svq_free); for (unsigned n = 0; n < hdev->nvqs; ++n) { - g_autoptr(VhostShadowVirtqueue) svq = vhost_svq_new(); + g_autoptr(VhostShadowVirtqueue) svq = vhost_svq_new(v->iova_tree); if (unlikely(!svq)) { error_setg(errp, "Cannot create svq %u", n); @@ -807,33 +836,70 @@ static int vhost_vdpa_svq_set_fds(struct vhost_dev *dev, /** * Unmap a SVQ area in the device */ -static bool vhost_vdpa_svq_unmap_ring(struct vhost_vdpa *v, hwaddr iova, - hwaddr size) +static bool vhost_vdpa_svq_unmap_ring(struct vhost_vdpa *v, + const DMAMap *needle) { + const DMAMap *result = vhost_iova_tree_find_iova(v->iova_tree, needle); + hwaddr size; int r; - size = ROUND_UP(size, qemu_real_host_page_size); - r = vhost_vdpa_dma_unmap(v, iova, size); + if (unlikely(!result)) { + error_report("Unable to find SVQ address to unmap"); + return false; + } + + size = ROUND_UP(result->size, qemu_real_host_page_size); + r = vhost_vdpa_dma_unmap(v, result->iova, size); return r == 0; } static bool vhost_vdpa_svq_unmap_rings(struct vhost_dev *dev, const VhostShadowVirtqueue *svq) { + DMAMap needle = {}; struct vhost_vdpa *v = dev->opaque; struct vhost_vring_addr svq_addr; - size_t device_size = vhost_svq_device_area_size(svq); - size_t driver_size = vhost_svq_driver_area_size(svq); bool ok; vhost_svq_get_vring_addr(svq, &svq_addr); - ok = vhost_vdpa_svq_unmap_ring(v, svq_addr.desc_user_addr, driver_size); + needle.translated_addr = svq_addr.desc_user_addr; + ok = vhost_vdpa_svq_unmap_ring(v, &needle); if (unlikely(!ok)) { return false; } - return vhost_vdpa_svq_unmap_ring(v, svq_addr.used_user_addr, device_size); + needle.translated_addr = svq_addr.used_user_addr; + return vhost_vdpa_svq_unmap_ring(v, &needle); +} + +/** + * Map the SVQ area in the device + * + * @v: Vhost-vdpa device + * @needle: The area to search iova + * @errorp: Error pointer + */ +static bool vhost_vdpa_svq_map_ring(struct vhost_vdpa *v, DMAMap *needle, + Error **errp) +{ + int r; + + r = vhost_iova_tree_map_alloc(v->iova_tree, needle); + if (unlikely(r != IOVA_OK)) { + error_setg(errp, "Cannot allocate iova (%d)", r); + return false; + } + + r = vhost_vdpa_dma_map(v, needle->iova, needle->size + 1, + (void *)(uintptr_t)needle->translated_addr, + needle->perm == IOMMU_RO); + if (unlikely(r != 0)) { + error_setg_errno(errp, -r, "Cannot map region to device"); + vhost_iova_tree_remove(v->iova_tree, needle); + } + + return r == 0; } /** @@ -849,28 +915,44 @@ static bool vhost_vdpa_svq_map_rings(struct vhost_dev *dev, struct vhost_vring_addr *addr, Error **errp) { + DMAMap device_region, driver_region; + struct vhost_vring_addr svq_addr; struct vhost_vdpa *v = dev->opaque; size_t device_size = vhost_svq_device_area_size(svq); size_t driver_size = vhost_svq_driver_area_size(svq); - int r; + size_t avail_offset; + bool ok; ERRP_GUARD(); - vhost_svq_get_vring_addr(svq, addr); + vhost_svq_get_vring_addr(svq, &svq_addr); - r = vhost_vdpa_dma_map(v, addr->desc_user_addr, driver_size, - (void *)(uintptr_t)addr->desc_user_addr, true); - if (unlikely(r != 0)) { - error_setg_errno(errp, -r, "Cannot create vq driver region: "); + driver_region = (DMAMap) { + .translated_addr = svq_addr.desc_user_addr, + .size = driver_size - 1, + .perm = IOMMU_RO, + }; + ok = vhost_vdpa_svq_map_ring(v, &driver_region, errp); + if (unlikely(!ok)) { + error_prepend(errp, "Cannot create vq driver region: "); return false; } + addr->desc_user_addr = driver_region.iova; + avail_offset = svq_addr.avail_user_addr - svq_addr.desc_user_addr; + addr->avail_user_addr = driver_region.iova + avail_offset; - r = vhost_vdpa_dma_map(v, addr->used_user_addr, device_size, - (void *)(intptr_t)addr->used_user_addr, false); - if (unlikely(r != 0)) { - error_setg_errno(errp, -r, "Cannot create vq device region: "); + device_region = (DMAMap) { + .translated_addr = svq_addr.used_user_addr, + .size = device_size - 1, + .perm = IOMMU_RW, + }; + ok = vhost_vdpa_svq_map_ring(v, &device_region, errp); + if (unlikely(!ok)) { + error_prepend(errp, "Cannot create vq device region: "); + vhost_vdpa_svq_unmap_ring(v, &driver_region); } + addr->used_user_addr = device_region.iova; - return r == 0; + return ok; } static bool vhost_vdpa_svq_setup(struct vhost_dev *dev, |