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