Allow an VFIO mdev device to listen to map events
This will allow a mdev driver to dma map memory
as soon as it gets added to the domain

--
Signed-off-by: Maxim Levitsky <mlevi...@redhat.com>
---
 drivers/vfio/vfio_iommu_type1.c | 97 +++++++++++++++++++++++++++++----
 include/linux/vfio.h            |  4 ++
 2 files changed, 89 insertions(+), 12 deletions(-)

diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index d0f731c9920a..393b56d78166 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -63,17 +63,22 @@ module_param_named(dma_entry_limit, dma_entry_limit, uint, 
0644);
 MODULE_PARM_DESC(dma_entry_limit,
                 "Maximum number of user DMA mappings per container (65535).");
 
+/* a container, usually one per VM*/
 struct vfio_iommu {
        struct list_head        domain_list;
        struct vfio_domain      *external_domain; /* domain for external user */
        struct mutex            lock;
        struct rb_root          dma_list;
-       struct blocking_notifier_head notifier;
+       struct blocking_notifier_head map_notifiers;
+       struct blocking_notifier_head unmap_notifiers;
        unsigned int            dma_avail;
        bool                    v2;
        bool                    nesting;
 };
 
+/* An IOMMU domain - also usually one per VM, unless devices assigned to VM
+ * are connected via different IOMMUs
+ */
 struct vfio_domain {
        struct iommu_domain     *domain;
        struct list_head        next;
@@ -563,8 +568,7 @@ static int vfio_iommu_type1_pin_pages(void *iommu_data,
 
        mutex_lock(&iommu->lock);
 
-       /* Fail if notifier list is empty */
-       if ((!iommu->external_domain) || (!iommu->notifier.head)) {
+       if (!iommu->external_domain) {
                ret = -EINVAL;
                goto pin_done;
        }
@@ -967,7 +971,7 @@ static int vfio_dma_do_unmap(struct vfio_iommu *iommu,
                         * invalidation.
                         */
                        mutex_unlock(&iommu->lock);
-                       blocking_notifier_call_chain(&iommu->notifier,
+                       blocking_notifier_call_chain(&iommu->unmap_notifiers,
                                                    VFIO_IOMMU_NOTIFY_DMA_UNMAP,
                                                    &nb_unmap);
                        goto again;
@@ -1144,6 +1148,22 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
        else
                ret = vfio_pin_map_dma(iommu, dma, size);
 
+       mutex_unlock(&iommu->lock);
+
+       /*
+        * Notify anyone (mdev vendor drivers) that new mapping has being
+        * created - vendor drivers can in response pin/dma map the memory
+        */
+       ret = blocking_notifier_call_chain(&iommu->map_notifiers,
+                                   VFIO_IOMMU_NOTIFY_DMA_MAP,
+                                   map);
+
+       ret = notifier_to_errno(ret);
+       if (ret)
+               vfio_remove_dma(iommu, dma);
+
+       return ret;
+
 out_unlock:
        mutex_unlock(&iommu->lock);
        return ret;
@@ -1508,7 +1528,8 @@ static void vfio_sanity_check_pfn_list(struct vfio_iommu 
*iommu)
                        break;
        }
        /* mdev vendor driver must unregister notifier */
-       WARN_ON(iommu->notifier.head);
+       WARN_ON(iommu->map_notifiers.head);
+       WARN_ON(iommu->unmap_notifiers.head);
 }
 
 static void vfio_iommu_type1_detach_group(void *iommu_data,
@@ -1598,7 +1619,8 @@ static void *vfio_iommu_type1_open(unsigned long arg)
        iommu->dma_list = RB_ROOT;
        iommu->dma_avail = dma_entry_limit;
        mutex_init(&iommu->lock);
-       BLOCKING_INIT_NOTIFIER_HEAD(&iommu->notifier);
+       BLOCKING_INIT_NOTIFIER_HEAD(&iommu->unmap_notifiers);
+       BLOCKING_INIT_NOTIFIER_HEAD(&iommu->map_notifiers);
 
        return iommu;
 }
@@ -1738,23 +1760,74 @@ static int vfio_iommu_type1_register_notifier(void 
*iommu_data,
                                              struct notifier_block *nb)
 {
        struct vfio_iommu *iommu = iommu_data;
+       struct rb_node *node;
+       int ret = 0;
+
+       if (*events == VFIO_IOMMU_NOTIFY_DMA_MAP) {
+
+               /* now register the notifier */
+               ret = blocking_notifier_chain_register(&iommu->map_notifiers,
+                               nb);
 
-       /* clear known events */
-       *events &= ~VFIO_IOMMU_NOTIFY_DMA_UNMAP;
+               /* replay the mapping */
+                node = rb_first(&iommu->dma_list);
+               while (node) {
+                       struct vfio_dma *dma = rb_entry(node, struct vfio_dma,
+                                       node);
 
-       /* refuse to register if still events remaining */
-       if (*events)
+                       struct vfio_iommu_type1_dma_map map;
+
+                       map.argsz = sizeof(struct vfio_iommu_type1_dma_map);
+                       map.flags = 0;
+
+                       if (dma->prot & IOMMU_READ)
+                               map.flags |= VFIO_DMA_MAP_FLAG_READ;
+                       if (dma->prot & IOMMU_WRITE)
+                               map.flags |= VFIO_DMA_MAP_FLAG_WRITE;
+
+                       map.iova = dma->iova;
+                       map.vaddr = dma->vaddr;
+                       map.size = dma->size;
+
+                       node = rb_next(node);
+
+                       /* Call only the first notifier, the one that
+                        * we just registered
+                        */
+                       ret = __blocking_notifier_call_chain(
+                                       &iommu->map_notifiers,
+                                       VFIO_IOMMU_NOTIFY_DMA_MAP,
+                                       &map, 1, NULL);
+
+                       ret = notifier_to_errno(ret);
+                       if (ret) {
+                               blocking_notifier_chain_unregister(
+                                               &iommu->map_notifiers, nb);
+                               return ret;
+                       }
+               }
+
+       } else if (*events == VFIO_IOMMU_NOTIFY_DMA_UNMAP) {
+               ret =  blocking_notifier_chain_register(
+                               &iommu->unmap_notifiers, nb);
+       } else {
                return -EINVAL;
+       }
+       return ret;
 
-       return blocking_notifier_chain_register(&iommu->notifier, nb);
 }
 
 static int vfio_iommu_type1_unregister_notifier(void *iommu_data,
                                                struct notifier_block *nb)
 {
        struct vfio_iommu *iommu = iommu_data;
+       int ret;
 
-       return blocking_notifier_chain_unregister(&iommu->notifier, nb);
+       ret = blocking_notifier_chain_unregister(&iommu->map_notifiers, nb);
+       if (ret)
+               ret = blocking_notifier_chain_unregister(
+                               &iommu->unmap_notifiers, nb);
+       return ret;
 }
 
 static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = {
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index 66741ab087c1..957f09263bfe 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -118,10 +118,14 @@ enum vfio_notify_type {
 
 /* events for VFIO_IOMMU_NOTIFY */
 #define VFIO_IOMMU_NOTIFY_DMA_UNMAP    BIT(0)
+#define VFIO_IOMMU_NOTIFY_DMA_MAP      BIT(1)
 
 /* events for VFIO_GROUP_NOTIFY */
 #define VFIO_GROUP_NOTIFY_SET_KVM      BIT(0)
 
+/* Note: currently you can only register a notifier for a single event
+ * at the time
+ */
 extern int vfio_register_notifier(struct device *dev,
                                  enum vfio_notify_type type,
                                  unsigned long *required_events,
-- 
2.17.2

Reply via email to