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