File: vhost-iova-tree.c

package info (click to toggle)
qemu 1%3A10.0.3%2Bds-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 413,680 kB
  • sloc: ansic: 4,733,433; pascal: 114,769; python: 105,506; asm: 68,431; sh: 52,881; makefile: 27,469; perl: 18,778; cpp: 11,435; xml: 3,404; objc: 2,877; yacc: 2,505; php: 1,299; tcl: 1,296; lex: 1,110; sql: 71; awk: 43; sed: 35; javascript: 7
file content (191 lines) | stat: -rw-r--r-- 5,320 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
 * 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;

    /* Allocated IOVA addresses */
    IOVATree *iova_map;

    /* GPA->IOVA address memory maps */
    IOVATree *gpa_iova_map;
};

/**
 * Create a new VhostIOVATree
 *
 * Returns the new VhostIOVATree.
 */
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();
    tree->iova_map = iova_tree_new();
    tree->gpa_iova_map = gpa_tree_new();
    return tree;
}

/**
 * Delete a VhostIOVATree
 */
void vhost_iova_tree_delete(VhostIOVATree *iova_tree)
{
    iova_tree_destroy(iova_tree->iova_taddr_map);
    iova_tree_destroy(iova_tree->iova_map);
    iova_tree_destroy(iova_tree->gpa_iova_map);
    g_free(iova_tree);
}

/**
 * Find the IOVA address stored from a memory address
 *
 * @tree: The VhostIOVATree
 * @map: The map with the memory address
 *
 * Returns the stored IOVA->HVA 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 IOVA range and add the mapping to the IOVA->HVA tree
 *
 * @tree: The VhostIOVATree
 * @map: The IOVA mapping
 * @taddr: The translated address (HVA)
 *
 * 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 an assigned IOVA in map->iova if the return value is IOVA_OK.
 */
int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map, hwaddr taddr)
{
    int ret;

    /* Some vhost devices do not like addr 0. Skip first page */
    hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size();

    if (taddr + map->size < taddr || map->perm == IOMMU_NONE) {
        return IOVA_ERR_INVALID;
    }

    /* Allocate a node in the IOVA-only tree */
    ret = iova_tree_alloc_map(tree->iova_map, map, iova_first, tree->iova_last);
    if (unlikely(ret != IOVA_OK)) {
        return ret;
    }

    /* Insert a node in the IOVA->HVA tree */
    map->translated_addr = taddr;
    return iova_tree_insert(tree->iova_taddr_map, map);
}

/**
 * Remove existing mappings from the IOVA-only and IOVA->HVA trees
 *
 * @iova_tree: The VhostIOVATree
 * @map: The map to remove
 */
void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map)
{
    iova_tree_remove(iova_tree->iova_taddr_map, map);
    iova_tree_remove(iova_tree->iova_map, map);
}

/**
 * Find the IOVA address stored from a guest memory address (GPA)
 *
 * @tree: The VhostIOVATree
 * @map: The map with the guest memory address
 *
 * Returns the stored GPA->IOVA mapping, or NULL if not found.
 */
const DMAMap *vhost_iova_tree_find_gpa(const VhostIOVATree *tree,
                                       const DMAMap *map)
{
    return iova_tree_find_iova(tree->gpa_iova_map, map);
}

/**
 * Allocate a new IOVA range and add the mapping to the GPA->IOVA tree
 *
 * @tree: The VhostIOVATree
 * @map: The IOVA mapping
 * @taddr: The translated address (GPA)
 *
 * Returns:
 * - IOVA_OK if the map fits both containers
 * - IOVA_ERR_INVALID if the map does not make sense (like size overflow)
 * - IOVA_ERR_NOMEM if the IOVA-only tree cannot allocate more space
 *
 * It returns an assigned IOVA in map->iova if the return value is IOVA_OK.
 */
int vhost_iova_tree_map_alloc_gpa(VhostIOVATree *tree, DMAMap *map, hwaddr taddr)
{
    int ret;

    /* Some vhost devices don't like addr 0. Skip first page */
    hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size();

    if (taddr + map->size < taddr || map->perm == IOMMU_NONE) {
        return IOVA_ERR_INVALID;
    }

    /* Allocate a node in the IOVA-only tree */
    ret = iova_tree_alloc_map(tree->iova_map, map, iova_first, tree->iova_last);
    if (unlikely(ret != IOVA_OK)) {
        return ret;
    }

    /* Insert a node in the GPA->IOVA tree */
    map->translated_addr = taddr;
    return gpa_tree_insert(tree->gpa_iova_map, map);
}

/**
 * Remove existing mappings from the IOVA-only and GPA->IOVA trees
 *
 * @tree: The VhostIOVATree
 * @map: The map to remove
 */
void vhost_iova_tree_remove_gpa(VhostIOVATree *iova_tree, DMAMap map)
{
    iova_tree_remove(iova_tree->gpa_iova_map, map);
    iova_tree_remove(iova_tree->iova_map, map);
}