virtio-iommu: Support bypass domain

The driver can create a bypass domain by passing the
VIRTIO_IOMMU_ATTACH_F_BYPASS flag on the ATTACH request. Bypass domains
perform slightly better than domains with identity mappings since they
skip translation.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Message-Id: <20220214124356.872985-4-jean-philippe@linaro.org>
Acked-by: Cornelia Huck <cohuck@redhat.com>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Tested-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
Jean-Philippe Brucker 2022-02-14 12:43:55 +00:00 committed by Michael S. Tsirkin
parent 448179e33e
commit d9c96f2425
1 changed files with 34 additions and 5 deletions

View File

@ -43,6 +43,7 @@
typedef struct VirtIOIOMMUDomain { typedef struct VirtIOIOMMUDomain {
uint32_t id; uint32_t id;
bool bypass;
GTree *mappings; GTree *mappings;
QLIST_HEAD(, VirtIOIOMMUEndpoint) endpoint_list; QLIST_HEAD(, VirtIOIOMMUEndpoint) endpoint_list;
} VirtIOIOMMUDomain; } VirtIOIOMMUDomain;
@ -258,12 +259,16 @@ static void virtio_iommu_put_endpoint(gpointer data)
} }
static VirtIOIOMMUDomain *virtio_iommu_get_domain(VirtIOIOMMU *s, static VirtIOIOMMUDomain *virtio_iommu_get_domain(VirtIOIOMMU *s,
uint32_t domain_id) uint32_t domain_id,
bool bypass)
{ {
VirtIOIOMMUDomain *domain; VirtIOIOMMUDomain *domain;
domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id)); domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id));
if (domain) { if (domain) {
if (domain->bypass != bypass) {
return NULL;
}
return domain; return domain;
} }
domain = g_malloc0(sizeof(*domain)); domain = g_malloc0(sizeof(*domain));
@ -271,6 +276,7 @@ static VirtIOIOMMUDomain *virtio_iommu_get_domain(VirtIOIOMMU *s,
domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp,
NULL, (GDestroyNotify)g_free, NULL, (GDestroyNotify)g_free,
(GDestroyNotify)g_free); (GDestroyNotify)g_free);
domain->bypass = bypass;
g_tree_insert(s->domains, GUINT_TO_POINTER(domain_id), domain); g_tree_insert(s->domains, GUINT_TO_POINTER(domain_id), domain);
QLIST_INIT(&domain->endpoint_list); QLIST_INIT(&domain->endpoint_list);
trace_virtio_iommu_get_domain(domain_id); trace_virtio_iommu_get_domain(domain_id);
@ -334,11 +340,16 @@ static int virtio_iommu_attach(VirtIOIOMMU *s,
{ {
uint32_t domain_id = le32_to_cpu(req->domain); uint32_t domain_id = le32_to_cpu(req->domain);
uint32_t ep_id = le32_to_cpu(req->endpoint); uint32_t ep_id = le32_to_cpu(req->endpoint);
uint32_t flags = le32_to_cpu(req->flags);
VirtIOIOMMUDomain *domain; VirtIOIOMMUDomain *domain;
VirtIOIOMMUEndpoint *ep; VirtIOIOMMUEndpoint *ep;
trace_virtio_iommu_attach(domain_id, ep_id); trace_virtio_iommu_attach(domain_id, ep_id);
if (flags & ~VIRTIO_IOMMU_ATTACH_F_BYPASS) {
return VIRTIO_IOMMU_S_INVAL;
}
ep = virtio_iommu_get_endpoint(s, ep_id); ep = virtio_iommu_get_endpoint(s, ep_id);
if (!ep) { if (!ep) {
return VIRTIO_IOMMU_S_NOENT; return VIRTIO_IOMMU_S_NOENT;
@ -356,7 +367,12 @@ static int virtio_iommu_attach(VirtIOIOMMU *s,
} }
} }
domain = virtio_iommu_get_domain(s, domain_id); domain = virtio_iommu_get_domain(s, domain_id,
flags & VIRTIO_IOMMU_ATTACH_F_BYPASS);
if (!domain) {
/* Incompatible bypass flag */
return VIRTIO_IOMMU_S_INVAL;
}
QLIST_INSERT_HEAD(&domain->endpoint_list, ep, next); QLIST_INSERT_HEAD(&domain->endpoint_list, ep, next);
ep->domain = domain; ep->domain = domain;
@ -419,6 +435,10 @@ static int virtio_iommu_map(VirtIOIOMMU *s,
return VIRTIO_IOMMU_S_NOENT; return VIRTIO_IOMMU_S_NOENT;
} }
if (domain->bypass) {
return VIRTIO_IOMMU_S_INVAL;
}
interval = g_malloc0(sizeof(*interval)); interval = g_malloc0(sizeof(*interval));
interval->low = virt_start; interval->low = virt_start;
@ -464,6 +484,11 @@ static int virtio_iommu_unmap(VirtIOIOMMU *s,
if (!domain) { if (!domain) {
return VIRTIO_IOMMU_S_NOENT; return VIRTIO_IOMMU_S_NOENT;
} }
if (domain->bypass) {
return VIRTIO_IOMMU_S_INVAL;
}
interval.low = virt_start; interval.low = virt_start;
interval.high = virt_end; interval.high = virt_end;
@ -780,6 +805,9 @@ static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr,
entry.perm = flag; entry.perm = flag;
} }
goto unlock; goto unlock;
} else if (ep->domain->bypass) {
entry.perm = flag;
goto unlock;
} }
found = g_tree_lookup_extended(ep->domain->mappings, (gpointer)(&interval), found = g_tree_lookup_extended(ep->domain->mappings, (gpointer)(&interval),
@ -1139,8 +1167,8 @@ static const VMStateDescription vmstate_endpoint = {
static const VMStateDescription vmstate_domain = { static const VMStateDescription vmstate_domain = {
.name = "domain", .name = "domain",
.version_id = 1, .version_id = 2,
.minimum_version_id = 1, .minimum_version_id = 2,
.pre_load = domain_preload, .pre_load = domain_preload,
.fields = (VMStateField[]) { .fields = (VMStateField[]) {
VMSTATE_UINT32(id, VirtIOIOMMUDomain), VMSTATE_UINT32(id, VirtIOIOMMUDomain),
@ -1149,6 +1177,7 @@ static const VMStateDescription vmstate_domain = {
VirtIOIOMMUInterval, VirtIOIOMMUMapping), VirtIOIOMMUInterval, VirtIOIOMMUMapping),
VMSTATE_QLIST_V(endpoint_list, VirtIOIOMMUDomain, 1, VMSTATE_QLIST_V(endpoint_list, VirtIOIOMMUDomain, 1,
vmstate_endpoint, VirtIOIOMMUEndpoint, next), vmstate_endpoint, VirtIOIOMMUEndpoint, next),
VMSTATE_BOOL_V(bypass, VirtIOIOMMUDomain, 2),
VMSTATE_END_OF_LIST() VMSTATE_END_OF_LIST()
} }
}; };
@ -1186,7 +1215,7 @@ static const VMStateDescription vmstate_virtio_iommu_device = {
.version_id = 2, .version_id = 2,
.post_load = iommu_post_load, .post_load = iommu_post_load,
.fields = (VMStateField[]) { .fields = (VMStateField[]) {
VMSTATE_GTREE_DIRECT_KEY_V(domains, VirtIOIOMMU, 1, VMSTATE_GTREE_DIRECT_KEY_V(domains, VirtIOIOMMU, 2,
&vmstate_domain, VirtIOIOMMUDomain), &vmstate_domain, VirtIOIOMMUDomain),
VMSTATE_UINT8_V(config.bypass, VirtIOIOMMU, 2), VMSTATE_UINT8_V(config.bypass, VirtIOIOMMU, 2),
VMSTATE_END_OF_LIST() VMSTATE_END_OF_LIST()