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