Mimic the dma-buf method using dma_[map|unmap]_resource to map
for pcie-p2p dma.

There's an ongoing area of work upstream to sort out how this best
should be done. One method proposed is to add an additional
pci_p2p_dma_pagemap aliasing the device_private pagemap and use
the corresponding pci_p2p_dma_pagemap page as input for
dma_map_page(). However, that would incur double the amount of
memory and latency to set up the drm_pagemap and given the huge
amount of memory present on modern GPUs, that would really not work.
Hence the simple approach used in this patch.

v2:
- Simplify xe_page_to_pcie(). (Matt Brost)

Signed-off-by: Thomas Hellström <[email protected]>
---
 drivers/gpu/drm/xe/xe_svm.c | 34 +++++++++++++++++++++++++++++++---
 drivers/gpu/drm/xe/xe_svm.h |  1 +
 2 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c
index c0b17b548a00..86d7b0882b60 100644
--- a/drivers/gpu/drm/xe/xe_svm.c
+++ b/drivers/gpu/drm/xe/xe_svm.c
@@ -3,6 +3,8 @@
  * Copyright © 2024 Intel Corporation
  */
 
+#include <linux/pci-p2pdma.h>
+
 #include <drm/drm_drv.h>
 #include <drm/drm_managed.h>
 #include <drm/drm_pagemap.h>
@@ -441,6 +443,14 @@ static u64 xe_page_to_dpa(struct page *page)
        return dpa;
 }
 
+static u64 xe_page_to_pcie(struct page *page)
+{
+       struct xe_pagemap *xpagemap = xe_page_to_pagemap(page);
+       struct xe_vram_region *vr = xe_pagemap_to_vr(xpagemap);
+
+       return xe_page_to_dpa(page) - vr->dpa_base + vr->io_start;
+}
+
 enum xe_svm_copy_dir {
        XE_SVM_COPY_TO_VRAM,
        XE_SVM_COPY_TO_SRAM,
@@ -792,7 +802,10 @@ static bool xe_has_interconnect(struct drm_pagemap_peer 
*peer1,
        struct device *dev1 = xe_peer_to_dev(peer1);
        struct device *dev2 = xe_peer_to_dev(peer2);
 
-       return dev1 == dev2;
+       if (dev1 == dev2)
+               return true;
+
+       return pci_p2pdma_distance(to_pci_dev(dev1), dev2, true) >= 0;
 }
 
 static DRM_PAGEMAP_OWNER_LIST_DEFINE(xe_owner_list);
@@ -1552,13 +1565,27 @@ xe_drm_pagemap_device_map(struct drm_pagemap *dpagemap,
                addr = xe_page_to_dpa(page);
                prot = XE_INTERCONNECT_VRAM;
        } else {
-               addr = DMA_MAPPING_ERROR;
-               prot = 0;
+               addr = dma_map_resource(dev,
+                                       xe_page_to_pcie(page),
+                                       PAGE_SIZE << order, dir,
+                                       DMA_ATTR_SKIP_CPU_SYNC);
+               prot = XE_INTERCONNECT_P2P;
        }
 
        return drm_pagemap_addr_encode(addr, prot, order, dir);
 }
 
+static void xe_drm_pagemap_device_unmap(struct drm_pagemap *dpagemap,
+                                       struct device *dev,
+                                       struct drm_pagemap_addr addr)
+{
+       if (addr.proto != XE_INTERCONNECT_P2P)
+               return;
+
+       dma_unmap_resource(dev, addr.addr, PAGE_SIZE << addr.order,
+                          addr.dir, DMA_ATTR_SKIP_CPU_SYNC);
+}
+
 static void xe_pagemap_destroy_work(struct work_struct *work)
 {
        struct xe_pagemap *xpagemap = container_of(work, typeof(*xpagemap), 
destroy_work);
@@ -1595,6 +1622,7 @@ static void xe_pagemap_destroy(struct drm_pagemap 
*dpagemap, bool from_atomic_or
 
 static const struct drm_pagemap_ops xe_drm_pagemap_ops = {
        .device_map = xe_drm_pagemap_device_map,
+       .device_unmap = xe_drm_pagemap_device_unmap,
        .populate_mm = xe_drm_pagemap_populate_mm,
        .destroy = xe_pagemap_destroy,
 };
diff --git a/drivers/gpu/drm/xe/xe_svm.h b/drivers/gpu/drm/xe/xe_svm.h
index 60eae01a4220..64971c9b2a1a 100644
--- a/drivers/gpu/drm/xe/xe_svm.h
+++ b/drivers/gpu/drm/xe/xe_svm.h
@@ -13,6 +13,7 @@
 #include <drm/drm_pagemap_util.h>
 
 #define XE_INTERCONNECT_VRAM DRM_INTERCONNECT_DRIVER
+#define XE_INTERCONNECT_P2P (XE_INTERCONNECT_VRAM + 1)
 
 struct drm_device;
 struct drm_file;
-- 
2.51.1

Reply via email to