201 lines
6.4 KiB
Diff
201 lines
6.4 KiB
Diff
|
|
From c938dd769a872c569c3985f06c8b5854231ed74c Mon Sep 17 00:00:00 2001
|
||
|
|
From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= <eperezma@redhat.com>
|
||
|
|
Date: Mon, 14 Mar 2022 18:34:50 +0100
|
||
|
|
Subject: [PATCH] vhost: Add VhostIOVATree
|
||
|
|
MIME-Version: 1.0
|
||
|
|
Content-Type: text/plain; charset=UTF-8
|
||
|
|
Content-Transfer-Encoding: 8bit
|
||
|
|
|
||
|
|
This tree is able to look for a translated address from an IOVA address.
|
||
|
|
|
||
|
|
At first glance it is similar to util/iova-tree. However, SVQ working on
|
||
|
|
devices with limited IOVA space need more capabilities, like allocating
|
||
|
|
IOVA chunks or performing reverse translations (qemu addresses to iova).
|
||
|
|
|
||
|
|
The allocation capability, as "assign a free IOVA address to this chunk
|
||
|
|
of memory in qemu's address space" allows shadow virtqueue to create a
|
||
|
|
new address space that is not restricted by guest's addressable one, so
|
||
|
|
we can allocate shadow vqs vrings outside of it.
|
||
|
|
|
||
|
|
It duplicates the tree so it can search efficiently in both directions,
|
||
|
|
and it will signal overlap if iova or the translated address is present
|
||
|
|
in any tree.
|
||
|
|
|
||
|
|
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
|
||
|
|
Acked-by: Michael S. Tsirkin <mst@redhat.com>
|
||
|
|
Signed-off-by: Jason Wang <jasowang@redhat.com>
|
||
|
|
Signed-off-by: fangyi <eric.fangyi@huawei.com>
|
||
|
|
---
|
||
|
|
hw/virtio/meson.build | 2 +-
|
||
|
|
hw/virtio/vhost-iova-tree.c | 110 ++++++++++++++++++++++++++++++++++++
|
||
|
|
hw/virtio/vhost-iova-tree.h | 27 +++++++++
|
||
|
|
3 files changed, 138 insertions(+), 1 deletion(-)
|
||
|
|
create mode 100644 hw/virtio/vhost-iova-tree.c
|
||
|
|
create mode 100644 hw/virtio/vhost-iova-tree.h
|
||
|
|
|
||
|
|
diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
|
||
|
|
index eb46c05a25..c2e193f56d 100644
|
||
|
|
--- a/hw/virtio/meson.build
|
||
|
|
+++ b/hw/virtio/meson.build
|
||
|
|
@@ -11,7 +11,7 @@ softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-stub.c'))
|
||
|
|
|
||
|
|
virtio_ss = ss.source_set()
|
||
|
|
virtio_ss.add(files('virtio.c'))
|
||
|
|
-virtio_ss.add(when: 'CONFIG_VHOST', if_true: files('vhost.c', 'vhost-backend.c', 'vhost-shadow-virtqueue.c'))
|
||
|
|
+virtio_ss.add(when: 'CONFIG_VHOST', if_true: files('vhost.c', 'vhost-backend.c', 'vhost-shadow-virtqueue.c', 'vhost-iova-tree.c'))
|
||
|
|
virtio_ss.add(when: 'CONFIG_VHOST_USER', if_true: files('vhost-user.c'))
|
||
|
|
virtio_ss.add(when: 'CONFIG_VHOST_VDPA', if_true: files('vhost-vdpa.c'))
|
||
|
|
virtio_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-balloon.c'))
|
||
|
|
diff --git a/hw/virtio/vhost-iova-tree.c b/hw/virtio/vhost-iova-tree.c
|
||
|
|
new file mode 100644
|
||
|
|
index 0000000000..55fed1fefb
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/hw/virtio/vhost-iova-tree.c
|
||
|
|
@@ -0,0 +1,110 @@
|
||
|
|
+/*
|
||
|
|
+ * vhost software live migration iova tree
|
||
|
|
+ *
|
||
|
|
+ * SPDX-FileCopyrightText: Red Hat, Inc. 2021
|
||
|
|
+ * SPDX-FileContributor: Author: Eugenio Pérez <eperezma@redhat.com>
|
||
|
|
+ *
|
||
|
|
+ * SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
|
+ */
|
||
|
|
+
|
||
|
|
+#include "qemu/osdep.h"
|
||
|
|
+#include "qemu/iova-tree.h"
|
||
|
|
+#include "vhost-iova-tree.h"
|
||
|
|
+
|
||
|
|
+#define iova_min_addr qemu_real_host_page_size
|
||
|
|
+
|
||
|
|
+/**
|
||
|
|
+ * VhostIOVATree, able to:
|
||
|
|
+ * - Translate iova address
|
||
|
|
+ * - Reverse translate iova address (from translated to iova)
|
||
|
|
+ * - Allocate IOVA regions for translated range (linear operation)
|
||
|
|
+ */
|
||
|
|
+struct VhostIOVATree {
|
||
|
|
+ /* First addressable iova address in the device */
|
||
|
|
+ uint64_t iova_first;
|
||
|
|
+
|
||
|
|
+ /* Last addressable iova address in the device */
|
||
|
|
+ uint64_t iova_last;
|
||
|
|
+
|
||
|
|
+ /* IOVA address to qemu memory maps. */
|
||
|
|
+ IOVATree *iova_taddr_map;
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+/**
|
||
|
|
+ * Create a new IOVA tree
|
||
|
|
+ *
|
||
|
|
+ * Returns the new IOVA tree
|
||
|
|
+ */
|
||
|
|
+VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last)
|
||
|
|
+{
|
||
|
|
+ VhostIOVATree *tree = g_new(VhostIOVATree, 1);
|
||
|
|
+
|
||
|
|
+ /* Some devices do not like 0 addresses */
|
||
|
|
+ tree->iova_first = MAX(iova_first, iova_min_addr);
|
||
|
|
+ tree->iova_last = iova_last;
|
||
|
|
+
|
||
|
|
+ tree->iova_taddr_map = iova_tree_new();
|
||
|
|
+ return tree;
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+/**
|
||
|
|
+ * Delete an iova tree
|
||
|
|
+ */
|
||
|
|
+void vhost_iova_tree_delete(VhostIOVATree *iova_tree)
|
||
|
|
+{
|
||
|
|
+ iova_tree_destroy(iova_tree->iova_taddr_map);
|
||
|
|
+ g_free(iova_tree);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+/**
|
||
|
|
+ * Find the IOVA address stored from a memory address
|
||
|
|
+ *
|
||
|
|
+ * @tree: The iova tree
|
||
|
|
+ * @map: The map with the memory address
|
||
|
|
+ *
|
||
|
|
+ * Return the stored mapping, or NULL if not found.
|
||
|
|
+ */
|
||
|
|
+const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree,
|
||
|
|
+ const DMAMap *map)
|
||
|
|
+{
|
||
|
|
+ return iova_tree_find_iova(tree->iova_taddr_map, map);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+/**
|
||
|
|
+ * Allocate a new mapping
|
||
|
|
+ *
|
||
|
|
+ * @tree: The iova tree
|
||
|
|
+ * @map: The iova map
|
||
|
|
+ *
|
||
|
|
+ * Returns:
|
||
|
|
+ * - IOVA_OK if the map fits in the container
|
||
|
|
+ * - IOVA_ERR_INVALID if the map does not make sense (like size overflow)
|
||
|
|
+ * - IOVA_ERR_NOMEM if tree cannot allocate more space.
|
||
|
|
+ *
|
||
|
|
+ * It returns assignated iova in map->iova if return value is VHOST_DMA_MAP_OK.
|
||
|
|
+ */
|
||
|
|
+int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map)
|
||
|
|
+{
|
||
|
|
+ /* Some vhost devices do not like addr 0. Skip first page */
|
||
|
|
+ hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size;
|
||
|
|
+
|
||
|
|
+ if (map->translated_addr + map->size < map->translated_addr ||
|
||
|
|
+ map->perm == IOMMU_NONE) {
|
||
|
|
+ return IOVA_ERR_INVALID;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ /* Allocate a node in IOVA address */
|
||
|
|
+ return iova_tree_alloc_map(tree->iova_taddr_map, map, iova_first,
|
||
|
|
+ tree->iova_last);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+/**
|
||
|
|
+ * Remove existing mappings from iova tree
|
||
|
|
+ *
|
||
|
|
+ * @iova_tree: The vhost iova tree
|
||
|
|
+ * @map: The map to remove
|
||
|
|
+ */
|
||
|
|
+void vhost_iova_tree_remove(VhostIOVATree *iova_tree, const DMAMap *map)
|
||
|
|
+{
|
||
|
|
+ iova_tree_remove(iova_tree->iova_taddr_map, map);
|
||
|
|
+}
|
||
|
|
diff --git a/hw/virtio/vhost-iova-tree.h b/hw/virtio/vhost-iova-tree.h
|
||
|
|
new file mode 100644
|
||
|
|
index 0000000000..6a4f24e0f9
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/hw/virtio/vhost-iova-tree.h
|
||
|
|
@@ -0,0 +1,27 @@
|
||
|
|
+/*
|
||
|
|
+ * vhost software live migration iova tree
|
||
|
|
+ *
|
||
|
|
+ * SPDX-FileCopyrightText: Red Hat, Inc. 2021
|
||
|
|
+ * SPDX-FileContributor: Author: Eugenio Pérez <eperezma@redhat.com>
|
||
|
|
+ *
|
||
|
|
+ * SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
|
+ */
|
||
|
|
+
|
||
|
|
+#ifndef HW_VIRTIO_VHOST_IOVA_TREE_H
|
||
|
|
+#define HW_VIRTIO_VHOST_IOVA_TREE_H
|
||
|
|
+
|
||
|
|
+#include "qemu/iova-tree.h"
|
||
|
|
+#include "exec/memory.h"
|
||
|
|
+
|
||
|
|
+typedef struct VhostIOVATree VhostIOVATree;
|
||
|
|
+
|
||
|
|
+VhostIOVATree *vhost_iova_tree_new(uint64_t iova_first, uint64_t iova_last);
|
||
|
|
+void vhost_iova_tree_delete(VhostIOVATree *iova_tree);
|
||
|
|
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostIOVATree, vhost_iova_tree_delete);
|
||
|
|
+
|
||
|
|
+const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *iova_tree,
|
||
|
|
+ const DMAMap *map);
|
||
|
|
+int vhost_iova_tree_map_alloc(VhostIOVATree *iova_tree, DMAMap *map);
|
||
|
|
+void vhost_iova_tree_remove(VhostIOVATree *iova_tree, const DMAMap *map);
|
||
|
|
+
|
||
|
|
+#endif
|
||
|
|
--
|
||
|
|
2.27.0
|
||
|
|
|