xref: /qemu/hw/virtio/vhost-iova-tree.c (revision 92cf61e70838c20adc82daa3170fdbb9d174b508)
1 /*
2  * vhost software live migration iova tree
3  *
4  * SPDX-FileCopyrightText: Red Hat, Inc. 2021
5  * SPDX-FileContributor: Author: Eugenio Pérez <eperezma@redhat.com>
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9 
10 #include "qemu/osdep.h"
11 #include "qemu/iova-tree.h"
12 #include "vhost-iova-tree.h"
13 
14 #define iova_min_addr qemu_real_host_page_size()
15 
16 /**
17  * VhostIOVATree, able to:
18  * - Translate iova address
19  * - Reverse translate iova address (from translated to iova)
20  * - Allocate IOVA regions for translated range (linear operation)
21  */
22 struct VhostIOVATree {
23     /* First addressable iova address in the device */
24     uint64_t iova_first;
25 
26     /* Last addressable iova address in the device */
27     uint64_t iova_last;
28 
29     /* IOVA address to qemu memory maps. */
30     IOVATree *iova_taddr_map;
31 
32     /* Allocated IOVA addresses */
33     IOVATree *iova_map;
34 };
35 
36 /**
37  * Create a new IOVA tree
38  *
39  * Returns the new IOVA tree
40  */
41 VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last)
42 {
43     VhostIOVATree *tree = g_new(VhostIOVATree, 1);
44 
45     /* Some devices do not like 0 addresses */
46     tree->iova_first = MAX(iova_first, iova_min_addr);
47     tree->iova_last = iova_last;
48 
49     tree->iova_taddr_map = iova_tree_new();
50     tree->iova_map = iova_tree_new();
51     return tree;
52 }
53 
54 /**
55  * Delete an iova tree
56  */
57 void vhost_iova_tree_delete(VhostIOVATree *iova_tree)
58 {
59     iova_tree_destroy(iova_tree->iova_taddr_map);
60     iova_tree_destroy(iova_tree->iova_map);
61     g_free(iova_tree);
62 }
63 
64 /**
65  * Find the IOVA address stored from a memory address
66  *
67  * @tree: The iova tree
68  * @map: The map with the memory address
69  *
70  * Return the stored mapping, or NULL if not found.
71  */
72 const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree,
73                                         const DMAMap *map)
74 {
75     return iova_tree_find_iova(tree->iova_taddr_map, map);
76 }
77 
78 /**
79  * Allocate a new mapping
80  *
81  * @tree: The iova tree
82  * @map: The iova map
83  * @taddr: The translated address (HVA)
84  *
85  * Returns:
86  * - IOVA_OK if the map fits in the container
87  * - IOVA_ERR_INVALID if the map does not make sense (like size overflow)
88  * - IOVA_ERR_NOMEM if tree cannot allocate more space.
89  *
90  * It returns assignated iova in map->iova if return value is VHOST_DMA_MAP_OK.
91  */
92 int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map, hwaddr taddr)
93 {
94     int ret;
95 
96     /* Some vhost devices do not like addr 0. Skip first page */
97     hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size();
98 
99     if (taddr + map->size < taddr || map->perm == IOMMU_NONE) {
100         return IOVA_ERR_INVALID;
101     }
102 
103     /* Allocate a node in the IOVA-only tree */
104     ret = iova_tree_alloc_map(tree->iova_map, map, iova_first, tree->iova_last);
105     if (unlikely(ret != IOVA_OK)) {
106         return ret;
107     }
108 
109     /* Insert a node in the IOVA->HVA tree */
110     map->translated_addr = taddr;
111     return iova_tree_insert(tree->iova_taddr_map, map);
112 }
113 
114 /**
115  * Remove existing mappings from iova tree
116  *
117  * @iova_tree: The vhost iova tree
118  * @map: The map to remove
119  */
120 void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map)
121 {
122     iova_tree_remove(iova_tree->iova_taddr_map, map);
123     iova_tree_remove(iova_tree->iova_map, map);
124 }
125