Once a DMABUF is revoked the domain will be unmapped under the pages
mutex. Double unmapping will trigger a WARN, and mapping while revoked
will fail.

Check for revoked DMABUFs along all the map and unmap paths to resolve
this. Ensure that map/unmap is always done under the pages mutex so it is
synchronized with the revoke notifier.

If a revoke happens between allocating the iopt_pages and the population
to a domain then the population will succeed, and leave things unmapped as
though revoke had happened immediately after.

Currently there is no way to repopulate the domains. Userspace is expected
to know if it is going to do something that would trigger revoke (eg if it
is about to do a FLR) then it should go and remove the DMABUF mappings
before and put the back after. The revoke is only to protect the kernel
from mis-behaving userspace.

Reviewed-by: Nicolin Chen <[email protected]>
Reviewed-by: Kevin Tian <[email protected]>
Tested-by: Nicolin Chen <[email protected]>
Tested-by: Shuai Xue <[email protected]>
Signed-off-by: Jason Gunthorpe <[email protected]>
---
 drivers/iommu/iommufd/io_pagetable.c | 11 +++++-
 drivers/iommu/iommufd/io_pagetable.h |  8 +++++
 drivers/iommu/iommufd/pages.c        | 54 +++++++++++++++++-----------
 3 files changed, 52 insertions(+), 21 deletions(-)

diff --git a/drivers/iommu/iommufd/io_pagetable.c 
b/drivers/iommu/iommufd/io_pagetable.c
index b3cf3825a88c7f..38c5fdc6c82128 100644
--- a/drivers/iommu/iommufd/io_pagetable.c
+++ b/drivers/iommu/iommufd/io_pagetable.c
@@ -970,9 +970,14 @@ static void iopt_unfill_domain(struct io_pagetable *iopt,
                                WARN_ON(!area->storage_domain);
                        if (area->storage_domain == domain)
                                area->storage_domain = storage_domain;
+                       if (iopt_is_dmabuf(pages)) {
+                               if (!iopt_dmabuf_revoked(pages))
+                                       iopt_area_unmap_domain(area, domain);
+                       }
                        mutex_unlock(&pages->mutex);
 
-                       iopt_area_unmap_domain(area, domain);
+                       if (!iopt_is_dmabuf(pages))
+                               iopt_area_unmap_domain(area, domain);
                }
                return;
        }
@@ -1261,6 +1266,10 @@ static int iopt_area_split(struct iopt_area *area, 
unsigned long iova)
        if (!pages || area->prevent_access)
                return -EBUSY;
 
+       /* Maintaining the domains_itree below is a bit complicated */
+       if (iopt_is_dmabuf(pages))
+               return -EOPNOTSUPP;
+
        if (new_start & (alignment - 1) ||
            iopt_area_start_byte(area, new_start) & (alignment - 1))
                return -EINVAL;
diff --git a/drivers/iommu/iommufd/io_pagetable.h 
b/drivers/iommu/iommufd/io_pagetable.h
index 08b6bfb6b14489..892daf4b1f1eea 100644
--- a/drivers/iommu/iommufd/io_pagetable.h
+++ b/drivers/iommu/iommufd/io_pagetable.h
@@ -238,6 +238,14 @@ static inline bool iopt_is_dmabuf(struct iopt_pages *pages)
        return pages->type == IOPT_ADDRESS_DMABUF;
 }
 
+static inline bool iopt_dmabuf_revoked(struct iopt_pages *pages)
+{
+       lockdep_assert_held(&pages->mutex);
+       if (iopt_is_dmabuf(pages))
+               return pages->dmabuf.phys.len == 0;
+       return false;
+}
+
 struct iopt_pages *iopt_alloc_user_pages(void __user *uptr,
                                         unsigned long length, bool writable);
 struct iopt_pages *iopt_alloc_file_pages(struct file *file, unsigned long 
start,
diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c
index 89a5c999e3a2d4..e8bfd734fb695b 100644
--- a/drivers/iommu/iommufd/pages.c
+++ b/drivers/iommu/iommufd/pages.c
@@ -1650,6 +1650,9 @@ void iopt_area_unmap_domain(struct iopt_area *area, 
struct iommu_domain *domain)
 void iopt_area_unfill_domain(struct iopt_area *area, struct iopt_pages *pages,
                             struct iommu_domain *domain)
 {
+       if (iopt_dmabuf_revoked(pages))
+               return;
+
        __iopt_area_unfill_domain(area, pages, domain,
                                  iopt_area_last_index(area));
 }
@@ -1670,6 +1673,9 @@ int iopt_area_fill_domain(struct iopt_area *area, struct 
iommu_domain *domain)
 
        lockdep_assert_held(&area->pages->mutex);
 
+       if (iopt_dmabuf_revoked(area->pages))
+               return 0;
+
        rc = pfn_reader_first(&pfns, area->pages, iopt_area_index(area),
                              iopt_area_last_index(area));
        if (rc)
@@ -1729,33 +1735,38 @@ int iopt_area_fill_domains(struct iopt_area *area, 
struct iopt_pages *pages)
                return 0;
 
        mutex_lock(&pages->mutex);
-       rc = pfn_reader_first(&pfns, pages, iopt_area_index(area),
-                             iopt_area_last_index(area));
-       if (rc)
-               goto out_unlock;
+       if (!iopt_dmabuf_revoked(pages)) {
+               rc = pfn_reader_first(&pfns, pages, iopt_area_index(area),
+                                     iopt_area_last_index(area));
+               if (rc)
+                       goto out_unlock;
 
-       while (!pfn_reader_done(&pfns)) {
-               done_first_end_index = pfns.batch_end_index;
-               done_all_end_index = pfns.batch_start_index;
-               xa_for_each(&area->iopt->domains, index, domain) {
-                       rc = batch_to_domain(&pfns.batch, domain, area,
-                                            pfns.batch_start_index);
+               while (!pfn_reader_done(&pfns)) {
+                       done_first_end_index = pfns.batch_end_index;
+                       done_all_end_index = pfns.batch_start_index;
+                       xa_for_each(&area->iopt->domains, index, domain) {
+                               rc = batch_to_domain(&pfns.batch, domain, area,
+                                                    pfns.batch_start_index);
+                               if (rc)
+                                       goto out_unmap;
+                       }
+                       done_all_end_index = done_first_end_index;
+
+                       rc = pfn_reader_next(&pfns);
                        if (rc)
                                goto out_unmap;
                }
-               done_all_end_index = done_first_end_index;
-
-               rc = pfn_reader_next(&pfns);
+               rc = pfn_reader_update_pinned(&pfns);
                if (rc)
                        goto out_unmap;
+
+               pfn_reader_destroy(&pfns);
        }
-       rc = pfn_reader_update_pinned(&pfns);
-       if (rc)
-               goto out_unmap;
 
        area->storage_domain = xa_load(&area->iopt->domains, 0);
        interval_tree_insert(&area->pages_node, &pages->domains_itree);
-       goto out_destroy;
+       mutex_unlock(&pages->mutex);
+       return 0;
 
 out_unmap:
        pfn_reader_release_pins(&pfns);
@@ -1782,7 +1793,6 @@ int iopt_area_fill_domains(struct iopt_area *area, struct 
iopt_pages *pages)
                                                        end_index);
                }
        }
-out_destroy:
        pfn_reader_destroy(&pfns);
 out_unlock:
        mutex_unlock(&pages->mutex);
@@ -1809,11 +1819,15 @@ void iopt_area_unfill_domains(struct iopt_area *area, 
struct iopt_pages *pages)
        if (!area->storage_domain)
                goto out_unlock;
 
-       xa_for_each(&iopt->domains, index, domain)
-               if (domain != area->storage_domain)
+       xa_for_each(&iopt->domains, index, domain) {
+               if (domain == area->storage_domain)
+                       continue;
+
+               if (!iopt_dmabuf_revoked(pages))
                        iopt_area_unmap_domain_range(
                                area, domain, iopt_area_index(area),
                                iopt_area_last_index(area));
+       }
 
        if (IS_ENABLED(CONFIG_IOMMUFD_TEST))
                WARN_ON(RB_EMPTY_NODE(&area->pages_node.rb));
-- 
2.43.0

Reply via email to