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