On Thu, Nov 18, 2021 at 09:12:41AM +0800, Lu Baolu wrote:
> The existing iommu_attach_device() allows only for singleton group. As
> we have added group ownership attribute, we can enforce this interface
> only for kernel domain usage.

Below is what I came up with.
 - Replace the file * with a simple void *

 - Use owner_count == 0 <-> dma_owner == DMA_OWNER to simplify
    the logic and remove levels of indent

 - Add a kernel state DMA_OWNER_PRIVATE_DOMAIN

 - Rename the user state to DMA_OWNER_PRIVATE_DOMAIN_USER

   It differs from the above because it does extra work to keep the
   group isolated that kernel users do no need to do.
 
 - Rename the kernel state to DMA_OWNER_DMA_API to better reflect
   its purpose. Inspired by Robin's point that alot of this is
   indirectly coupled to the domain pointer.

 - Have iommu_attach_device() atomically swap from DMA_OWNER_DMA_API
   to DMA_OWNER_PRIVATE_DOMAIN - replaces the group size check.

When we figure out tegra we can add an WARN_ON to iommu_attach_group()
that dma_owner != DMA_OWNER_NONE || DMA_OWNER_DMA_API

Then the whole thing makes some general sense..

Jason

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 064d0679906afd..4cafe074775e30 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -49,7 +49,7 @@ struct iommu_group {
        struct list_head entry;
        enum iommu_dma_owner dma_owner;
        refcount_t owner_cnt;
-       struct file *owner_user_file;
+       void *owner_cookie;
 };
 
 struct group_device {
@@ -1937,12 +1937,18 @@ int iommu_attach_device(struct iommu_domain *domain, 
struct device *dev)
         * change while we are attaching
         */
        mutex_lock(&group->mutex);
-       ret = -EINVAL;
-       if (iommu_group_device_count(group) != 1)
+       if (group->dma_owner != DMA_OWNER_DMA_API ||
+           refcount_read(&group->owner_cnt) != 1) {
+               ret = -EBUSY;
                goto out_unlock;
+       }
 
        ret = __iommu_attach_group(domain, group);
+       if (ret)
+               goto out_unlock;
 
+       group->dma_owner = DMA_OWNER_PRIVATE_DOMAIN;
+       group->owner_cookie = domain;
 out_unlock:
        mutex_unlock(&group->mutex);
        iommu_group_put(group);
@@ -2193,14 +2199,11 @@ void iommu_detach_device(struct iommu_domain *domain, 
struct device *dev)
                return;
 
        mutex_lock(&group->mutex);
-       if (iommu_group_device_count(group) != 1) {
-               WARN_ON(1);
-               goto out_unlock;
-       }
-
+       WARN_ON(group->dma_owner != DMA_OWNER_PRIVATE_DOMAIN ||
+               refcount_read(&group->owner_cnt) != 1 ||
+               group->owner_cookie != domain);
+       group->dma_owner = DMA_OWNER_DMA_API;
        __iommu_detach_group(domain, group);
-
-out_unlock:
        mutex_unlock(&group->mutex);
        iommu_group_put(group);
 }
@@ -3292,44 +3295,33 @@ static ssize_t iommu_group_store_type(struct 
iommu_group *group,
 
 static int __iommu_group_set_dma_owner(struct iommu_group *group,
                                       enum iommu_dma_owner owner,
-                                      struct file *user_file)
+                                      void *owner_cookie)
 {
-       if (group->dma_owner != DMA_OWNER_NONE && group->dma_owner != owner)
-               return -EBUSY;
-
-       if (owner == DMA_OWNER_USER) {
-               if (!user_file)
-                       return -EINVAL;
-
-               if (group->owner_user_file && group->owner_user_file != 
user_file)
-                       return -EPERM;
+       if (refcount_inc_not_zero(&group->owner_cnt)) {
+               if (group->dma_owner != owner ||
+                   group->owner_cookie != owner_cookie) {
+                       refcount_dec(&group->owner_cnt);
+                       return -EBUSY;
+               }
+               return 0;
        }
 
-       if (!refcount_inc_not_zero(&group->owner_cnt)) {
-               group->dma_owner = owner;
-               refcount_set(&group->owner_cnt, 1);
-
-               if (owner == DMA_OWNER_USER) {
-                       /*
-                        * The UNMANAGED domain shouldn't be attached before
-                        * claiming the USER ownership for the first time.
-                        */
-                       if (group->domain) {
-                               if (group->domain != group->default_domain) {
-                                       group->dma_owner = DMA_OWNER_NONE;
-                                       refcount_set(&group->owner_cnt, 0);
-
-                                       return -EBUSY;
-                               }
-
-                               __iommu_detach_group(group->domain, group);
-                       }
-
-                       get_file(user_file);
-                       group->owner_user_file = user_file;
+       /*
+        * We must ensure that any device DMAs issued after this call
+        * are discarded. DMAs can only reach real memory once someone
+        * has attached a real domain.
+        */
+       if (owner == DMA_OWNER_PRIVATE_DOMAIN_USER) {
+               if (group->domain) {
+                       if (group->domain != group->default_domain)
+                               return -EBUSY;
+                       __iommu_detach_group(group->domain, group);
                }
        }
 
+       group->dma_owner = owner;
+       group->owner_cookie = owner_cookie;
+       refcount_set(&group->owner_cnt, 1);
        return 0;
 }
 
@@ -3339,20 +3331,18 @@ static void __iommu_group_release_dma_owner(struct 
iommu_group *group,
        if (WARN_ON(group->dma_owner != owner))
                return;
 
-       if (refcount_dec_and_test(&group->owner_cnt)) {
-               group->dma_owner = DMA_OWNER_NONE;
+       if (!refcount_dec_and_test(&group->owner_cnt))
+               return;
 
-               if (owner == DMA_OWNER_USER) {
-                       fput(group->owner_user_file);
-                       group->owner_user_file = NULL;
+       group->dma_owner = DMA_OWNER_NONE;
 
-                       /*
-                        * The UNMANAGED domain should be detached before all 
USER
-                        * owners have been released.
-                        */
-                       if (!WARN_ON(group->domain) && group->default_domain)
-                               __iommu_attach_group(group->default_domain, 
group);
-               }
+       /*
+        * The UNMANAGED domain should be detached before all USER
+        * owners have been released.
+        */
+       if (owner == DMA_OWNER_PRIVATE_DOMAIN_USER) {
+               if (!WARN_ON(group->domain) && group->default_domain)
+                       __iommu_attach_group(group->default_domain, group);
        }
 }
 
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index d8946f22edd5df..7f50dfa7207e9c 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -164,14 +164,21 @@ enum iommu_dev_features {
 
 /**
  * enum iommu_dma_owner - IOMMU DMA ownership
- * @DMA_OWNER_NONE: No DMA ownership
- * @DMA_OWNER_KERNEL: Device DMAs are initiated by a kernel driver
- * @DMA_OWNER_USER: Device DMAs are initiated by a userspace driver
+ * @DMA_OWNER_NONE:
+ *  No DMA ownership
+ * @DMA_OWNER_DMA_API:
+ *  Device DMAs are initiated by a kernel driver through the DMA API
+ * @DMA_OWNER_PRIVATE_DOMAIN:
+ *  Device DMAs are initiated by a kernel driver
+ * @DMA_OWNER_PRIVATE_DOMAIN_USER:
+ *  Device DMAs are initiated by userspace, kernel ensures that DMAs
+ *  never go to kernel memory.
  */
 enum iommu_dma_owner {
        DMA_OWNER_NONE,
-       DMA_OWNER_KERNEL,
-       DMA_OWNER_USER,
+       DMA_OWNER_DMA_API,
+       DMA_OWNER_PRIVATE_DOMAIN,
+       DMA_OWNER_PRIVATE_DOMAIN_USER,
 };
 
 #define IOMMU_PASID_INVALID    (-1U)
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to