Extract all common procedure, between the iommufd_access_attach API and a new iommufd_access_replace API, to an iommufd_access_change_pt helper. And separate an unlocked __iommufd_access_detach to use it in the helper too. This adds a new iommufd_access_replace() for VFIO emulated pathway to use. Suggested-by: Jason Gunthorpe <jgg@xxxxxxxxxx> Reviewed-by: Kevin Tian <kevin.tian@xxxxxxxxx> Signed-off-by: Nicolin Chen <nicolinc@xxxxxxxxxx> --- drivers/iommu/iommufd/device.c | 72 ++++++++++++++++++++++++++-------- include/linux/iommufd.h | 1 + 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 072912d87f53..1015d6c42e2b 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -757,13 +757,11 @@ void iommufd_access_destroy(struct iommufd_access *access) } EXPORT_SYMBOL_NS_GPL(iommufd_access_destroy, IOMMUFD); -void iommufd_access_detach(struct iommufd_access *access) +static void __iommufd_access_detach(struct iommufd_access *access) { struct iommufd_ioas *cur_ioas = access->ioas; - mutex_lock(&access->ioas_lock); - if (WARN_ON(!access->ioas)) - goto out; + lockdep_assert_held(&access->ioas_lock); /* * Set ioas to NULL to block any further iommufd_access_pin_pages(). * iommufd_access_unpin_pages() can continue using access->ioas_unpin. @@ -777,44 +775,86 @@ void iommufd_access_detach(struct iommufd_access *access) } iopt_remove_access(&cur_ioas->iopt, access); refcount_dec(&cur_ioas->obj.users); +} + +void iommufd_access_detach(struct iommufd_access *access) +{ + mutex_lock(&access->ioas_lock); + if (WARN_ON(!access->ioas)) + goto out; + __iommufd_access_detach(access); out: access->ioas_unpin = NULL; mutex_unlock(&access->ioas_lock); } EXPORT_SYMBOL_NS_GPL(iommufd_access_detach, IOMMUFD); -int iommufd_access_attach(struct iommufd_access *access, u32 ioas_id) +static int iommufd_access_change_pt(struct iommufd_access *access, u32 ioas_id) { + struct iommufd_ioas *cur_ioas = access->ioas; struct iommufd_ioas *new_ioas; - int rc = 0; + int rc; - mutex_lock(&access->ioas_lock); - if (WARN_ON(access->ioas || access->ioas_unpin)) { - mutex_unlock(&access->ioas_lock); - return -EINVAL; - } + lockdep_assert_held(&access->ioas_lock); new_ioas = iommufd_get_ioas(access->ictx, ioas_id); - if (IS_ERR(new_ioas)) { - mutex_unlock(&access->ioas_lock); + if (IS_ERR(new_ioas)) return PTR_ERR(new_ioas); - } + + if (cur_ioas) + __iommufd_access_detach(access); rc = iopt_add_access(&new_ioas->iopt, access); if (rc) { - mutex_unlock(&access->ioas_lock); iommufd_put_object(&new_ioas->obj); + if (cur_ioas) + WARN_ON(iommufd_access_change_pt(access, + cur_ioas->obj.id)); return rc; } iommufd_ref_to_users(&new_ioas->obj); access->ioas = new_ioas; access->ioas_unpin = new_ioas; - mutex_unlock(&access->ioas_lock); return 0; } + +int iommufd_access_attach(struct iommufd_access *access, u32 ioas_id) +{ + int rc; + + mutex_lock(&access->ioas_lock); + if (WARN_ON(access->ioas || access->ioas_unpin)) { + mutex_unlock(&access->ioas_lock); + return -EINVAL; + } + + rc = iommufd_access_change_pt(access, ioas_id); + mutex_unlock(&access->ioas_lock); + return rc; +} EXPORT_SYMBOL_NS_GPL(iommufd_access_attach, IOMMUFD); +int iommufd_access_replace(struct iommufd_access *access, u32 ioas_id) +{ + int rc; + + mutex_lock(&access->ioas_lock); + if (!access->ioas) { + mutex_unlock(&access->ioas_lock); + return -ENOENT; + } + if (access->ioas->obj.id == ioas_id) { + mutex_unlock(&access->ioas_lock); + return 0; + } + + rc = iommufd_access_change_pt(access, ioas_id); + mutex_unlock(&access->ioas_lock); + return rc; +} +EXPORT_SYMBOL_NS_GPL(iommufd_access_replace, IOMMUFD); + /** * iommufd_access_notify_unmap - Notify users of an iopt to stop using it * @iopt: iopt to work on diff --git a/include/linux/iommufd.h b/include/linux/iommufd.h index 0ac60256b659..ffc3a949f837 100644 --- a/include/linux/iommufd.h +++ b/include/linux/iommufd.h @@ -49,6 +49,7 @@ iommufd_access_create(struct iommufd_ctx *ictx, const struct iommufd_access_ops *ops, void *data, u32 *id); void iommufd_access_destroy(struct iommufd_access *access); int iommufd_access_attach(struct iommufd_access *access, u32 ioas_id); +int iommufd_access_replace(struct iommufd_access *access, u32 ioas_id); void iommufd_access_detach(struct iommufd_access *access); void iommufd_ctx_get(struct iommufd_ctx *ictx); -- 2.41.0