This patch implements the endpoint attach/detach to/from a domain. Signed-off-by: Eric Auger <eric.au...@redhat.com>
--- v11 -> v12: - check the device is protected by the iommu on attach - on detach, check the domain id the device is attached to matches the one used in the detach command - fix mapping ref counter and destroy the domain when no end-points are attached anymore --- hw/virtio/virtio-iommu.c | 65 +++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 6a03c3d7ae..63b2d30f28 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -119,11 +119,12 @@ static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer user_data) static void virtio_iommu_detach_endpoint_from_domain(VirtIOIOMMUEndpoint *ep) { QLIST_REMOVE(ep, next); + g_tree_unref(ep->domain->mappings); ep->domain = NULL; } -VirtIOIOMMUEndpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s, uint32_t ep_id); -VirtIOIOMMUEndpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s, uint32_t ep_id) +static VirtIOIOMMUEndpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s, + uint32_t ep_id) { VirtIOIOMMUEndpoint *ep; @@ -151,8 +152,8 @@ static void virtio_iommu_put_endpoint(gpointer data) g_free(ep); } -VirtIOIOMMUDomain *virtio_iommu_get_domain(VirtIOIOMMU *s, uint32_t domain_id); -VirtIOIOMMUDomain *virtio_iommu_get_domain(VirtIOIOMMU *s, uint32_t domain_id) +static VirtIOIOMMUDomain *virtio_iommu_get_domain(VirtIOIOMMU *s, + uint32_t domain_id) { VirtIOIOMMUDomain *domain; @@ -179,7 +180,6 @@ static void virtio_iommu_put_domain(gpointer data) QLIST_FOREACH_SAFE(iter, &domain->endpoint_list, next, tmp) { virtio_iommu_detach_endpoint_from_domain(iter); } - g_tree_destroy(domain->mappings); trace_virtio_iommu_put_domain(domain->id); g_free(domain); } @@ -228,10 +228,37 @@ static int virtio_iommu_attach(VirtIOIOMMU *s, { uint32_t domain_id = le32_to_cpu(req->domain); uint32_t ep_id = le32_to_cpu(req->endpoint); + VirtIOIOMMUDomain *domain; + VirtIOIOMMUEndpoint *ep; trace_virtio_iommu_attach(domain_id, ep_id); - return VIRTIO_IOMMU_S_UNSUPP; + ep = virtio_iommu_get_endpoint(s, ep_id); + if (!ep) { + return VIRTIO_IOMMU_S_NOENT; + } + + if (ep->domain) { + VirtIOIOMMUDomain *previous_domain = ep->domain; + /* + * the device is already attached to a domain, + * detach it first + */ + virtio_iommu_detach_endpoint_from_domain(ep); + if (QLIST_EMPTY(&previous_domain->endpoint_list)) { + g_tree_remove(s->domains, GUINT_TO_POINTER(previous_domain->id)); + } + } + + domain = virtio_iommu_get_domain(s, domain_id); + if (!QLIST_EMPTY(&domain->endpoint_list)) { + g_tree_ref(domain->mappings); + } + QLIST_INSERT_HEAD(&domain->endpoint_list, ep, next); + + ep->domain = domain; + + return VIRTIO_IOMMU_S_OK; } static int virtio_iommu_detach(VirtIOIOMMU *s, @@ -239,10 +266,34 @@ static int virtio_iommu_detach(VirtIOIOMMU *s, { uint32_t domain_id = le32_to_cpu(req->domain); uint32_t ep_id = le32_to_cpu(req->endpoint); + VirtIOIOMMUDomain *domain; + VirtIOIOMMUEndpoint *ep; trace_virtio_iommu_detach(domain_id, ep_id); - return VIRTIO_IOMMU_S_UNSUPP; + ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(ep_id)); + if (!ep) { + return VIRTIO_IOMMU_S_NOENT; + } + + domain = ep->domain; + + if (!domain || domain->id != domain_id) { + return VIRTIO_IOMMU_S_INVAL; + } + + virtio_iommu_detach_endpoint_from_domain(ep); + + /* + * when the last EP is detached, simply remove the domain for + * the domain list and destroy it. Note its mappings were already + * freed by the ref count mechanism. Next operation involving + * the same domain id will re-create one domain object. + */ + if (QLIST_EMPTY(&domain->endpoint_list)) { + g_tree_remove(s->domains, GUINT_TO_POINTER(domain->id)); + } + return VIRTIO_IOMMU_S_OK; } static int virtio_iommu_map(VirtIOIOMMU *s, -- 2.20.1