diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index a3030cd..ab1b82c 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -2019,15 +2019,24 @@ static int vfio_register_iommu_notifier(struct vfio_group *group, int ret; ret = vfio_group_add_container_user(group); - if (ret) + if (ret) { + pr_info("vfio_group_add_container_user failed with %d\n", ret); return -EINVAL; + } container = group->container; driver = container->iommu_driver; - if (likely(driver && driver->ops->register_notifier)) + if (likely(driver && driver->ops->register_notifier)) { ret = driver->ops->register_notifier(container->iommu_data, - events, nb); - else + events, nb); + if (unlikely(!ret) && driver->ops->retro_notify) { + ret = driver->ops->retro_notify(container->iommu_data); + if (unlikely((ret & NOTIFY_BAD) == NOTIFY_BAD)) + ret = -ENOTTY; + else + ret = 0; + } + } else ret = -ENOTTY; vfio_group_try_dissolve_container(group); @@ -2140,6 +2149,7 @@ int vfio_register_notifier(struct device *dev, enum vfio_notify_type type, ret = vfio_register_group_notifier(group, events, nb); break; default: + pr_info("bad notification type %d\n", type); ret = -EINVAL; } diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index d0f731c..b47b8f96 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -558,8 +558,10 @@ static int vfio_iommu_type1_pin_pages(void *iommu_data, return -EINVAL; /* Supported for v2 version only */ - if (!iommu->v2) + if (!iommu->v2) { + pr_debug("non v2 IOMMU\n"); return -EACCES; + } mutex_lock(&iommu->lock); @@ -1050,6 +1052,30 @@ static int vfio_pin_map_dma(struct vfio_iommu *iommu, struct vfio_dma *dma, return ret; } +static int vfio_dma_map_trigger_notifiers(struct vfio_iommu * const iommu, + struct vfio_dma const * const dma) + +{ + struct vfio_iommu_type1_dma_map nb_map = {0}; + + BUG_ON(!iommu); + BUG_ON(!dma); + + nb_map.flags = dma->prot; + + if ((dma->prot & IOMMU_READ) == IOMMU_READ) + nb_map.flags |= VFIO_DMA_MAP_FLAG_READ; + if ((dma->prot & IOMMU_WRITE) == IOMMU_WRITE) + nb_map.flags |= VFIO_DMA_MAP_FLAG_WRITE; + nb_map.vaddr = dma->vaddr; + nb_map.iova = dma->iova; + nb_map.size = dma->size; + + return blocking_notifier_call_chain(&iommu->notifier, + VFIO_IOMMU_NOTIFY_DMA_MAP, + &nb_map); +} + static int vfio_dma_do_map(struct vfio_iommu *iommu, struct vfio_iommu_type1_dma_map *map) { @@ -1139,13 +1165,25 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu, vfio_link_dma(iommu, dma); /* Don't pin and map if container doesn't contain IOMMU capable domain*/ - if (!IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu)) + if (!IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu)) { dma->size = size; - else + ret = 0; + } else ret = vfio_pin_map_dma(iommu, dma, size); out_unlock: mutex_unlock(&iommu->lock); + /* FIXME is the following safe without having acquired the mutex? */ + if (!IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu) && !ret) { + ret = vfio_dma_map_trigger_notifiers(iommu, dma); + /* FIXME proceed or clean up and fail? */ + if ((ret & NOTIFY_BAD) == NOTIFY_BAD) { + pr_debug("failed to trigger notifier(s): %d\n", ret); + ret = -EINVAL; + } else + ret = 0; + } + return ret; } @@ -1504,8 +1542,11 @@ static void vfio_sanity_check_pfn_list(struct vfio_iommu *iommu) dma = rb_entry(n, struct vfio_dma, node); - if (WARN_ON(!RB_EMPTY_ROOT(&dma->pfn_list))) + if (WARN_ON(!RB_EMPTY_ROOT(&dma->pfn_list))) { + pr_debug("DMA region %llx-%llx still pinned\n", + dma->iova, dma->iova + dma->size); break; + } } /* mdev vendor driver must unregister notifier */ WARN_ON(iommu->notifier.head); @@ -1740,7 +1781,7 @@ static int vfio_iommu_type1_register_notifier(void *iommu_data, struct vfio_iommu *iommu = iommu_data; /* clear known events */ - *events &= ~VFIO_IOMMU_NOTIFY_DMA_UNMAP; + *events &= ~(VFIO_IOMMU_NOTIFY_DMA_MAP | VFIO_IOMMU_NOTIFY_DMA_UNMAP); /* refuse to register if still events remaining */ if (*events) @@ -1749,6 +1790,25 @@ static int vfio_iommu_type1_register_notifier(void *iommu_data, return blocking_notifier_chain_register(&iommu->notifier, nb); } +static int vfio_iommu_type1_retro_notify(void *iommu_data) +{ + int err = NOTIFY_OK; + struct vfio_iommu *iommu; + struct vfio_dma *pos, *n; + + BUG_ON(!iommu_data); + + iommu = (struct vfio_iommu*)iommu_data; + + rbtree_postorder_for_each_entry_safe(pos, n, &iommu->dma_list, node) { + err = vfio_dma_map_trigger_notifiers(iommu, pos); + if ((err & NOTIFY_BAD) == NOTIFY_BAD) + break; + } + + return err; +} + static int vfio_iommu_type1_unregister_notifier(void *iommu_data, struct notifier_block *nb) { @@ -1769,6 +1829,7 @@ static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = { .unpin_pages = vfio_iommu_type1_unpin_pages, .register_notifier = vfio_iommu_type1_register_notifier, .unregister_notifier = vfio_iommu_type1_unregister_notifier, + .retro_notify = vfio_iommu_type1_retro_notify, }; static int __init vfio_iommu_type1_init(void) diff --git a/include/linux/vfio.h b/include/linux/vfio.h index 66741ab0..10ee80b 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -85,6 +85,7 @@ struct vfio_iommu_driver_ops { struct notifier_block *nb); int (*unregister_notifier)(void *iommu_data, struct notifier_block *nb); + int (*retro_notify)(void *iommu_data); }; extern int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops); @@ -118,6 +119,7 @@ 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)