blob: 67bf6d57ab9c04ec3a0f787df6e364b1faf14f3e [file] [log] [blame]
/*
* vhost software live migration iova tree
*
* SPDX-FileCopyrightText: Red Hat, Inc. 2021
* SPDX-FileContributor: Author: Eugenio PĂ©rez <eperezma@redhat.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "qemu/iova-tree.h"
#include "vhost-iova-tree.h"
#define iova_min_addr qemu_real_host_page_size()
/**
* VhostIOVATree, able to:
* - Translate iova address
* - Reverse translate iova address (from translated to iova)
* - Allocate IOVA regions for translated range (linear operation)
*/
struct VhostIOVATree {
/* First addressable iova address in the device */
uint64_t iova_first;
/* Last addressable iova address in the device */
uint64_t iova_last;
/* IOVA address to qemu memory maps. */
IOVATree *iova_taddr_map;
};
/**
* Create a new IOVA tree
*
* Returns the new IOVA tree
*/
VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last)
{
VhostIOVATree *tree = g_new(VhostIOVATree, 1);
/* Some devices do not like 0 addresses */
tree->iova_first = MAX(iova_first, iova_min_addr);
tree->iova_last = iova_last;
tree->iova_taddr_map = iova_tree_new();
return tree;
}
/**
* Delete an iova tree
*/
void vhost_iova_tree_delete(VhostIOVATree *iova_tree)
{
iova_tree_destroy(iova_tree->iova_taddr_map);
g_free(iova_tree);
}
/**
* Find the IOVA address stored from a memory address
*
* @tree: The iova tree
* @map: The map with the memory address
*
* Return the stored mapping, or NULL if not found.
*/
const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree,
const DMAMap *map)
{
return iova_tree_find_iova(tree->iova_taddr_map, map);
}
/**
* Allocate a new mapping
*
* @tree: The iova tree
* @map: The iova map
*
* Returns:
* - IOVA_OK if the map fits in the container
* - IOVA_ERR_INVALID if the map does not make sense (like size overflow)
* - IOVA_ERR_NOMEM if tree cannot allocate more space.
*
* It returns assignated iova in map->iova if return value is VHOST_DMA_MAP_OK.
*/
int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map)
{
/* Some vhost devices do not like addr 0. Skip first page */
hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size();
if (map->translated_addr + map->size < map->translated_addr ||
map->perm == IOMMU_NONE) {
return IOVA_ERR_INVALID;
}
/* Allocate a node in IOVA address */
return iova_tree_alloc_map(tree->iova_taddr_map, map, iova_first,
tree->iova_last);
}
/**
* Remove existing mappings from iova tree
*
* @iova_tree: The vhost iova tree
* @map: The map to remove
*/
void vhost_iova_tree_remove(VhostIOVATree *iova_tree, const DMAMap *map)
{
iova_tree_remove(iova_tree->iova_taddr_map, map);
}