From: wafer Xie <[email protected]> allocated and initialized when creating the vhost-vdpa device, and release the indirect buffer when vhost-vdpa is stopped.
Signed-off-by: wafer Xie <[email protected]> --- hw/virtio/vhost-shadow-virtqueue.c | 25 +++++ hw/virtio/vhost-vdpa.c | 163 ++++++++++++++++++++++++++++- 2 files changed, 187 insertions(+), 1 deletion(-) diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c index 2481d49345..85eff1d841 100644 --- a/hw/virtio/vhost-shadow-virtqueue.c +++ b/hw/virtio/vhost-shadow-virtqueue.c @@ -708,6 +708,25 @@ void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev, for (unsigned i = 0; i < svq->vring.num - 1; i++) { svq->desc_next[i] = i + 1; } + + /* Initialize indirect descriptor state */ + svq->indirect_enabled = false; + svq->current_indirect_buf = -1; + for (int i = 0; i < SVQ_NUM_INDIRECT_BUFS; i++) { + svq->indirect_bufs[i].desc = NULL; + svq->indirect_bufs[i].iova = 0; + svq->indirect_bufs[i].size = 0; + svq->indirect_bufs[i].state = SVQ_INDIRECT_BUF_FREED; + svq->indirect_bufs[i].num_descs = 0; + svq->indirect_bufs[i].freed_descs = 0; + svq->indirect_bufs[i].freeing_descs = 0; + svq->indirect_bufs[i].freed_head = 0; + } + + /* Initialize desc_state indirect_buf_idx to -1 */ + for (unsigned i = 0; i < svq->vring.num; i++) { + svq->desc_state[i].indirect_buf_idx = -1; + } } /** @@ -748,6 +767,10 @@ void vhost_svq_stop(VhostShadowVirtqueue *svq) munmap(svq->vring.desc, vhost_svq_driver_area_size(svq)); munmap(svq->vring.used, vhost_svq_device_area_size(svq)); event_notifier_set_handler(&svq->hdev_call, NULL); + + /* Reset indirect descriptor state (memory is freed by vhost-vdpa) */ + svq->indirect_enabled = false; + svq->current_indirect_buf = -1; } /** @@ -765,6 +788,8 @@ 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->indirect_enabled = false; + svq->current_indirect_buf = -1; return svq; } diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 7061b6e1a3..816868d153 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -584,6 +584,155 @@ static int vhost_vdpa_get_dev_features(struct vhost_dev *dev, return ret; } +/** + * Allocate a single indirect descriptor buffer for SVQ + * + * @v: vhost_vdpa instance + * @num_descs: Number of descriptors (should be vring.num) + * @buf: Output buffer structure to fill + * + * Returns true on success, false on failure. + */ +static bool vhost_vdpa_alloc_indirect_buf(struct vhost_vdpa *v, + uint16_t num_descs, + SVQIndirectDescBuf *buf) +{ + size_t desc_size = sizeof(vring_desc_t) * num_descs; + size_t alloc_size = ROUND_UP(desc_size, qemu_real_host_page_size()); + DMAMap needle = { + .size = alloc_size - 1, + .perm = IOMMU_RO, + }; + vring_desc_t *indirect_desc; + int r; + + /* Allocate memory for indirect descriptors */ + indirect_desc = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (indirect_desc == MAP_FAILED) { + error_report("Cannot allocate indirect descriptor buffer"); + return false; + } + + /* Allocate IOVA for the indirect descriptor buffer */ + r = vhost_iova_tree_map_alloc(v->shared->iova_tree, &needle, + (hwaddr)(uintptr_t)indirect_desc); + if (unlikely(r != IOVA_OK)) { + error_report("Cannot allocate iova for indirect descriptors (%d)", r); + munmap(indirect_desc, alloc_size); + return false; + } + + /* Map to device */ + r = vhost_vdpa_dma_map(v->shared, v->address_space_id, needle.iova, + alloc_size, indirect_desc, true); + if (unlikely(r != 0)) { + error_report("Cannot map indirect descriptors to device: %s (%d)", + g_strerror(-r), -r); + vhost_iova_tree_remove(v->shared->iova_tree, needle); + munmap(indirect_desc, alloc_size); + return false; + } + + buf->desc = indirect_desc; + buf->iova = needle.iova; + buf->size = alloc_size; + buf->state = SVQ_INDIRECT_BUF_FREED; + buf->num_descs = num_descs; + buf->freed_descs = num_descs; /* All descriptors are free initially */ + buf->freeing_descs = 0; + buf->freed_head = 0; + + return true; +} + +/** + * Free a single indirect descriptor buffer + * + * @v: vhost_vdpa instance + * @buf: Buffer to free + */ +static void vhost_vdpa_free_indirect_buf(struct vhost_vdpa *v, + SVQIndirectDescBuf *buf) +{ + DMAMap needle; + const DMAMap *result; + int r; + + if (!buf->desc) { + return; + } + + needle = (DMAMap) { + .translated_addr = (uint64_t)(uintptr_t)buf->desc, + }; + result = vhost_iova_tree_find_iova(v->shared->iova_tree, &needle); + + if (result) { + r = vhost_vdpa_dma_unmap(v->shared, v->address_space_id, + result->iova, buf->size); + if (unlikely(r != 0)) { + error_report("Cannot unmap indirect descriptors: %s (%d)", + g_strerror(-r), -r); + } + + vhost_iova_tree_remove(v->shared->iova_tree, *result); + } + + munmap(buf->desc, buf->size); + buf->desc = NULL; + buf->iova = 0; + buf->size = 0; +} + +/** + * Initialize indirect descriptor buffers for a single SVQ + * + * @v: vhost_vdpa instance + * @svq: Shadow virtqueue to initialize + * + * Returns true on success, false on failure. + */ +static bool vhost_vdpa_svq_init_indirect(struct vhost_vdpa *v, + VhostShadowVirtqueue *svq) +{ + if (!svq->vring.num) { + return true; + } + + /* Allocate indirect descriptor buffers for this SVQ */ + for (int j = 0; j < SVQ_NUM_INDIRECT_BUFS; j++) { + if (!vhost_vdpa_alloc_indirect_buf(v, svq->vring.num * 4, + &svq->indirect_bufs[j])) { + /* Cleanup already allocated buffers */ + for (int k = 0; k < j; k++) { + vhost_vdpa_free_indirect_buf(v, &svq->indirect_bufs[k]); + } + return false; + } + } + + svq->indirect_enabled = true; + svq->current_indirect_buf = 0; + return true; +} + +/** + * Cleanup indirect descriptor buffers for a single SVQ + * + * @v: vhost_vdpa instance + * @svq: Shadow virtqueue to cleanup + */ +static void vhost_vdpa_svq_cleanup_indirect(struct vhost_vdpa *v, + VhostShadowVirtqueue *svq) +{ + for (int j = 0; j < SVQ_NUM_INDIRECT_BUFS; j++) { + vhost_vdpa_free_indirect_buf(v, &svq->indirect_bufs[j]); + } + svq->indirect_enabled = false; + svq->current_indirect_buf = -1; +} + static void vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v) { g_autoptr(GPtrArray) shadow_vqs = NULL; @@ -791,8 +940,11 @@ static void vhost_vdpa_svq_cleanup(struct vhost_dev *dev) size_t idx; for (idx = 0; idx < v->shadow_vqs->len; ++idx) { - vhost_svq_stop(g_ptr_array_index(v->shadow_vqs, idx)); + VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, idx); + vhost_vdpa_svq_cleanup_indirect(v, svq); + vhost_svq_stop(svq); } + g_ptr_array_free(v->shadow_vqs, true); } @@ -1299,6 +1451,13 @@ static bool vhost_vdpa_svqs_start(struct vhost_dev *dev) error_setg_errno(&err, -r, "Cannot set device address"); goto err_set_addr; } + + /* Initialize indirect descriptor buffers for this SVQ */ + if (!vhost_vdpa_svq_init_indirect(v, svq)) { + /* Non-fatal: will fallback to chain mode */ + warn_report("Cannot initialize indirect descriptor for SVQ %u", + virtio_get_queue_index(vq)); + } } return true; @@ -1313,6 +1472,7 @@ err: error_reportf_err(err, "Cannot setup SVQ %u: ", i); for (unsigned j = 0; j < i; ++j) { VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, j); + vhost_vdpa_svq_cleanup_indirect(v, svq); vhost_vdpa_svq_unmap_rings(dev, svq); vhost_svq_stop(svq); } @@ -1331,6 +1491,7 @@ static void vhost_vdpa_svqs_stop(struct vhost_dev *dev) for (unsigned i = 0; i < v->shadow_vqs->len; ++i) { VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, i); + vhost_vdpa_svq_cleanup_indirect(v, svq); vhost_svq_stop(svq); vhost_vdpa_svq_unmap_rings(dev, svq); -- 2.48.1
