The new HW queue object, as an internal iommufd object, wants to reuse the
struct iommufd_access to pin some iova range in the iopt.

However, an access generally takes the refcount of an ictx. So, in such an
internal case, a deadlock could happen when the release of the ictx has to
wait for the release of the access first when releasing a hw_queue object,
which could wait for the release of the ictx that is refcounted:
    ictx --releases--> hw_queue --releases--> access
      ^                                         |
      |_________________releases________________v

To address this, add a set of lightweight internal APIs to unlink the ictx
and the access, i.e. no ictx refcounting by the access:
    ictx --releases--> hw_queue --releases--> access

Then, there's no point in setting the access->ictx. So simply define !ictx
as an flag for an internal use and add an inline helper.

Signed-off-by: Nicolin Chen <nicol...@nvidia.com>
---
 drivers/iommu/iommufd/iommufd_private.h | 23 ++++++++++
 drivers/iommu/iommufd/device.c          | 59 +++++++++++++++++++++----
 2 files changed, 73 insertions(+), 9 deletions(-)

diff --git a/drivers/iommu/iommufd/iommufd_private.h 
b/drivers/iommu/iommufd/iommufd_private.h
index 09f895638f68..9d1f55deb9ca 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -484,6 +484,29 @@ void iopt_remove_access(struct io_pagetable *iopt,
                        struct iommufd_access *access, u32 iopt_access_list_id);
 void iommufd_access_destroy_object(struct iommufd_object *obj);
 
+/* iommufd_access for internal use */
+static inline bool iommufd_access_is_internal(struct iommufd_access *access)
+{
+       return !access->ictx;
+}
+
+struct iommufd_access *iommufd_access_create_internal(struct iommufd_ctx 
*ictx);
+
+static inline void
+iommufd_access_destroy_internal(struct iommufd_ctx *ictx,
+                               struct iommufd_access *access)
+{
+       iommufd_object_destroy_user(ictx, &access->obj);
+}
+
+int iommufd_access_attach_internal(struct iommufd_access *access,
+                                  struct iommufd_ioas *ioas);
+
+static inline void iommufd_access_detach_internal(struct iommufd_access 
*access)
+{
+       iommufd_access_detach(access);
+}
+
 struct iommufd_eventq {
        struct iommufd_object obj;
        struct iommufd_ctx *ictx;
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index e9b6ca47095c..07a4ff753c12 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -1084,7 +1084,39 @@ void iommufd_access_destroy_object(struct iommufd_object 
*obj)
        if (access->ioas)
                WARN_ON(iommufd_access_change_ioas(access, NULL));
        mutex_unlock(&access->ioas_lock);
-       iommufd_ctx_put(access->ictx);
+       if (!iommufd_access_is_internal(access))
+               iommufd_ctx_put(access->ictx);
+}
+
+static struct iommufd_access *__iommufd_access_create(struct iommufd_ctx *ictx)
+{
+       struct iommufd_access *access;
+
+       /*
+        * There is no uAPI for the access object, but to keep things symmetric
+        * use the object infrastructure anyhow.
+        */
+       access = iommufd_object_alloc(ictx, access, IOMMUFD_OBJ_ACCESS);
+       if (IS_ERR(access))
+               return access;
+
+       /* The calling driver is a user until iommufd_access_destroy() */
+       refcount_inc(&access->obj.users);
+       mutex_init(&access->ioas_lock);
+       return access;
+}
+
+struct iommufd_access *iommufd_access_create_internal(struct iommufd_ctx *ictx)
+{
+       struct iommufd_access *access;
+
+       access = __iommufd_access_create(ictx);
+       if (IS_ERR(access))
+               return access;
+       access->iova_alignment = PAGE_SIZE;
+
+       iommufd_object_finalize(ictx, &access->obj);
+       return access;
 }
 
 /**
@@ -1106,11 +1138,7 @@ iommufd_access_create(struct iommufd_ctx *ictx,
 {
        struct iommufd_access *access;
 
-       /*
-        * There is no uAPI for the access object, but to keep things symmetric
-        * use the object infrastructure anyhow.
-        */
-       access = iommufd_object_alloc(ictx, access, IOMMUFD_OBJ_ACCESS);
+       access = __iommufd_access_create(ictx);
        if (IS_ERR(access))
                return access;
 
@@ -1122,13 +1150,10 @@ iommufd_access_create(struct iommufd_ctx *ictx,
        else
                access->iova_alignment = 1;
 
-       /* The calling driver is a user until iommufd_access_destroy() */
-       refcount_inc(&access->obj.users);
        access->ictx = ictx;
        iommufd_ctx_get(ictx);
        iommufd_object_finalize(ictx, &access->obj);
        *id = access->obj.id;
-       mutex_init(&access->ioas_lock);
        return access;
 }
 EXPORT_SYMBOL_NS_GPL(iommufd_access_create, "IOMMUFD");
@@ -1173,6 +1198,22 @@ int iommufd_access_attach(struct iommufd_access *access, 
u32 ioas_id)
 }
 EXPORT_SYMBOL_NS_GPL(iommufd_access_attach, "IOMMUFD");
 
+int iommufd_access_attach_internal(struct iommufd_access *access,
+                                  struct iommufd_ioas *ioas)
+{
+       int rc;
+
+       mutex_lock(&access->ioas_lock);
+       if (WARN_ON(access->ioas)) {
+               mutex_unlock(&access->ioas_lock);
+               return -EINVAL;
+       }
+
+       rc = iommufd_access_change_ioas(access, ioas);
+       mutex_unlock(&access->ioas_lock);
+       return rc;
+}
+
 int iommufd_access_replace(struct iommufd_access *access, u32 ioas_id)
 {
        int rc;
-- 
2.43.0


Reply via email to