From: David Woodhouse <[email protected]>

iommufd's dma-buf import path programmed the IOMMU unconditionally with
BATCH_MMIO (IOMMU_MMIO set, IOMMU_CACHE cleared).  This is correct for
the VFIO PCI dma-buf exporter -- its phys is PCI BAR (MMIO) memory --
but it is the wrong prot for a dma-buf whose phys is RAM: on AMD-Vi the
device DMA silently misroutes (Identify Controller against a
gmem-provider-backed target completes with status=0 but the target
region is not written).

Carry the exporter's memory type through to the fill routine:

  - Add is_cpu_ram to iopt_pages_dmabuf and pfn_reader_dmabuf.
  - Make sym_vfio_pci_dma_buf_iommufd_map() also fill *is_cpu_ram.
    VFIO PCI's exporter is MMIO (false); the sample gmem provider's
    exporter is CPU RAM (true).
  - pfn_reader_fill_dmabuf() picks BATCH_CPU_MEMORY vs BATCH_MMIO based
    on the captured kind.

The eventual formal negotiated exporter op should return phys and
memory type together in one call, replacing both symbol_get() lookups.

Signed-off-by: David Woodhouse (Kiro) <[email protected]>
---
 drivers/iommu/iommufd/io_pagetable.h |  7 ++++++
 drivers/iommu/iommufd/pages.c        | 33 +++++++++++++++++++++++-----
 2 files changed, 34 insertions(+), 6 deletions(-)

diff --git a/drivers/iommu/iommufd/io_pagetable.h 
b/drivers/iommu/iommufd/io_pagetable.h
index 27e3e311d395..887ed94474c7 100644
--- a/drivers/iommu/iommufd/io_pagetable.h
+++ b/drivers/iommu/iommufd/io_pagetable.h
@@ -206,6 +206,13 @@ struct iopt_pages_dmabuf {
        /* Always PAGE_SIZE aligned */
        unsigned long start;
        struct list_head tracker;
+       /*
+        * true if the exporter's phys is CPU RAM (map with IOMMU_CACHE, no
+        * IOMMU_MMIO); false for MMIO/BAR memory (map with IOMMU_MMIO).  Set
+        * by the exporter dispatch at map time; consulted by
+        * pfn_reader_fill_dmabuf() to pick BATCH_CPU_MEMORY vs BATCH_MMIO.
+        */
+       bool is_cpu_ram;
 };
 
 /*
diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c
index 2d4ea41460fd..f9b2ae6d7e96 100644
--- a/drivers/iommu/iommufd/pages.c
+++ b/drivers/iommu/iommufd/pages.c
@@ -1080,6 +1080,7 @@ static int pfn_reader_user_update_pinned(struct 
pfn_reader_user *user,
 struct pfn_reader_dmabuf {
        struct phys_vec phys;
        unsigned long start_offset;
+       bool is_cpu_ram;
 };
 
 static int pfn_reader_dmabuf_init(struct pfn_reader_dmabuf *dmabuf,
@@ -1091,6 +1092,7 @@ static int pfn_reader_dmabuf_init(struct 
pfn_reader_dmabuf *dmabuf,
 
        dmabuf->phys = pages->dmabuf.phys;
        dmabuf->start_offset = pages->dmabuf.start;
+       dmabuf->is_cpu_ram = pages->dmabuf.is_cpu_ram;
        return 0;
 }
 
@@ -1106,9 +1108,15 @@ static int pfn_reader_fill_dmabuf(struct 
pfn_reader_dmabuf *dmabuf,
         * always filled using page size aligned PFNs just like the other types.
         * If the dmabuf has been sliced on a sub page offset then the common
         * batch to domain code will adjust it before mapping to the domain.
+        *
+        * The exporter's memory type (CPU RAM vs MMIO/BAR) selects the batch
+        * kind so downstream iommu_map sets IOMMU_CACHE for cache-coherent RAM
+        * or IOMMU_MMIO for BAR memory.  The kind was captured at map time by
+        * the exporter dispatch.
         */
        batch_add_pfn_num(batch, PHYS_PFN(dmabuf->phys.paddr + start),
-                         last_index - start_index + 1, BATCH_MMIO);
+                         last_index - start_index + 1,
+                         dmabuf->is_cpu_ram ? BATCH_CPU_MEMORY : BATCH_MMIO);
        return 0;
 }
 
@@ -1459,22 +1467,31 @@ static const struct dma_buf_attach_ops 
iopt_dmabuf_attach_revoke_ops = {
  * iommufd and vfio have a circular dependency. Future work for a phys
  * based private interconnect will remove this.
  */
+/*
+ * Look up the exporter's phys accessor for iommufd's private-interconnect
+ * path.  Also fills *is_cpu_ram: true if the exporter's memory is normal
+ * cache-coherent RAM (needs BATCH_CPU_MEMORY / IOMMU_CACHE), false for MMIO
+ * (needs BATCH_MMIO / IOMMU_MMIO).  This will be replaced by a formal
+ * exporter op that returns phys + memory type together.
+ */
 static int
 sym_vfio_pci_dma_buf_iommufd_map(struct dma_buf_attachment *attachment,
-                                struct phys_vec *phys)
+                                struct phys_vec *phys, bool *is_cpu_ram)
 {
        typeof(&vfio_pci_dma_buf_iommufd_map) fn;
        int rc;
 
        rc = iommufd_test_dma_buf_iommufd_map(attachment, phys);
-       if (rc != -EOPNOTSUPP)
+       if (rc != -EOPNOTSUPP) {
+               *is_cpu_ram = false;    /* test hook mimics VFIO MMIO */
                return rc;
+       }
 
        /*
         * Prototype: try the sample gmem provider's dma-buf exporter.  This
         * mirrors the vfio-pci private-interconnect hook, and (like it) is
         * meant to be replaced by a formal negotiated exporter op returning
-        * phys for iommufd.
+        * phys + memory type.  The provider serves RAM, so mark it CPU_RAM.
         */
        {
                extern int gmem_provider_dma_buf_iommufd_map(
@@ -1485,8 +1502,10 @@ sym_vfio_pci_dma_buf_iommufd_map(struct 
dma_buf_attachment *attachment,
                if (gfn) {
                        rc = gfn(attachment, phys);
                        symbol_put(gmem_provider_dma_buf_iommufd_map);
-                       if (rc != -EOPNOTSUPP)
+                       if (rc != -EOPNOTSUPP) {
+                               *is_cpu_ram = true;
                                return rc;
+                       }
                }
        }
 
@@ -1498,6 +1517,7 @@ sym_vfio_pci_dma_buf_iommufd_map(struct 
dma_buf_attachment *attachment,
                return -EOPNOTSUPP;
        rc = fn(attachment, phys);
        symbol_put(vfio_pci_dma_buf_iommufd_map);
+       *is_cpu_ram = false;    /* VFIO PCI dma-buf carries BAR (MMIO) memory */
        return rc;
 }
 
@@ -1526,7 +1546,8 @@ static int iopt_map_dmabuf(struct iommufd_ctx *ictx, 
struct iopt_pages *pages,
        if (rc)
                goto err_detach;
 
-       rc = sym_vfio_pci_dma_buf_iommufd_map(attach, &pages->dmabuf.phys);
+       rc = sym_vfio_pci_dma_buf_iommufd_map(attach, &pages->dmabuf.phys,
+                                             &pages->dmabuf.is_cpu_ram);
        if (rc)
                goto err_unpin;
 
-- 
2.54.0

Reply via email to