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 <mlevitsk@xxxxxxxxxx> --- 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