qemu-arm
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-arm] [Qemu-devel] [PATCH v3 2/2] virtio-iommu: vfio integratio


From: Auger Eric
Subject: Re: [Qemu-arm] [Qemu-devel] [PATCH v3 2/2] virtio-iommu: vfio integration with virtio-iommu
Date: Mon, 18 Sep 2017 09:47:40 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.4.0

Hi Bharat,

On 21/08/2017 12:48, Bharat Bhushan wrote:
> This RFC patch allows virtio-iommu protection for PCI
> device-passthrough.
> 
> MSI region is mapped by current version of virtio-iommu driver.
> This uses VFIO extension of map/unmap notification when an area
> of memory is mappedi/unmapped in emulated iommu device.
> 
> This series is tested with 2 PCI devices to virtual machine using
> dma-ops and DPDK in VM is not yet tested.
> 
> Also with this series we observe below prints for MSI region mapping
> 
>    "qemu-system-aarch64: iommu map to non memory area 0"
> 
>    This print comes when vfio/map-notifier is called for MSI region.
> 
> vfio map/unmap notification is called for given device
>    This assumes that devid passed in virtio_iommu_attach is same as devfn
>    This assumption is based on 1:1 mapping of requested-id with device-id
>    in QEMU.
> 
> Signed-off-by: Bharat Bhushan <address@hidden>
> ---
> v2->v3:
>  - Addressed review comments:
>     - virtio-iommu_map_region function is split in two functions
>       virtio_iommu_notify_map/virtio_iommu_notify_unmap
>     - use size received from driver and do not split in 4K pages
>  
>  - map/unmap notification is called for given device/as
>    This relies on devid passed in virtio_iommu_attach is same as devfn
>    This is assumed as iommu-map maps 1:1 requested-id to device-id in QEMU
>    Looking for comment about this assumtion.
> 
>  - Keeping track devices in address-space
> 
>  - Verified with 2 PCI endpoints
>  - some code cleanup
> 
>  hw/virtio/trace-events           |   5 ++
>  hw/virtio/virtio-iommu.c         | 163 
> +++++++++++++++++++++++++++++++++++++++
>  include/hw/virtio/virtio-iommu.h |   6 ++
>  3 files changed, 174 insertions(+)
> 
> diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
> index 8db3d91..7e9663f 100644
> --- a/hw/virtio/trace-events
> +++ b/hw/virtio/trace-events
> @@ -39,3 +39,8 @@ virtio_iommu_unmap_left_interval(uint64_t low, uint64_t 
> high, uint64_t next_low,
>  virtio_iommu_unmap_right_interval(uint64_t low, uint64_t high, uint64_t 
> next_low, uint64_t next_high) "Unmap right [0x%"PRIx64",0x%"PRIx64"], new 
> interval=[0x%"PRIx64",0x%"PRIx64"]"
>  virtio_iommu_unmap_inc_interval(uint64_t low, uint64_t high) "Unmap inc 
> [0x%"PRIx64",0x%"PRIx64"]"
>  virtio_iommu_translate_result(uint64_t virt_addr, uint64_t phys_addr, 
> uint32_t sid) "0x%"PRIx64" -> 0x%"PRIx64 " for sid=%d"
> +virtio_iommu_notify_flag_add(const char *iommu) "Add virtio-iommu notifier 
> node for memory region %s"
> +virtio_iommu_notify_flag_del(const char *iommu) "Del virtio-iommu notifier 
> node for memory region %s"
> +virtio_iommu_remap(hwaddr iova, hwaddr pa, hwaddr size) "iova=0x%"PRIx64" 
> pa=0x%" PRIx64" size=0x%"PRIx64""
> +virtio_iommu_notify_map(hwaddr iova, hwaddr paddr, hwaddr map_size) 
> "iova=0x%"PRIx64" pa=0x%" PRIx64" size=0x%"PRIx64""
> +virtio_iommu_notify_unmap(hwaddr iova, hwaddr paddr, hwaddr map_size) 
> "iova=0x%"PRIx64" pa=0x%" PRIx64" size=0x%"PRIx64""
useless paddr
> diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c
> index 9217587..9eae050 100644
> --- a/hw/virtio/virtio-iommu.c
> +++ b/hw/virtio/virtio-iommu.c
> @@ -55,11 +55,13 @@ typedef struct viommu_interval {
>  typedef struct viommu_dev {
>      uint32_t id;
>      viommu_as *as;
> +    QLIST_ENTRY(viommu_dev) next;
>  } viommu_dev;
>  
>  struct viommu_as {
>      uint32_t id;
>      GTree *mappings;
> +    QLIST_HEAD(, viommu_dev) device_list;
>  };
>  
>  static inline uint16_t virtio_iommu_get_sid(IOMMUDevice *dev)
> @@ -133,12 +135,70 @@ static gint interval_cmp(gconstpointer a, gconstpointer 
> b, gpointer user_data)
>      }
>  }
>  
> +static void virtio_iommu_notify_map(IOMMUMemoryRegion *mr, hwaddr iova,
> +                                    hwaddr paddr, hwaddr size)
> +{
> +    IOMMUTLBEntry entry;
> +
> +    entry.target_as = &address_space_memory;
> +    entry.addr_mask = size - 1;
> +
> +    entry.iova = iova;
> +    trace_virtio_iommu_notify_map(iova, paddr, size);
> +    entry.perm = IOMMU_RW;
> +    entry.translated_addr = paddr;
> +
> +    memory_region_notify_iommu(mr, entry);
> +}
> +
> +static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr iova,
> +                                      hwaddr paddr, hwaddr size)
unmap() doesn't need to have paddr arg.
> +{
> +    IOMMUTLBEntry entry;
> +
> +    entry.target_as = &address_space_memory;
> +    entry.addr_mask = size - 1;
> +
> +    entry.iova = iova;
> +    trace_virtio_iommu_notify_unmap(iova, paddr, size);
ditto
> +    entry.perm = IOMMU_NONE;
> +    entry.translated_addr = 0;
> +
> +    memory_region_notify_iommu(mr, entry);
> +}
> +
> +static gboolean virtio_iommu_maping_unmap(gpointer key, gpointer value,
> +                                          gpointer data)
s/virtio_iommu_maping_unmap/virtio_iommu_mapping_unmap
> +{
> +    viommu_mapping *mapping = (viommu_mapping *) value;
> +    IOMMUMemoryRegion *mr = (IOMMUMemoryRegion *) data;
> +
> +    virtio_iommu_notify_unmap(mr, mapping->virt_addr, 0, mapping->size);
> +
> +    return true;
> +}
> +
>  static void virtio_iommu_detach_dev(VirtIOIOMMU *s, viommu_dev *dev)
>  {
> +    VirtioIOMMUNotifierNode *node;
>      viommu_as *as = dev->as;
> +    int devid = dev->id;
>  
>      trace_virtio_iommu_detach(dev->id);
>  
> +    /* Remove device from address-space list */
> +    QLIST_REMOVE(dev, next);
> +    /* unmap all if no devices attached to address-spaceRemove */
comment to be reworked
> +    if (QLIST_EMPTY(&as->device_list)) {
I don't get why you execute the code below only if the device_list is
empty. Each time a device is detached don't you need to notify its
associated iommu_mr?
> +        QLIST_FOREACH(node, &s->notifiers_list, next) {
> +            if (devid == node->iommu_dev->devfn) {
> +                g_tree_foreach(as->mappings, virtio_iommu_maping_unmap,
> +                               &node->iommu_dev->iommu_mr);
> +            }
> +        }
> +    }
> +
> +    /* Remove device from global list */
>      g_tree_remove(s->devices, GUINT_TO_POINTER(dev->id));
>      g_tree_unref(as->mappings);
>  }
> @@ -171,6 +231,7 @@ static int virtio_iommu_attach(VirtIOIOMMU *s,
>      if (!as) {
>          as = g_malloc0(sizeof(*as));
>          as->id = asid;
> +        QLIST_INIT(&as->device_list);
>          as->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp,
>                                           NULL, NULL, (GDestroyNotify)g_free);
>          g_tree_insert(s->address_spaces, GUINT_TO_POINTER(asid), as);
> @@ -182,6 +243,7 @@ static int virtio_iommu_attach(VirtIOIOMMU *s,
>      dev->id = devid;
>      trace_virtio_iommu_new_devid(devid);
>      g_tree_insert(s->devices, GUINT_TO_POINTER(devid), dev);
> +    QLIST_INSERT_HEAD(&as->device_list, dev, next);
>      g_tree_ref(as->mappings);
>  
>      return VIRTIO_IOMMU_S_OK;
> @@ -219,6 +281,8 @@ static int virtio_iommu_map(VirtIOIOMMU *s,
>      viommu_as *as;
>      viommu_interval *interval;
>      viommu_mapping *mapping;
> +    VirtioIOMMUNotifierNode *node;
> +    viommu_dev *dev;
>  
>      interval = g_malloc0(sizeof(*interval));
>  
> @@ -230,6 +294,11 @@ static int virtio_iommu_map(VirtIOIOMMU *s,
>          return VIRTIO_IOMMU_S_NOENT;
>      }
>  
> +    dev = QLIST_FIRST(&as->device_list);
> +    if (!dev) {
> +        return VIRTIO_IOMMU_S_NOENT;
> +    }
> +
>      mapping = g_tree_lookup(as->mappings, (gpointer)interval);
>      if (mapping) {
>          g_free(interval);
> @@ -246,6 +315,14 @@ static int virtio_iommu_map(VirtIOIOMMU *s,
>  
>      g_tree_insert(as->mappings, interval, mapping);
>  
> +    /* All devices in an address-space share mapping */
> +    QLIST_FOREACH(node, &s->notifiers_list, next) {
> +        if (dev->id == node->iommu_dev->devfn) {
> +            virtio_iommu_notify_map(&node->iommu_dev->iommu_mr, virt_addr,
> +                                    phys_addr, size);
> +        }
> +    }
> +
>      return VIRTIO_IOMMU_S_OK;
>  }
>  
> @@ -259,6 +336,8 @@ static int virtio_iommu_unmap(VirtIOIOMMU *s,
>      viommu_mapping *mapping;
>      viommu_interval interval;
>      viommu_as *as;
> +    VirtioIOMMUNotifierNode *node;
> +    viommu_dev *dev;
>  
>      trace_virtio_iommu_unmap(asid, virt_addr, size, flags);
>  
> @@ -267,6 +346,12 @@ static int virtio_iommu_unmap(VirtIOIOMMU *s,
>          error_report("%s: no as", __func__);
>          return VIRTIO_IOMMU_S_NOENT;
>      }
> +
> +    dev = QLIST_FIRST(&as->device_list);
> +    if (!dev) {
> +        return VIRTIO_IOMMU_S_NOENT;
> +    }
> +
>      interval.low = virt_addr;
>      interval.high = virt_addr + size - 1;
>  
> @@ -296,7 +381,15 @@ static int virtio_iommu_unmap(VirtIOIOMMU *s,
>          } else {
>              break;
>          }
> +
extra line
>          if (interval.low >= interval.high) {
> +            /* All devices in an address-space share mapping */
> +            QLIST_FOREACH(node, &s->notifiers_list, next) {
> +                if (dev->id == node->iommu_dev->devfn) {
> +                    virtio_iommu_notify_unmap(&node->iommu_dev->iommu_mr, 
> virt_addr,
> +                                              0, size);
Why do you notify only a single mr? All devices belonging to the as are
affected by this change. So don't you need to notify all as device's mr?
> +                }
> +            }
>              return VIRTIO_IOMMU_S_OK;
>          } else {
>              mapping = g_tree_lookup(as->mappings, (gpointer)&interval);
> @@ -439,6 +532,36 @@ static void virtio_iommu_handle_command(VirtIODevice 
> *vdev, VirtQueue *vq)
>      }
>  }
>  
> +static void virtio_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu_mr,
> +                                          IOMMUNotifierFlag old,
> +                                          IOMMUNotifierFlag new)
> +{
> +    IOMMUDevice *sdev = container_of(iommu_mr, IOMMUDevice, iommu_mr);
> +    VirtIOIOMMU *s = sdev->viommu;
> +    VirtioIOMMUNotifierNode *node = NULL;
> +    VirtioIOMMUNotifierNode *next_node = NULL;
> +
> +    if (old == IOMMU_NOTIFIER_NONE) {
> +        trace_virtio_iommu_notify_flag_add(iommu_mr->parent_obj.name);
> +        node = g_malloc0(sizeof(*node));
> +        node->iommu_dev = sdev;
> +        QLIST_INSERT_HEAD(&s->notifiers_list, node, next);
> +        return;
> +    }
> +
> +    /* update notifier node with new flags */
> +    QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) {
> +        if (node->iommu_dev == sdev) {
> +            if (new == IOMMU_NOTIFIER_NONE) {
> +                
> trace_virtio_iommu_notify_flag_del(iommu_mr->parent_obj.name);
> +                QLIST_REMOVE(node, next);
> +                g_free(node);
> +            }
> +            return;
> +        }
> +    }
> +}
> +
>  static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr 
> addr,
>                                              IOMMUAccessFlags flag)
>  {
> @@ -553,11 +676,49 @@ static gint int_cmp(gconstpointer a, gconstpointer b, 
> gpointer user_data)
>      return (ua > ub) - (ua < ub);
>  }
>  
> +static gboolean virtio_iommu_remap(gpointer key, gpointer value, gpointer 
> data)
> +{
> +    viommu_mapping *mapping = (viommu_mapping *) value;
> +    IOMMUMemoryRegion *mr = (IOMMUMemoryRegion *) data;
> +
> +    trace_virtio_iommu_remap(mapping->virt_addr, mapping->phys_addr,
> +                             mapping->size);
> +    /* unmap previous entry and map again */
> +    virtio_iommu_notify_unmap(mr, mapping->virt_addr, 0, mapping->size);
> +
> +    virtio_iommu_notify_map(mr, mapping->virt_addr, mapping->phys_addr,
> +                            mapping->size);
> +    return true;
you need to return false here otherwise  g_tree_foreach will return on
the first as mapping.

Thanks

Eric
> +}
> +
> +static void virtio_iommu_replay(IOMMUMemoryRegion *mr, IOMMUNotifier *n)
> +{
> +    IOMMUDevice *sdev = container_of(mr, IOMMUDevice, iommu_mr);
> +    VirtIOIOMMU *s = sdev->viommu;
> +    uint32_t sid;
> +    viommu_dev *dev;
> +
> +    sid = virtio_iommu_get_sid(sdev);
> +
> +    qemu_mutex_lock(&s->mutex);
> +
> +    dev = g_tree_lookup(s->devices, GUINT_TO_POINTER(sid));
> +    if (!dev) {
> +        goto unlock;
> +    }
> +
> +    g_tree_foreach(dev->as->mappings, virtio_iommu_remap, mr);
> +
> +unlock:
> +    qemu_mutex_unlock(&s->mutex);
> +}
> +
>  static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
>  {
>      VirtIODevice *vdev = VIRTIO_DEVICE(dev);
>      VirtIOIOMMU *s = VIRTIO_IOMMU(dev);
>  
> +    QLIST_INIT(&s->notifiers_list);
>      virtio_init(vdev, "virtio-iommu", VIRTIO_ID_IOMMU,
>                  sizeof(struct virtio_iommu_config));
>  
> @@ -644,6 +805,8 @@ static void 
> virtio_iommu_memory_region_class_init(ObjectClass *klass,
>      IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
>  
>      imrc->translate = virtio_iommu_translate;
> +    imrc->notify_flag_changed = virtio_iommu_notify_flag_changed;
> +    imrc->replay = virtio_iommu_replay;
>  }
>  
>  static const TypeInfo virtio_iommu_info = {
> diff --git a/include/hw/virtio/virtio-iommu.h 
> b/include/hw/virtio/virtio-iommu.h
> index f9c988f..7e04184 100644
> --- a/include/hw/virtio/virtio-iommu.h
> +++ b/include/hw/virtio/virtio-iommu.h
> @@ -46,6 +46,11 @@ typedef struct IOMMUPciBus {
>      IOMMUDevice  *pbdev[0]; /* Parent array is sparse, so dynamically alloc 
> */
>  } IOMMUPciBus;
>  
> +typedef struct VirtioIOMMUNotifierNode {
> +    IOMMUDevice *iommu_dev;
> +    QLIST_ENTRY(VirtioIOMMUNotifierNode) next;
> +} VirtioIOMMUNotifierNode;
> +
>  typedef struct VirtIOIOMMU {
>      VirtIODevice parent_obj;
>      VirtQueue *vq;
> @@ -56,6 +61,7 @@ typedef struct VirtIOIOMMU {
>      GTree *address_spaces;
>      QemuMutex mutex;
>      GTree *devices;
> +    QLIST_HEAD(, VirtioIOMMUNotifierNode) notifiers_list;
>  } VirtIOIOMMU;
>  
>  #endif
> 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]