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