This patch introduce domain and endpoint internal
datatypes. Both are stored in RB trees. The domain
owns a list of endpoints attached to it.
Helpers to get/put end points and domains are introduced.
get() helpers will become static in subsequent patches.
Signed-off-by: Eric Auger <eric.auger@redhat.com>
---
v6 -> v7:
- on virtio_iommu_find_add_as the bus number computation may
not be finalized yet so we cannot register the EPs at that time.
Hence, let's remove the get_endpoint and also do not use the
bus number for building the memory region name string (only
used for debug though).
v4 -> v5:
- initialize as->endpoint_list
v3 -> v4:
- new separate patch
---
hw/virtio/trace-events | 4 ++
hw/virtio/virtio-iommu.c | 125 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 128 insertions(+), 1 deletion(-)
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index 9270b0463e..4b15086872 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -61,3 +61,7 @@ virtio_iommu_map(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end, uin
virtio_iommu_unmap(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64
virtio_iommu_translate(const char *name, uint32_t rid, uint64_t iova, int flag) "mr=%s rid=%d addr=0x%"PRIx64" flag=%d"
virtio_iommu_init_iommu_mr(char *iommu_mr) "init %s"
+virtio_iommu_get_endpoint(uint32_t ep_id) "Alloc endpoint=%d"
+virtio_iommu_put_endpoint(uint32_t ep_id) "Free endpoint=%d"
+virtio_iommu_get_domain(uint32_t domain_id) "Alloc domain=%d"
+virtio_iommu_put_domain(uint32_t domain_id) "Free domain=%d"
diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c
index dead062baf..1b9c3ba416 100644
--- a/hw/virtio/virtio-iommu.c
+++ b/hw/virtio/virtio-iommu.c
@@ -33,20 +33,124 @@
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-access.h"
#include "hw/virtio/virtio-iommu.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/pci/pci.h"
/* Max size */
#define VIOMMU_DEFAULT_QUEUE_SIZE 256
+typedef struct viommu_domain {
+ uint32_t id;
+ GTree *mappings;
+ QLIST_HEAD(, viommu_endpoint) endpoint_list;
+} viommu_domain;
+
+typedef struct viommu_endpoint {
+ uint32_t id;
+ viommu_domain *domain;
+ QLIST_ENTRY(viommu_endpoint) next;
+ VirtIOIOMMU *viommu;
+} viommu_endpoint;
+
+typedef struct viommu_interval {
+ uint64_t low;
+ uint64_t high;
+} viommu_interval;
+
static inline uint16_t virtio_iommu_get_sid(IOMMUDevice *dev)
{
return PCI_BUILD_BDF(pci_bus_num(dev->bus), dev->devfn);
}
+static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ viommu_interval *inta = (viommu_interval *)a;
+ viommu_interval *intb = (viommu_interval *)b;
+
+ if (inta->high <= intb->low) {
+ return -1;
+ } else if (intb->high <= inta->low) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void virtio_iommu_detach_endpoint_from_domain(viommu_endpoint *ep)
+{
+ QLIST_REMOVE(ep, next);
+ ep->domain = NULL;
+}
+
+viommu_endpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s, uint32_t ep_id);
+viommu_endpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s, uint32_t ep_id)
+{
+ viommu_endpoint *ep;
+
+ ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(ep_id));
+ if (ep) {
+ return ep;
+ }
+ ep = g_malloc0(sizeof(*ep));
+ ep->id = ep_id;
+ ep->viommu = s;
+ trace_virtio_iommu_get_endpoint(ep_id);
+ g_tree_insert(s->endpoints, GUINT_TO_POINTER(ep_id), ep);
+ return ep;
+}
+
+static void virtio_iommu_put_endpoint(gpointer data)
+{
+ viommu_endpoint *ep = (viommu_endpoint *)data;
+
+ if (ep->domain) {
+ virtio_iommu_detach_endpoint_from_domain(ep);
+ g_tree_unref(ep->domain->mappings);
+ }
+
+ trace_virtio_iommu_put_endpoint(ep->id);
+ g_free(ep);
+}
+
+viommu_domain *virtio_iommu_get_domain(VirtIOIOMMU *s, uint32_t domain_id);
+viommu_domain *virtio_iommu_get_domain(VirtIOIOMMU *s, uint32_t domain_id)
+{
+ viommu_domain *domain;
+
+ domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id));
+ if (domain) {
+ return domain;
+ }
+ domain = g_malloc0(sizeof(*domain));
+ domain->id = domain_id;
+ domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp,
+ NULL, (GDestroyNotify)g_free,
+ (GDestroyNotify)g_free);
+ g_tree_insert(s->domains, GUINT_TO_POINTER(domain_id), domain);
+ QLIST_INIT(&domain->endpoint_list);
+ trace_virtio_iommu_get_domain(domain_id);
+ return domain;
+}
+
+static void virtio_iommu_put_domain(gpointer data)
+{
+ viommu_domain *domain = (viommu_domain *)data;
+ viommu_endpoint *iter, *tmp;
+
+ 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);
+}
+
static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
int devfn)
{
VirtIOIOMMU *s = opaque;
IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus);
+ static uint32_t mr_index;
IOMMUDevice *sdev;
if (!sbus) {
@@ -60,7 +164,7 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
if (!sdev) {
char *name = g_strdup_printf("%s-%d-%d",
TYPE_VIRTIO_IOMMU_MEMORY_REGION,
- pci_bus_num(bus), devfn);
+ mr_index++, devfn);
sdev = sbus->pbdev[devfn] = g_malloc0(sizeof(IOMMUDevice));
sdev->viommu = s;
@@ -75,6 +179,7 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
UINT64_MAX);
address_space_init(&sdev->as,
MEMORY_REGION(&sdev->iommu_mr), TYPE_VIRTIO_IOMMU);
+ g_free(name);
}
return &sdev->as;
@@ -332,6 +437,13 @@ static const VMStateDescription vmstate_virtio_iommu_device = {
},
};
+static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ uint ua = GPOINTER_TO_UINT(a);
+ uint ub = GPOINTER_TO_UINT(b);
+ return (ua > ub) - (ua < ub);
+}
+
static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
@@ -356,6 +468,8 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MAP_UNMAP);
virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS);
+ qemu_mutex_init(&s->mutex);
+
memset(s->as_by_bus_num, 0, sizeof(s->as_by_bus_num));
s->as_by_busptr = g_hash_table_new(NULL, NULL);
@@ -364,11 +478,20 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
} else {
error_setg(errp, "VIRTIO-IOMMU is not attached to any PCI bus!");
}
+
+ s->domains = g_tree_new_full((GCompareDataFunc)int_cmp,
+ NULL, NULL, virtio_iommu_put_domain);
+ s->endpoints = g_tree_new_full((GCompareDataFunc)int_cmp,
+ NULL, NULL, virtio_iommu_put_endpoint);
}
static void virtio_iommu_device_unrealize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOIOMMU *s = VIRTIO_IOMMU(dev);
+
+ g_tree_destroy(s->domains);
+ g_tree_destroy(s->endpoints);
virtio_cleanup(vdev);
}
--
2.17.2
Hi Eric,
> -----Original Message-----
> From: Eric Auger <eric.auger@redhat.com>
> Sent: Thursday, November 22, 2018 10:45 PM
> To: eric.auger.pro@gmail.com; eric.auger@redhat.com; qemu-
> devel@nongnu.org; qemu-arm@nongnu.org; peter.maydell@linaro.org;
> mst@redhat.com; jean-philippe.brucker@arm.com
> Cc: kevin.tian@intel.com; tn@semihalf.com; Bharat Bhushan
> <bharat.bhushan@nxp.com>; peterx@redhat.com
> Subject: [RFC v9 06/17] virtio-iommu: Endpoint and domains structs and
> helpers
>
> This patch introduce domain and endpoint internal datatypes. Both are
> stored in RB trees. The domain owns a list of endpoints attached to it.
>
> Helpers to get/put end points and domains are introduced.
> get() helpers will become static in subsequent patches.
>
> Signed-off-by: Eric Auger <eric.auger@redhat.com>
>
> ---
>
> v6 -> v7:
> - on virtio_iommu_find_add_as the bus number computation may
> not be finalized yet so we cannot register the EPs at that time.
> Hence, let's remove the get_endpoint and also do not use the
> bus number for building the memory region name string (only
> used for debug though).
Endpoint registration from virtio_iommu_find_add_as to PROBE request.
It is mentioned that " the bus number computation may not be finalized ". Can you please give some more information.
I am asking this because from vfio perspective translate/replay will be called much before the PROBE request and endpoint needed to be registered by that time.
Thanks
-Bharat
>
> v4 -> v5:
> - initialize as->endpoint_list
>
> v3 -> v4:
> - new separate patch
> ---
> hw/virtio/trace-events | 4 ++
> hw/virtio/virtio-iommu.c | 125
> ++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 128 insertions(+), 1 deletion(-)
>
> diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index
> 9270b0463e..4b15086872 100644
> --- a/hw/virtio/trace-events
> +++ b/hw/virtio/trace-events
> @@ -61,3 +61,7 @@ virtio_iommu_map(uint32_t domain_id, uint64_t
> virt_start, uint64_t virt_end, uin virtio_iommu_unmap(uint32_t domain_id,
> uint64_t virt_start, uint64_t virt_end) "domain=%d virt_start=0x%"PRIx64"
> virt_end=0x%"PRIx64 virtio_iommu_translate(const char *name, uint32_t
> rid, uint64_t iova, int flag) "mr=%s rid=%d addr=0x%"PRIx64" flag=%d"
> virtio_iommu_init_iommu_mr(char *iommu_mr) "init %s"
> +virtio_iommu_get_endpoint(uint32_t ep_id) "Alloc endpoint=%d"
> +virtio_iommu_put_endpoint(uint32_t ep_id) "Free endpoint=%d"
> +virtio_iommu_get_domain(uint32_t domain_id) "Alloc domain=%d"
> +virtio_iommu_put_domain(uint32_t domain_id) "Free domain=%d"
> diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index
> dead062baf..1b9c3ba416 100644
> --- a/hw/virtio/virtio-iommu.c
> +++ b/hw/virtio/virtio-iommu.c
> @@ -33,20 +33,124 @@
> #include "hw/virtio/virtio-bus.h"
> #include "hw/virtio/virtio-access.h"
> #include "hw/virtio/virtio-iommu.h"
> +#include "hw/pci/pci_bus.h"
> +#include "hw/pci/pci.h"
>
> /* Max size */
> #define VIOMMU_DEFAULT_QUEUE_SIZE 256
>
> +typedef struct viommu_domain {
> + uint32_t id;
> + GTree *mappings;
> + QLIST_HEAD(, viommu_endpoint) endpoint_list; } viommu_domain;
> +
> +typedef struct viommu_endpoint {
> + uint32_t id;
> + viommu_domain *domain;
> + QLIST_ENTRY(viommu_endpoint) next;
> + VirtIOIOMMU *viommu;
> +} viommu_endpoint;
> +
> +typedef struct viommu_interval {
> + uint64_t low;
> + uint64_t high;
> +} viommu_interval;
> +
> static inline uint16_t virtio_iommu_get_sid(IOMMUDevice *dev) {
> return PCI_BUILD_BDF(pci_bus_num(dev->bus), dev->devfn); }
>
> +static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer
> +user_data) {
> + viommu_interval *inta = (viommu_interval *)a;
> + viommu_interval *intb = (viommu_interval *)b;
> +
> + if (inta->high <= intb->low) {
> + return -1;
> + } else if (intb->high <= inta->low) {
> + return 1;
> + } else {
> + return 0;
> + }
> +}
> +
> +static void
> virtio_iommu_detach_endpoint_from_domain(viommu_endpoint
> +*ep) {
> + QLIST_REMOVE(ep, next);
> + ep->domain = NULL;
> +}
> +
> +viommu_endpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s,
> uint32_t
> +ep_id); viommu_endpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s,
> +uint32_t ep_id) {
> + viommu_endpoint *ep;
> +
> + ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(ep_id));
> + if (ep) {
> + return ep;
> + }
> + ep = g_malloc0(sizeof(*ep));
> + ep->id = ep_id;
> + ep->viommu = s;
> + trace_virtio_iommu_get_endpoint(ep_id);
> + g_tree_insert(s->endpoints, GUINT_TO_POINTER(ep_id), ep);
> + return ep;
> +}
> +
> +static void virtio_iommu_put_endpoint(gpointer data) {
> + viommu_endpoint *ep = (viommu_endpoint *)data;
> +
> + if (ep->domain) {
> + virtio_iommu_detach_endpoint_from_domain(ep);
> + g_tree_unref(ep->domain->mappings);
> + }
> +
> + trace_virtio_iommu_put_endpoint(ep->id);
> + g_free(ep);
> +}
> +
> +viommu_domain *virtio_iommu_get_domain(VirtIOIOMMU *s, uint32_t
> +domain_id); viommu_domain *virtio_iommu_get_domain(VirtIOIOMMU
> *s,
> +uint32_t domain_id) {
> + viommu_domain *domain;
> +
> + domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id));
> + if (domain) {
> + return domain;
> + }
> + domain = g_malloc0(sizeof(*domain));
> + domain->id = domain_id;
> + domain->mappings =
> g_tree_new_full((GCompareDataFunc)interval_cmp,
> + NULL, (GDestroyNotify)g_free,
> + (GDestroyNotify)g_free);
> + g_tree_insert(s->domains, GUINT_TO_POINTER(domain_id), domain);
> + QLIST_INIT(&domain->endpoint_list);
> + trace_virtio_iommu_get_domain(domain_id);
> + return domain;
> +}
> +
> +static void virtio_iommu_put_domain(gpointer data) {
> + viommu_domain *domain = (viommu_domain *)data;
> + viommu_endpoint *iter, *tmp;
> +
> + 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);
> +}
> +
> static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void
> *opaque,
> int devfn) {
> VirtIOIOMMU *s = opaque;
> IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus);
> + static uint32_t mr_index;
> IOMMUDevice *sdev;
>
> if (!sbus) {
> @@ -60,7 +164,7 @@ static AddressSpace
> *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
> if (!sdev) {
> char *name = g_strdup_printf("%s-%d-%d",
> TYPE_VIRTIO_IOMMU_MEMORY_REGION,
> - pci_bus_num(bus), devfn);
> + mr_index++, devfn);
> sdev = sbus->pbdev[devfn] = g_malloc0(sizeof(IOMMUDevice));
>
> sdev->viommu = s;
> @@ -75,6 +179,7 @@ static AddressSpace
> *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
> UINT64_MAX);
> address_space_init(&sdev->as,
> MEMORY_REGION(&sdev->iommu_mr),
> TYPE_VIRTIO_IOMMU);
> + g_free(name);
> }
>
> return &sdev->as;
> @@ -332,6 +437,13 @@ static const VMStateDescription
> vmstate_virtio_iommu_device = {
> },
> };
>
> +static gint int_cmp(gconstpointer a, gconstpointer b, gpointer
> +user_data) {
> + uint ua = GPOINTER_TO_UINT(a);
> + uint ub = GPOINTER_TO_UINT(b);
> + return (ua > ub) - (ua < ub);
> +}
> +
> static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) {
> VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -356,6 +468,8 @@ static
> void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
> virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MAP_UNMAP);
> virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS);
>
> + qemu_mutex_init(&s->mutex);
> +
> memset(s->as_by_bus_num, 0, sizeof(s->as_by_bus_num));
> s->as_by_busptr = g_hash_table_new(NULL, NULL);
>
> @@ -364,11 +478,20 @@ static void
> virtio_iommu_device_realize(DeviceState *dev, Error **errp)
> } else {
> error_setg(errp, "VIRTIO-IOMMU is not attached to any PCI bus!");
> }
> +
> + s->domains = g_tree_new_full((GCompareDataFunc)int_cmp,
> + NULL, NULL, virtio_iommu_put_domain);
> + s->endpoints = g_tree_new_full((GCompareDataFunc)int_cmp,
> + NULL, NULL,
> + virtio_iommu_put_endpoint);
> }
>
> static void virtio_iommu_device_unrealize(DeviceState *dev, Error **errp)
> {
> VirtIODevice *vdev = VIRTIO_DEVICE(dev);
> + VirtIOIOMMU *s = VIRTIO_IOMMU(dev);
> +
> + g_tree_destroy(s->domains);
> + g_tree_destroy(s->endpoints);
>
> virtio_cleanup(vdev);
> }
> --
> 2.17.2
Hi Bharat,
On 11/23/18 7:38 AM, Bharat Bhushan wrote:
> Hi Eric,
>
>> -----Original Message-----
>> From: Eric Auger <eric.auger@redhat.com>
>> Sent: Thursday, November 22, 2018 10:45 PM
>> To: eric.auger.pro@gmail.com; eric.auger@redhat.com; qemu-
>> devel@nongnu.org; qemu-arm@nongnu.org; peter.maydell@linaro.org;
>> mst@redhat.com; jean-philippe.brucker@arm.com
>> Cc: kevin.tian@intel.com; tn@semihalf.com; Bharat Bhushan
>> <bharat.bhushan@nxp.com>; peterx@redhat.com
>> Subject: [RFC v9 06/17] virtio-iommu: Endpoint and domains structs and
>> helpers
>>
>> This patch introduce domain and endpoint internal datatypes. Both are
>> stored in RB trees. The domain owns a list of endpoints attached to it.
>>
>> Helpers to get/put end points and domains are introduced.
>> get() helpers will become static in subsequent patches.
>>
>> Signed-off-by: Eric Auger <eric.auger@redhat.com>
>>
>> ---
>>
>> v6 -> v7:
>> - on virtio_iommu_find_add_as the bus number computation may
>> not be finalized yet so we cannot register the EPs at that time.
>> Hence, let's remove the get_endpoint and also do not use the
>> bus number for building the memory region name string (only
>> used for debug though).
>
> Endpoint registration from virtio_iommu_find_add_as to PROBE request.
> It is mentioned that " the bus number computation may not be finalized ". Can you please give some more information.
> I am asking this because from vfio perspective translate/replay will be called much before the PROBE request and endpoint needed to be registered by that time.
When from virtio_iommu_find_add() gets called, there are cases where the
BDF of the device is not yet computed, typically if the EP is plugged on
a secondary bus. That's why I postponed the registration. Do you have
idea When you would need the registration to happen?
Thanks
Eric
>
>
> Thanks
> -Bharat
>
>>
>> v4 -> v5:
>> - initialize as->endpoint_list
>>
>> v3 -> v4:
>> - new separate patch
>> ---
>> hw/virtio/trace-events | 4 ++
>> hw/virtio/virtio-iommu.c | 125
>> ++++++++++++++++++++++++++++++++++++++-
>> 2 files changed, 128 insertions(+), 1 deletion(-)
>>
>> diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index
>> 9270b0463e..4b15086872 100644
>> --- a/hw/virtio/trace-events
>> +++ b/hw/virtio/trace-events
>> @@ -61,3 +61,7 @@ virtio_iommu_map(uint32_t domain_id, uint64_t
>> virt_start, uint64_t virt_end, uin virtio_iommu_unmap(uint32_t domain_id,
>> uint64_t virt_start, uint64_t virt_end) "domain=%d virt_start=0x%"PRIx64"
>> virt_end=0x%"PRIx64 virtio_iommu_translate(const char *name, uint32_t
>> rid, uint64_t iova, int flag) "mr=%s rid=%d addr=0x%"PRIx64" flag=%d"
>> virtio_iommu_init_iommu_mr(char *iommu_mr) "init %s"
>> +virtio_iommu_get_endpoint(uint32_t ep_id) "Alloc endpoint=%d"
>> +virtio_iommu_put_endpoint(uint32_t ep_id) "Free endpoint=%d"
>> +virtio_iommu_get_domain(uint32_t domain_id) "Alloc domain=%d"
>> +virtio_iommu_put_domain(uint32_t domain_id) "Free domain=%d"
>> diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index
>> dead062baf..1b9c3ba416 100644
>> --- a/hw/virtio/virtio-iommu.c
>> +++ b/hw/virtio/virtio-iommu.c
>> @@ -33,20 +33,124 @@
>> #include "hw/virtio/virtio-bus.h"
>> #include "hw/virtio/virtio-access.h"
>> #include "hw/virtio/virtio-iommu.h"
>> +#include "hw/pci/pci_bus.h"
>> +#include "hw/pci/pci.h"
>>
>> /* Max size */
>> #define VIOMMU_DEFAULT_QUEUE_SIZE 256
>>
>> +typedef struct viommu_domain {
>> + uint32_t id;
>> + GTree *mappings;
>> + QLIST_HEAD(, viommu_endpoint) endpoint_list; } viommu_domain;
>> +
>> +typedef struct viommu_endpoint {
>> + uint32_t id;
>> + viommu_domain *domain;
>> + QLIST_ENTRY(viommu_endpoint) next;
>> + VirtIOIOMMU *viommu;
>> +} viommu_endpoint;
>> +
>> +typedef struct viommu_interval {
>> + uint64_t low;
>> + uint64_t high;
>> +} viommu_interval;
>> +
>> static inline uint16_t virtio_iommu_get_sid(IOMMUDevice *dev) {
>> return PCI_BUILD_BDF(pci_bus_num(dev->bus), dev->devfn); }
>>
>> +static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer
>> +user_data) {
>> + viommu_interval *inta = (viommu_interval *)a;
>> + viommu_interval *intb = (viommu_interval *)b;
>> +
>> + if (inta->high <= intb->low) {
>> + return -1;
>> + } else if (intb->high <= inta->low) {
>> + return 1;
>> + } else {
>> + return 0;
>> + }
>> +}
>> +
>> +static void
>> virtio_iommu_detach_endpoint_from_domain(viommu_endpoint
>> +*ep) {
>> + QLIST_REMOVE(ep, next);
>> + ep->domain = NULL;
>> +}
>> +
>> +viommu_endpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s,
>> uint32_t
>> +ep_id); viommu_endpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s,
>> +uint32_t ep_id) {
>> + viommu_endpoint *ep;
>> +
>> + ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(ep_id));
>> + if (ep) {
>> + return ep;
>> + }
>> + ep = g_malloc0(sizeof(*ep));
>> + ep->id = ep_id;
>> + ep->viommu = s;
>> + trace_virtio_iommu_get_endpoint(ep_id);
>> + g_tree_insert(s->endpoints, GUINT_TO_POINTER(ep_id), ep);
>> + return ep;
>> +}
>> +
>> +static void virtio_iommu_put_endpoint(gpointer data) {
>> + viommu_endpoint *ep = (viommu_endpoint *)data;
>> +
>> + if (ep->domain) {
>> + virtio_iommu_detach_endpoint_from_domain(ep);
>> + g_tree_unref(ep->domain->mappings);
>> + }
>> +
>> + trace_virtio_iommu_put_endpoint(ep->id);
>> + g_free(ep);
>> +}
>> +
>> +viommu_domain *virtio_iommu_get_domain(VirtIOIOMMU *s, uint32_t
>> +domain_id); viommu_domain *virtio_iommu_get_domain(VirtIOIOMMU
>> *s,
>> +uint32_t domain_id) {
>> + viommu_domain *domain;
>> +
>> + domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id));
>> + if (domain) {
>> + return domain;
>> + }
>> + domain = g_malloc0(sizeof(*domain));
>> + domain->id = domain_id;
>> + domain->mappings =
>> g_tree_new_full((GCompareDataFunc)interval_cmp,
>> + NULL, (GDestroyNotify)g_free,
>> + (GDestroyNotify)g_free);
>> + g_tree_insert(s->domains, GUINT_TO_POINTER(domain_id), domain);
>> + QLIST_INIT(&domain->endpoint_list);
>> + trace_virtio_iommu_get_domain(domain_id);
>> + return domain;
>> +}
>> +
>> +static void virtio_iommu_put_domain(gpointer data) {
>> + viommu_domain *domain = (viommu_domain *)data;
>> + viommu_endpoint *iter, *tmp;
>> +
>> + 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);
>> +}
>> +
>> static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void
>> *opaque,
>> int devfn) {
>> VirtIOIOMMU *s = opaque;
>> IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus);
>> + static uint32_t mr_index;
>> IOMMUDevice *sdev;
>>
>> if (!sbus) {
>> @@ -60,7 +164,7 @@ static AddressSpace
>> *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
>> if (!sdev) {
>> char *name = g_strdup_printf("%s-%d-%d",
>> TYPE_VIRTIO_IOMMU_MEMORY_REGION,
>> - pci_bus_num(bus), devfn);
>> + mr_index++, devfn);
>> sdev = sbus->pbdev[devfn] = g_malloc0(sizeof(IOMMUDevice));
>>
>> sdev->viommu = s;
>> @@ -75,6 +179,7 @@ static AddressSpace
>> *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
>> UINT64_MAX);
>> address_space_init(&sdev->as,
>> MEMORY_REGION(&sdev->iommu_mr),
>> TYPE_VIRTIO_IOMMU);
>> + g_free(name);
>> }
>>
>> return &sdev->as;
>> @@ -332,6 +437,13 @@ static const VMStateDescription
>> vmstate_virtio_iommu_device = {
>> },
>> };
>>
>> +static gint int_cmp(gconstpointer a, gconstpointer b, gpointer
>> +user_data) {
>> + uint ua = GPOINTER_TO_UINT(a);
>> + uint ub = GPOINTER_TO_UINT(b);
>> + return (ua > ub) - (ua < ub);
>> +}
>> +
>> static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) {
>> VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -356,6 +468,8 @@ static
>> void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
>> virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MAP_UNMAP);
>> virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS);
>>
>> + qemu_mutex_init(&s->mutex);
>> +
>> memset(s->as_by_bus_num, 0, sizeof(s->as_by_bus_num));
>> s->as_by_busptr = g_hash_table_new(NULL, NULL);
>>
>> @@ -364,11 +478,20 @@ static void
>> virtio_iommu_device_realize(DeviceState *dev, Error **errp)
>> } else {
>> error_setg(errp, "VIRTIO-IOMMU is not attached to any PCI bus!");
>> }
>> +
>> + s->domains = g_tree_new_full((GCompareDataFunc)int_cmp,
>> + NULL, NULL, virtio_iommu_put_domain);
>> + s->endpoints = g_tree_new_full((GCompareDataFunc)int_cmp,
>> + NULL, NULL,
>> + virtio_iommu_put_endpoint);
>> }
>>
>> static void virtio_iommu_device_unrealize(DeviceState *dev, Error **errp)
>> {
>> VirtIODevice *vdev = VIRTIO_DEVICE(dev);
>> + VirtIOIOMMU *s = VIRTIO_IOMMU(dev);
>> +
>> + g_tree_destroy(s->domains);
>> + g_tree_destroy(s->endpoints);
>>
>> virtio_cleanup(vdev);
>> }
>> --
>> 2.17.2
>
>
Hi Eric,
> -----Original Message-----
> From: Auger Eric <eric.auger@redhat.com>
> Sent: Friday, November 23, 2018 1:23 PM
> To: Bharat Bhushan <bharat.bhushan@nxp.com>;
> eric.auger.pro@gmail.com; qemu-devel@nongnu.org; qemu-
> arm@nongnu.org; peter.maydell@linaro.org; mst@redhat.com; jean-
> philippe.brucker@arm.com
> Cc: tn@semihalf.com; kevin.tian@intel.com; peterx@redhat.com
> Subject: Re: [Qemu-devel] [RFC v9 06/17] virtio-iommu: Endpoint and
> domains structs and helpers
>
> Hi Bharat,
>
> On 11/23/18 7:38 AM, Bharat Bhushan wrote:
> > Hi Eric,
> >
> >> -----Original Message-----
> >> From: Eric Auger <eric.auger@redhat.com>
> >> Sent: Thursday, November 22, 2018 10:45 PM
> >> To: eric.auger.pro@gmail.com; eric.auger@redhat.com; qemu-
> >> devel@nongnu.org; qemu-arm@nongnu.org; peter.maydell@linaro.org;
> >> mst@redhat.com; jean-philippe.brucker@arm.com
> >> Cc: kevin.tian@intel.com; tn@semihalf.com; Bharat Bhushan
> >> <bharat.bhushan@nxp.com>; peterx@redhat.com
> >> Subject: [RFC v9 06/17] virtio-iommu: Endpoint and domains structs
> >> and helpers
> >>
> >> This patch introduce domain and endpoint internal datatypes. Both are
> >> stored in RB trees. The domain owns a list of endpoints attached to it.
> >>
> >> Helpers to get/put end points and domains are introduced.
> >> get() helpers will become static in subsequent patches.
> >>
> >> Signed-off-by: Eric Auger <eric.auger@redhat.com>
> >>
> >> ---
> >>
> >> v6 -> v7:
> >> - on virtio_iommu_find_add_as the bus number computation may
> >> not be finalized yet so we cannot register the EPs at that time.
> >> Hence, let's remove the get_endpoint and also do not use the
> >> bus number for building the memory region name string (only
> >> used for debug though).
> >
> > Endpoint registration from virtio_iommu_find_add_as to PROBE request.
> > It is mentioned that " the bus number computation may not be finalized ".
> Can you please give some more information.
> > I am asking this because from vfio perspective translate/replay will be
> called much before the PROBE request and endpoint needed to be
> registered by that time.
> When from virtio_iommu_find_add() gets called, there are cases where the
> BDF of the device is not yet computed, typically if the EP is plugged on a
> secondary bus. That's why I postponed the registration. Do you have idea
> When you would need the registration to happen?
We want the endpoint registeration before replay/translate() is called for both virtio/vfio and I am trying to understand when we should register the endpoint.
I am looking at amd iommu, there pci_setup_iommu() provides the callback function which is called with "devfn" from pci_device_iommu_address_space(). Are you saying that devfn provided by pci_device_iommu_address_space() can be invalid?
Thanks
-Bharat
>
> Thanks
>
> Eric
> >
> >
> > Thanks
> > -Bharat
> >
> >>
> >> v4 -> v5:
> >> - initialize as->endpoint_list
> >>
> >> v3 -> v4:
> >> - new separate patch
> >> ---
> >> hw/virtio/trace-events | 4 ++
> >> hw/virtio/virtio-iommu.c | 125
> >> ++++++++++++++++++++++++++++++++++++++-
> >> 2 files changed, 128 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index
> >> 9270b0463e..4b15086872 100644
> >> --- a/hw/virtio/trace-events
> >> +++ b/hw/virtio/trace-events
> >> @@ -61,3 +61,7 @@ virtio_iommu_map(uint32_t domain_id, uint64_t
> >> virt_start, uint64_t virt_end, uin virtio_iommu_unmap(uint32_t
> >> domain_id, uint64_t virt_start, uint64_t virt_end) "domain=%d
> virt_start=0x%"PRIx64"
> >> virt_end=0x%"PRIx64 virtio_iommu_translate(const char *name,
> >> uint32_t rid, uint64_t iova, int flag) "mr=%s rid=%d addr=0x%"PRIx64"
> flag=%d"
> >> virtio_iommu_init_iommu_mr(char *iommu_mr) "init %s"
> >> +virtio_iommu_get_endpoint(uint32_t ep_id) "Alloc endpoint=%d"
> >> +virtio_iommu_put_endpoint(uint32_t ep_id) "Free endpoint=%d"
> >> +virtio_iommu_get_domain(uint32_t domain_id) "Alloc domain=%d"
> >> +virtio_iommu_put_domain(uint32_t domain_id) "Free domain=%d"
> >> diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c
> >> index
> >> dead062baf..1b9c3ba416 100644
> >> --- a/hw/virtio/virtio-iommu.c
> >> +++ b/hw/virtio/virtio-iommu.c
> >> @@ -33,20 +33,124 @@
> >> #include "hw/virtio/virtio-bus.h"
> >> #include "hw/virtio/virtio-access.h"
> >> #include "hw/virtio/virtio-iommu.h"
> >> +#include "hw/pci/pci_bus.h"
> >> +#include "hw/pci/pci.h"
> >>
> >> /* Max size */
> >> #define VIOMMU_DEFAULT_QUEUE_SIZE 256
> >>
> >> +typedef struct viommu_domain {
> >> + uint32_t id;
> >> + GTree *mappings;
> >> + QLIST_HEAD(, viommu_endpoint) endpoint_list; } viommu_domain;
> >> +
> >> +typedef struct viommu_endpoint {
> >> + uint32_t id;
> >> + viommu_domain *domain;
> >> + QLIST_ENTRY(viommu_endpoint) next;
> >> + VirtIOIOMMU *viommu;
> >> +} viommu_endpoint;
> >> +
> >> +typedef struct viommu_interval {
> >> + uint64_t low;
> >> + uint64_t high;
> >> +} viommu_interval;
> >> +
> >> static inline uint16_t virtio_iommu_get_sid(IOMMUDevice *dev) {
> >> return PCI_BUILD_BDF(pci_bus_num(dev->bus), dev->devfn); }
> >>
> >> +static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer
> >> +user_data) {
> >> + viommu_interval *inta = (viommu_interval *)a;
> >> + viommu_interval *intb = (viommu_interval *)b;
> >> +
> >> + if (inta->high <= intb->low) {
> >> + return -1;
> >> + } else if (intb->high <= inta->low) {
> >> + return 1;
> >> + } else {
> >> + return 0;
> >> + }
> >> +}
> >> +
> >> +static void
> >> virtio_iommu_detach_endpoint_from_domain(viommu_endpoint
> >> +*ep) {
> >> + QLIST_REMOVE(ep, next);
> >> + ep->domain = NULL;
> >> +}
> >> +
> >> +viommu_endpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s,
> >> uint32_t
> >> +ep_id); viommu_endpoint *virtio_iommu_get_endpoint(VirtIOIOMMU
> *s,
> >> +uint32_t ep_id) {
> >> + viommu_endpoint *ep;
> >> +
> >> + ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(ep_id));
> >> + if (ep) {
> >> + return ep;
> >> + }
> >> + ep = g_malloc0(sizeof(*ep));
> >> + ep->id = ep_id;
> >> + ep->viommu = s;
> >> + trace_virtio_iommu_get_endpoint(ep_id);
> >> + g_tree_insert(s->endpoints, GUINT_TO_POINTER(ep_id), ep);
> >> + return ep;
> >> +}
> >> +
> >> +static void virtio_iommu_put_endpoint(gpointer data) {
> >> + viommu_endpoint *ep = (viommu_endpoint *)data;
> >> +
> >> + if (ep->domain) {
> >> + virtio_iommu_detach_endpoint_from_domain(ep);
> >> + g_tree_unref(ep->domain->mappings);
> >> + }
> >> +
> >> + trace_virtio_iommu_put_endpoint(ep->id);
> >> + g_free(ep);
> >> +}
> >> +
> >> +viommu_domain *virtio_iommu_get_domain(VirtIOIOMMU *s,
> uint32_t
> >> +domain_id); viommu_domain
> *virtio_iommu_get_domain(VirtIOIOMMU
> >> *s,
> >> +uint32_t domain_id) {
> >> + viommu_domain *domain;
> >> +
> >> + domain = g_tree_lookup(s->domains,
> GUINT_TO_POINTER(domain_id));
> >> + if (domain) {
> >> + return domain;
> >> + }
> >> + domain = g_malloc0(sizeof(*domain));
> >> + domain->id = domain_id;
> >> + domain->mappings =
> >> g_tree_new_full((GCompareDataFunc)interval_cmp,
> >> + NULL, (GDestroyNotify)g_free,
> >> + (GDestroyNotify)g_free);
> >> + g_tree_insert(s->domains, GUINT_TO_POINTER(domain_id),
> domain);
> >> + QLIST_INIT(&domain->endpoint_list);
> >> + trace_virtio_iommu_get_domain(domain_id);
> >> + return domain;
> >> +}
> >> +
> >> +static void virtio_iommu_put_domain(gpointer data) {
> >> + viommu_domain *domain = (viommu_domain *)data;
> >> + viommu_endpoint *iter, *tmp;
> >> +
> >> + 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);
> >> +}
> >> +
> >> static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void
> >> *opaque,
> >> int devfn) {
> >> VirtIOIOMMU *s = opaque;
> >> IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus);
> >> + static uint32_t mr_index;
> >> IOMMUDevice *sdev;
> >>
> >> if (!sbus) {
> >> @@ -60,7 +164,7 @@ static AddressSpace
> >> *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
> >> if (!sdev) {
> >> char *name = g_strdup_printf("%s-%d-%d",
> >> TYPE_VIRTIO_IOMMU_MEMORY_REGION,
> >> - pci_bus_num(bus), devfn);
> >> + mr_index++, devfn);
> >> sdev = sbus->pbdev[devfn] = g_malloc0(sizeof(IOMMUDevice));
> >>
> >> sdev->viommu = s;
> >> @@ -75,6 +179,7 @@ static AddressSpace
> >> *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
> >> UINT64_MAX);
> >> address_space_init(&sdev->as,
> >> MEMORY_REGION(&sdev->iommu_mr),
> >> TYPE_VIRTIO_IOMMU);
> >> + g_free(name);
> >> }
> >>
> >> return &sdev->as;
> >> @@ -332,6 +437,13 @@ static const VMStateDescription
> >> vmstate_virtio_iommu_device = {
> >> },
> >> };
> >>
> >> +static gint int_cmp(gconstpointer a, gconstpointer b, gpointer
> >> +user_data) {
> >> + uint ua = GPOINTER_TO_UINT(a);
> >> + uint ub = GPOINTER_TO_UINT(b);
> >> + return (ua > ub) - (ua < ub);
> >> +}
> >> +
> >> static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
> {
> >> VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -356,6 +468,8 @@
> >> static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
> >> virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MAP_UNMAP);
> >> virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS);
> >>
> >> + qemu_mutex_init(&s->mutex);
> >> +
> >> memset(s->as_by_bus_num, 0, sizeof(s->as_by_bus_num));
> >> s->as_by_busptr = g_hash_table_new(NULL, NULL);
> >>
> >> @@ -364,11 +478,20 @@ static void
> >> virtio_iommu_device_realize(DeviceState *dev, Error **errp)
> >> } else {
> >> error_setg(errp, "VIRTIO-IOMMU is not attached to any PCI bus!");
> >> }
> >> +
> >> + s->domains = g_tree_new_full((GCompareDataFunc)int_cmp,
> >> + NULL, NULL, virtio_iommu_put_domain);
> >> + s->endpoints = g_tree_new_full((GCompareDataFunc)int_cmp,
> >> + NULL, NULL,
> >> + virtio_iommu_put_endpoint);
> >> }
> >>
> >> static void virtio_iommu_device_unrealize(DeviceState *dev, Error
> >> **errp) {
> >> VirtIODevice *vdev = VIRTIO_DEVICE(dev);
> >> + VirtIOIOMMU *s = VIRTIO_IOMMU(dev);
> >> +
> >> + g_tree_destroy(s->domains);
> >> + g_tree_destroy(s->endpoints);
> >>
> >> virtio_cleanup(vdev);
> >> }
> >> --
> >> 2.17.2
> >
> >
Hi Bharat,
On 11/23/18 10:14 AM, Bharat Bhushan wrote:
> Hi Eric,
>
>> -----Original Message-----
>> From: Auger Eric <eric.auger@redhat.com>
>> Sent: Friday, November 23, 2018 1:23 PM
>> To: Bharat Bhushan <bharat.bhushan@nxp.com>;
>> eric.auger.pro@gmail.com; qemu-devel@nongnu.org; qemu-
>> arm@nongnu.org; peter.maydell@linaro.org; mst@redhat.com; jean-
>> philippe.brucker@arm.com
>> Cc: tn@semihalf.com; kevin.tian@intel.com; peterx@redhat.com
>> Subject: Re: [Qemu-devel] [RFC v9 06/17] virtio-iommu: Endpoint and
>> domains structs and helpers
>>
>> Hi Bharat,
>>
>> On 11/23/18 7:38 AM, Bharat Bhushan wrote:
>>> Hi Eric,
>>>
>>>> -----Original Message-----
>>>> From: Eric Auger <eric.auger@redhat.com>
>>>> Sent: Thursday, November 22, 2018 10:45 PM
>>>> To: eric.auger.pro@gmail.com; eric.auger@redhat.com; qemu-
>>>> devel@nongnu.org; qemu-arm@nongnu.org; peter.maydell@linaro.org;
>>>> mst@redhat.com; jean-philippe.brucker@arm.com
>>>> Cc: kevin.tian@intel.com; tn@semihalf.com; Bharat Bhushan
>>>> <bharat.bhushan@nxp.com>; peterx@redhat.com
>>>> Subject: [RFC v9 06/17] virtio-iommu: Endpoint and domains structs
>>>> and helpers
>>>>
>>>> This patch introduce domain and endpoint internal datatypes. Both are
>>>> stored in RB trees. The domain owns a list of endpoints attached to it.
>>>>
>>>> Helpers to get/put end points and domains are introduced.
>>>> get() helpers will become static in subsequent patches.
>>>>
>>>> Signed-off-by: Eric Auger <eric.auger@redhat.com>
>>>>
>>>> ---
>>>>
>>>> v6 -> v7:
>>>> - on virtio_iommu_find_add_as the bus number computation may
>>>> not be finalized yet so we cannot register the EPs at that time.
>>>> Hence, let's remove the get_endpoint and also do not use the
>>>> bus number for building the memory region name string (only
>>>> used for debug though).
>>>
>>> Endpoint registration from virtio_iommu_find_add_as to PROBE request.
>>> It is mentioned that " the bus number computation may not be finalized ".
>> Can you please give some more information.
>>> I am asking this because from vfio perspective translate/replay will be
>> called much before the PROBE request and endpoint needed to be
>> registered by that time.
>> When from virtio_iommu_find_add() gets called, there are cases where the
>> BDF of the device is not yet computed, typically if the EP is plugged on a
>> secondary bus. That's why I postponed the registration. Do you have idea
>> When you would need the registration to happen?
>
> We want the endpoint registeration before replay/translate() is called for both virtio/vfio and I am trying to understand when we should register the endpoint.
> I am looking at amd iommu, there pci_setup_iommu() provides the callback function which is called with "devfn" from pci_device_iommu_address_space(). Are you saying that devfn provided by pci_device_iommu_address_space() can be invalid?
pci_bus_num() can return something wrong if called from
virtio_iommu_find_add_as.
That's what on smmuv3 (same on Intel), we use a separate
smmu_find_smmu_pcibus() called later. See comment for smmu_find_smmu_pcibus.
Thanks
Eric
>
> Thanks
> -Bharat
>
>>
>> Thanks
>>
>> Eric
>>>
>>>
>>> Thanks
>>> -Bharat
>>>
>>>>
>>>> v4 -> v5:
>>>> - initialize as->endpoint_list
>>>>
>>>> v3 -> v4:
>>>> - new separate patch
>>>> ---
>>>> hw/virtio/trace-events | 4 ++
>>>> hw/virtio/virtio-iommu.c | 125
>>>> ++++++++++++++++++++++++++++++++++++++-
>>>> 2 files changed, 128 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index
>>>> 9270b0463e..4b15086872 100644
>>>> --- a/hw/virtio/trace-events
>>>> +++ b/hw/virtio/trace-events
>>>> @@ -61,3 +61,7 @@ virtio_iommu_map(uint32_t domain_id, uint64_t
>>>> virt_start, uint64_t virt_end, uin virtio_iommu_unmap(uint32_t
>>>> domain_id, uint64_t virt_start, uint64_t virt_end) "domain=%d
>> virt_start=0x%"PRIx64"
>>>> virt_end=0x%"PRIx64 virtio_iommu_translate(const char *name,
>>>> uint32_t rid, uint64_t iova, int flag) "mr=%s rid=%d addr=0x%"PRIx64"
>> flag=%d"
>>>> virtio_iommu_init_iommu_mr(char *iommu_mr) "init %s"
>>>> +virtio_iommu_get_endpoint(uint32_t ep_id) "Alloc endpoint=%d"
>>>> +virtio_iommu_put_endpoint(uint32_t ep_id) "Free endpoint=%d"
>>>> +virtio_iommu_get_domain(uint32_t domain_id) "Alloc domain=%d"
>>>> +virtio_iommu_put_domain(uint32_t domain_id) "Free domain=%d"
>>>> diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c
>>>> index
>>>> dead062baf..1b9c3ba416 100644
>>>> --- a/hw/virtio/virtio-iommu.c
>>>> +++ b/hw/virtio/virtio-iommu.c
>>>> @@ -33,20 +33,124 @@
>>>> #include "hw/virtio/virtio-bus.h"
>>>> #include "hw/virtio/virtio-access.h"
>>>> #include "hw/virtio/virtio-iommu.h"
>>>> +#include "hw/pci/pci_bus.h"
>>>> +#include "hw/pci/pci.h"
>>>>
>>>> /* Max size */
>>>> #define VIOMMU_DEFAULT_QUEUE_SIZE 256
>>>>
>>>> +typedef struct viommu_domain {
>>>> + uint32_t id;
>>>> + GTree *mappings;
>>>> + QLIST_HEAD(, viommu_endpoint) endpoint_list; } viommu_domain;
>>>> +
>>>> +typedef struct viommu_endpoint {
>>>> + uint32_t id;
>>>> + viommu_domain *domain;
>>>> + QLIST_ENTRY(viommu_endpoint) next;
>>>> + VirtIOIOMMU *viommu;
>>>> +} viommu_endpoint;
>>>> +
>>>> +typedef struct viommu_interval {
>>>> + uint64_t low;
>>>> + uint64_t high;
>>>> +} viommu_interval;
>>>> +
>>>> static inline uint16_t virtio_iommu_get_sid(IOMMUDevice *dev) {
>>>> return PCI_BUILD_BDF(pci_bus_num(dev->bus), dev->devfn); }
>>>>
>>>> +static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer
>>>> +user_data) {
>>>> + viommu_interval *inta = (viommu_interval *)a;
>>>> + viommu_interval *intb = (viommu_interval *)b;
>>>> +
>>>> + if (inta->high <= intb->low) {
>>>> + return -1;
>>>> + } else if (intb->high <= inta->low) {
>>>> + return 1;
>>>> + } else {
>>>> + return 0;
>>>> + }
>>>> +}
>>>> +
>>>> +static void
>>>> virtio_iommu_detach_endpoint_from_domain(viommu_endpoint
>>>> +*ep) {
>>>> + QLIST_REMOVE(ep, next);
>>>> + ep->domain = NULL;
>>>> +}
>>>> +
>>>> +viommu_endpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s,
>>>> uint32_t
>>>> +ep_id); viommu_endpoint *virtio_iommu_get_endpoint(VirtIOIOMMU
>> *s,
>>>> +uint32_t ep_id) {
>>>> + viommu_endpoint *ep;
>>>> +
>>>> + ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(ep_id));
>>>> + if (ep) {
>>>> + return ep;
>>>> + }
>>>> + ep = g_malloc0(sizeof(*ep));
>>>> + ep->id = ep_id;
>>>> + ep->viommu = s;
>>>> + trace_virtio_iommu_get_endpoint(ep_id);
>>>> + g_tree_insert(s->endpoints, GUINT_TO_POINTER(ep_id), ep);
>>>> + return ep;
>>>> +}
>>>> +
>>>> +static void virtio_iommu_put_endpoint(gpointer data) {
>>>> + viommu_endpoint *ep = (viommu_endpoint *)data;
>>>> +
>>>> + if (ep->domain) {
>>>> + virtio_iommu_detach_endpoint_from_domain(ep);
>>>> + g_tree_unref(ep->domain->mappings);
>>>> + }
>>>> +
>>>> + trace_virtio_iommu_put_endpoint(ep->id);
>>>> + g_free(ep);
>>>> +}
>>>> +
>>>> +viommu_domain *virtio_iommu_get_domain(VirtIOIOMMU *s,
>> uint32_t
>>>> +domain_id); viommu_domain
>> *virtio_iommu_get_domain(VirtIOIOMMU
>>>> *s,
>>>> +uint32_t domain_id) {
>>>> + viommu_domain *domain;
>>>> +
>>>> + domain = g_tree_lookup(s->domains,
>> GUINT_TO_POINTER(domain_id));
>>>> + if (domain) {
>>>> + return domain;
>>>> + }
>>>> + domain = g_malloc0(sizeof(*domain));
>>>> + domain->id = domain_id;
>>>> + domain->mappings =
>>>> g_tree_new_full((GCompareDataFunc)interval_cmp,
>>>> + NULL, (GDestroyNotify)g_free,
>>>> + (GDestroyNotify)g_free);
>>>> + g_tree_insert(s->domains, GUINT_TO_POINTER(domain_id),
>> domain);
>>>> + QLIST_INIT(&domain->endpoint_list);
>>>> + trace_virtio_iommu_get_domain(domain_id);
>>>> + return domain;
>>>> +}
>>>> +
>>>> +static void virtio_iommu_put_domain(gpointer data) {
>>>> + viommu_domain *domain = (viommu_domain *)data;
>>>> + viommu_endpoint *iter, *tmp;
>>>> +
>>>> + 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);
>>>> +}
>>>> +
>>>> static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void
>>>> *opaque,
>>>> int devfn) {
>>>> VirtIOIOMMU *s = opaque;
>>>> IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus);
>>>> + static uint32_t mr_index;
>>>> IOMMUDevice *sdev;
>>>>
>>>> if (!sbus) {
>>>> @@ -60,7 +164,7 @@ static AddressSpace
>>>> *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
>>>> if (!sdev) {
>>>> char *name = g_strdup_printf("%s-%d-%d",
>>>> TYPE_VIRTIO_IOMMU_MEMORY_REGION,
>>>> - pci_bus_num(bus), devfn);
>>>> + mr_index++, devfn);
>>>> sdev = sbus->pbdev[devfn] = g_malloc0(sizeof(IOMMUDevice));
>>>>
>>>> sdev->viommu = s;
>>>> @@ -75,6 +179,7 @@ static AddressSpace
>>>> *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
>>>> UINT64_MAX);
>>>> address_space_init(&sdev->as,
>>>> MEMORY_REGION(&sdev->iommu_mr),
>>>> TYPE_VIRTIO_IOMMU);
>>>> + g_free(name);
>>>> }
>>>>
>>>> return &sdev->as;
>>>> @@ -332,6 +437,13 @@ static const VMStateDescription
>>>> vmstate_virtio_iommu_device = {
>>>> },
>>>> };
>>>>
>>>> +static gint int_cmp(gconstpointer a, gconstpointer b, gpointer
>>>> +user_data) {
>>>> + uint ua = GPOINTER_TO_UINT(a);
>>>> + uint ub = GPOINTER_TO_UINT(b);
>>>> + return (ua > ub) - (ua < ub);
>>>> +}
>>>> +
>>>> static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
>> {
>>>> VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -356,6 +468,8 @@
>>>> static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
>>>> virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MAP_UNMAP);
>>>> virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS);
>>>>
>>>> + qemu_mutex_init(&s->mutex);
>>>> +
>>>> memset(s->as_by_bus_num, 0, sizeof(s->as_by_bus_num));
>>>> s->as_by_busptr = g_hash_table_new(NULL, NULL);
>>>>
>>>> @@ -364,11 +478,20 @@ static void
>>>> virtio_iommu_device_realize(DeviceState *dev, Error **errp)
>>>> } else {
>>>> error_setg(errp, "VIRTIO-IOMMU is not attached to any PCI bus!");
>>>> }
>>>> +
>>>> + s->domains = g_tree_new_full((GCompareDataFunc)int_cmp,
>>>> + NULL, NULL, virtio_iommu_put_domain);
>>>> + s->endpoints = g_tree_new_full((GCompareDataFunc)int_cmp,
>>>> + NULL, NULL,
>>>> + virtio_iommu_put_endpoint);
>>>> }
>>>>
>>>> static void virtio_iommu_device_unrealize(DeviceState *dev, Error
>>>> **errp) {
>>>> VirtIODevice *vdev = VIRTIO_DEVICE(dev);
>>>> + VirtIOIOMMU *s = VIRTIO_IOMMU(dev);
>>>> +
>>>> + g_tree_destroy(s->domains);
>>>> + g_tree_destroy(s->endpoints);
>>>>
>>>> virtio_cleanup(vdev);
>>>> }
>>>> --
>>>> 2.17.2
>>>
>>>
© 2016 - 2026 Red Hat, Inc.