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 33 /** 34 * Create a new IOVA tree 35 * 36 * Returns the new IOVA tree 37 */ 38 VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last) 39 { 40 VhostIOVATree *tree = g_new(VhostIOVATree, 1); 41 42 /* Some devices do not like 0 addresses */ 43 tree->iova_first = MAX(iova_first, iova_min_addr); 44 tree->iova_last = iova_last; 45 46 tree->iova_taddr_map = iova_tree_new(); 47 return tree; 48 } 49 50 /** 51 * Delete an iova tree 52 */ 53 void vhost_iova_tree_delete(VhostIOVATree *iova_tree) 54 { 55 iova_tree_destroy(iova_tree->iova_taddr_map); 56 g_free(iova_tree); 57 } 58 59 /** 60 * Find the IOVA address stored from a memory address 61 * 62 * @tree: The iova tree 63 * @map: The map with the memory address 64 * 65 * Return the stored mapping, or NULL if not found. 66 */ 67 const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree, 68 const DMAMap *map) 69 { 70 return iova_tree_find_iova(tree->iova_taddr_map, map); 71 } 72 73 /** 74 * Allocate a new mapping 75 * 76 * @tree: The iova tree 77 * @map: The iova map 78 * 79 * Returns: 80 * - IOVA_OK if the map fits in the container 81 * - IOVA_ERR_INVALID if the map does not make sense (like size overflow) 82 * - IOVA_ERR_NOMEM if tree cannot allocate more space. 83 * 84 * It returns assignated iova in map->iova if return value is VHOST_DMA_MAP_OK. 85 */ 86 int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map) 87 { 88 /* Some vhost devices do not like addr 0. Skip first page */ 89 hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size; 90 91 if (map->translated_addr + map->size < map->translated_addr || 92 map->perm == IOMMU_NONE) { 93 return IOVA_ERR_INVALID; 94 } 95 96 /* Allocate a node in IOVA address */ 97 return iova_tree_alloc_map(tree->iova_taddr_map, map, iova_first, 98 tree->iova_last); 99 } 100 101 /** 102 * Remove existing mappings from iova tree 103 * 104 * @iova_tree: The vhost iova tree 105 * @map: The map to remove 106 */ 107 void vhost_iova_tree_remove(VhostIOVATree *iova_tree, const DMAMap *map) 108 { 109 iova_tree_remove(iova_tree->iova_taddr_map, map); 110 } 111