mirror of
https://github.com/qemu/qemu.git
synced 2024-12-15 23:43:31 +08:00
69292a8e40
It's convenient to call iova_tree_remove from a map returned from iova_tree_find or iova_tree_find_iova. With the current code this is not possible, since we will free it, and then we will try to search for it again. Fix it making accepting the map by value, forcing a copy of the argument. Not applying a fixes tag, since there is no use like that at the moment. Signed-off-by: Eugenio Pérez <eperezma@redhat.com> Signed-off-by: Jason Wang <jasowang@redhat.com>
111 lines
2.8 KiB
C
111 lines
2.8 KiB
C
/*
|
|
* 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, DMAMap map)
|
|
{
|
|
iova_tree_remove(iova_tree->iova_taddr_map, map);
|
|
}
|