On 2026-09-22 09:12, David Yat Sin wrote:
amdgpu_vm_flush_compute_tlb() walks the XCCs of the compute partition one
at a time, and amdgpu_gmc_flush_gpu_tlb_pasid() submits the invalidation
to one XCC's KIQ and then busy-waits for its fence before the caller can
move on to the next. On a partition that owns every XCC of the device
that is num_xcc round trips back to back.

The XCCs do not depend on each other here. Each has its own KIQ ring,
ring lock and fence sequence, the page tables are already updated before
any invalidation is issued, and no invalidation needs another XCC to have
finished first.

Split the submit out of amdgpu_gmc_flush_gpu_tlb_pasid() and add
amdgpu_gmc_flush_gpu_tlb_pasid_xccs(), which queues the invalidation on
every XCC in the mask before collecting any of the fences, so the round
trips overlap. Every XCC is flushed even when one of them fails, and the
first error is what gets returned. The single XCC entry point keeps its
behaviour, so only the compute TLB flush path changes.

Signed-off-by: David Yat Sin <[email protected]>

Reviewed-by: Felix Kuehling <[email protected]>


---
  drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 226 +++++++++++++++++-------
  drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h |   3 +
  drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c  |  10 +-
  3 files changed, 172 insertions(+), 67 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
index 7c0554506334..6a9bcd28b43c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
@@ -780,15 +780,108 @@ void amdgpu_gmc_flush_gpu_tlb(struct amdgpu_device 
*adev, uint32_t vmid,
        dev_err(adev->dev, "Error flushing GPU TLB using the SDMA (%d)!\n", r);
  }
+static void amdgpu_gmc_flush_pasid_regs(struct amdgpu_device *adev, uint16_t pasid,
+                                       uint32_t flush_type, bool all_hub,
+                                       uint32_t inst)
+{
+       if (!adev->gmc.gmc_funcs->flush_gpu_tlb_pasid)
+               return;
+
+       if (adev->gmc.flush_tlb_needs_extra_type_2)
+               adev->gmc.gmc_funcs->flush_gpu_tlb_pasid(adev, pasid, 2, 
all_hub,
+                                                        inst);
+
+       if (adev->gmc.flush_tlb_needs_extra_type_0 && flush_type == 2)
+               adev->gmc.gmc_funcs->flush_gpu_tlb_pasid(adev, pasid, 0, 
all_hub,
+                                                        inst);
+
+       adev->gmc.gmc_funcs->flush_gpu_tlb_pasid(adev, pasid, flush_type, 
all_hub,
+                                                inst);
+}
+
+/*
+ * Queue the invalidation on one XCC and return its fence without waiting, so
+ * that a caller flushing several XCCs can have them in flight together.  The
+ * KIQ ring, its lock and its fence sequence are per XCC, so the submissions do
+ * not interfere with each other.
+ */
+static int amdgpu_gmc_flush_pasid_kiq_submit(struct amdgpu_device *adev,
+                                            uint16_t pasid, uint32_t 
flush_type,
+                                            bool all_hub, uint32_t inst,
+                                            uint32_t *seq)
+{
+       struct amdgpu_kiq *kiq = &adev->gfx.kiq[inst];
+       struct amdgpu_ring *ring = &kiq->ring;
+       unsigned int ndw;
+       int r;
+
+       /* 2 dwords flush + 8 dwords fence */
+       ndw = kiq->pmf->invalidate_tlbs_size + 8;
+
+       if (adev->gmc.flush_tlb_needs_extra_type_2)
+               ndw += kiq->pmf->invalidate_tlbs_size;
+
+       if (adev->gmc.flush_tlb_needs_extra_type_0)
+               ndw += kiq->pmf->invalidate_tlbs_size;
+
+       spin_lock(&kiq->ring_lock);
+       r = amdgpu_ring_alloc(ring, ndw);
+       if (r) {
+               spin_unlock(&kiq->ring_lock);
+               return r;
+       }
+
+       if (adev->gmc.flush_tlb_needs_extra_type_2)
+               kiq->pmf->kiq_invalidate_tlbs(ring, pasid, 2, all_hub);
+
+       if (flush_type == 2 && adev->gmc.flush_tlb_needs_extra_type_0)
+               kiq->pmf->kiq_invalidate_tlbs(ring, pasid, 0, all_hub);
+
+       kiq->pmf->kiq_invalidate_tlbs(ring, pasid, flush_type, all_hub);
+       r = amdgpu_fence_emit_polling(ring, seq, MAX_KIQ_REG_WAIT);
+       if (r) {
+               amdgpu_ring_undo(ring);
+               spin_unlock(&kiq->ring_lock);
+               return r;
+       }
+
+       amdgpu_ring_commit(ring);
+       spin_unlock(&kiq->ring_lock);
+
+       return 0;
+}
+
+static int amdgpu_gmc_flush_pasid_kiq_wait(struct amdgpu_device *adev,
+                                          uint32_t inst, uint32_t seq)
+{
+       struct amdgpu_ring *ring = &adev->gfx.kiq[inst].ring;
+       int cnt = 0;
+       signed long r;
+
+       r = amdgpu_fence_wait_polling(ring, seq, MAX_KIQ_REG_WAIT);
+
+       might_sleep();
+       while (r < 1 && cnt++ < MAX_KIQ_REG_TRY &&
+              !amdgpu_reset_pending(adev->reset_domain)) {
+               msleep(MAX_KIQ_REG_BAILOUT_INTERVAL);
+               r = amdgpu_fence_wait_polling(ring, seq, MAX_KIQ_REG_WAIT);
+       }
+
+       if (cnt > MAX_KIQ_REG_TRY) {
+               dev_err(adev->dev, "timeout waiting for kiq fence\n");
+               return -ETIME;
+       }
+
+       return 0;
+}
+
  int amdgpu_gmc_flush_gpu_tlb_pasid(struct amdgpu_device *adev, uint16_t pasid,
                                   uint32_t flush_type, bool all_hub,
                                   uint32_t inst)
  {
        struct amdgpu_ring *ring = &adev->gfx.kiq[inst].ring;
-       struct amdgpu_kiq *kiq = &adev->gfx.kiq[inst];
-       unsigned int ndw;
-       int r, cnt = 0;
        uint32_t seq;
+       int r;
/*
         * A GPU reset should flush all TLBs anyway, so no need to do
@@ -798,76 +891,91 @@ int amdgpu_gmc_flush_gpu_tlb_pasid(struct amdgpu_device 
*adev, uint16_t pasid,
                return 0;
if (!adev->gmc.flush_pasid_uses_kiq || !ring->sched.ready) {
+               amdgpu_gmc_flush_pasid_regs(adev, pasid, flush_type, all_hub, 
inst);
+               r = 0;
+               goto error_unlock_reset;
+       }
- if (!adev->gmc.gmc_funcs->flush_gpu_tlb_pasid) {
-                       r = 0;
-                       goto error_unlock_reset;
-               }
-
-               if (adev->gmc.flush_tlb_needs_extra_type_2)
-                       adev->gmc.gmc_funcs->flush_gpu_tlb_pasid(adev, pasid,
-                                                                2, all_hub,
-                                                                inst);
+       r = amdgpu_gmc_flush_pasid_kiq_submit(adev, pasid, flush_type, all_hub,
+                                             inst, &seq);
+       if (r)
+               goto error_unlock_reset;
- if (adev->gmc.flush_tlb_needs_extra_type_0 && flush_type == 2)
-                       adev->gmc.gmc_funcs->flush_gpu_tlb_pasid(adev, pasid,
-                                                                0, all_hub,
-                                                                inst);
+       r = amdgpu_gmc_flush_pasid_kiq_wait(adev, inst, seq);
- adev->gmc.gmc_funcs->flush_gpu_tlb_pasid(adev, pasid,
-                                                        flush_type, all_hub,
-                                                        inst);
-               r = 0;
-       } else {
-               /* 2 dwords flush + 8 dwords fence */
-               ndw = kiq->pmf->invalidate_tlbs_size + 8;
+error_unlock_reset:
+       up_read(&adev->reset_domain->sem);
+       return r;
+}
- if (adev->gmc.flush_tlb_needs_extra_type_2)
-                       ndw += kiq->pmf->invalidate_tlbs_size;
+/**
+ * amdgpu_gmc_flush_gpu_tlb_pasid_xccs - flush a pasid on several XCCs at once
+ *
+ * @adev: amdgpu_device pointer
+ * @pasid: pasid to be flushed
+ * @flush_type: the flush type
+ * @all_hub: flush all hubs
+ * @xcc_mask: mask of the XCCs to flush
+ *
+ * Submits the invalidation to every XCC in @xcc_mask before waiting for any of
+ * them, so the round trips overlap instead of running back to back.  Flushing
+ * one XCC at a time costs num_xcc times the latency of a single one, which on
+ * a partition holding every XCC of the device is most of the cost of a compute
+ * TLB flush.
+ *
+ * The XCCs are independent of each other here: the page tables are already
+ * updated before any invalidation is issued, and nothing in an invalidation
+ * depends on another XCC having completed its own.
+ *
+ * Returns:
+ * 0 for success, the first error otherwise.  Every XCC is flushed even if one
+ * of them fails.
+ */
+int amdgpu_gmc_flush_gpu_tlb_pasid_xccs(struct amdgpu_device *adev, uint16_t 
pasid,
+                                       uint32_t flush_type, bool all_hub,
+                                       uint32_t xcc_mask)
+{
+       uint32_t seq[AMDGPU_MAX_GC_INSTANCES];
+       unsigned long pending = 0;
+       int xcc, r = 0, tmp;
- if (adev->gmc.flush_tlb_needs_extra_type_0)
-                       ndw += kiq->pmf->invalidate_tlbs_size;
+       /*
+        * A GPU reset should flush all TLBs anyway, so no need to do
+        * this while one is ongoing.
+        */
+       if (!down_read_trylock(&adev->reset_domain->sem))
+               return 0;
- spin_lock(&adev->gfx.kiq[inst].ring_lock);
-               r = amdgpu_ring_alloc(ring, ndw);
-               if (r) {
-                       spin_unlock(&adev->gfx.kiq[inst].ring_lock);
-                       goto error_unlock_reset;
-               }
-               if (adev->gmc.flush_tlb_needs_extra_type_2)
-                       kiq->pmf->kiq_invalidate_tlbs(ring, pasid, 2, all_hub);
+       for_each_inst(xcc, xcc_mask) {
+               struct amdgpu_ring *ring;
- if (flush_type == 2 && adev->gmc.flush_tlb_needs_extra_type_0)
-                       kiq->pmf->kiq_invalidate_tlbs(ring, pasid, 0, all_hub);
+               if (xcc >= AMDGPU_MAX_GC_INSTANCES)
+                       break;
- kiq->pmf->kiq_invalidate_tlbs(ring, pasid, flush_type, all_hub);
-               r = amdgpu_fence_emit_polling(ring, &seq, MAX_KIQ_REG_WAIT);
-               if (r) {
-                       amdgpu_ring_undo(ring);
-                       spin_unlock(&adev->gfx.kiq[inst].ring_lock);
-                       goto error_unlock_reset;
+               ring = &adev->gfx.kiq[xcc].ring;
+               if (!adev->gmc.flush_pasid_uses_kiq || !ring->sched.ready) {
+                       amdgpu_gmc_flush_pasid_regs(adev, pasid, flush_type,
+                                                   all_hub, xcc);
+                       continue;
                }
- amdgpu_ring_commit(ring);
-               spin_unlock(&adev->gfx.kiq[inst].ring_lock);
-
-               r = amdgpu_fence_wait_polling(ring, seq, MAX_KIQ_REG_WAIT);
-
-               might_sleep();
-               while (r < 1 && cnt++ < MAX_KIQ_REG_TRY &&
-                      !amdgpu_reset_pending(adev->reset_domain)) {
-                       msleep(MAX_KIQ_REG_BAILOUT_INTERVAL);
-                       r = amdgpu_fence_wait_polling(ring, seq, 
MAX_KIQ_REG_WAIT);
+               tmp = amdgpu_gmc_flush_pasid_kiq_submit(adev, pasid, flush_type,
+                                                       all_hub, xcc, 
&seq[xcc]);
+               if (tmp) {
+                       if (!r)
+                               r = tmp;
+                       continue;
                }
- if (cnt > MAX_KIQ_REG_TRY) {
-                       dev_err(adev->dev, "timeout waiting for kiq fence\n");
-                       r = -ETIME;
-               } else
-                       r = 0;
+               __set_bit(xcc, &pending);
+       }
+
+       for_each_set_bit(xcc, &pending, AMDGPU_MAX_GC_INSTANCES) {
+               tmp = amdgpu_gmc_flush_pasid_kiq_wait(adev, xcc, seq[xcc]);
+               if (tmp && !r)
+                       r = tmp;
        }
-error_unlock_reset:
        up_read(&adev->reset_domain->sem);
        return r;
  }
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h
index cffd5ca3fc66..5ea237ce133b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h
@@ -447,6 +447,9 @@ void amdgpu_gmc_flush_gpu_tlb(struct amdgpu_device *adev, 
uint32_t vmid,
  int amdgpu_gmc_flush_gpu_tlb_pasid(struct amdgpu_device *adev, uint16_t pasid,
                                   uint32_t flush_type, bool all_hub,
                                   uint32_t inst);
+int amdgpu_gmc_flush_gpu_tlb_pasid_xccs(struct amdgpu_device *adev, uint16_t 
pasid,
+                                       uint32_t flush_type, bool all_hub,
+                                       uint32_t xcc_mask);
  void amdgpu_gmc_fw_reg_write_reg_wait(struct amdgpu_device *adev,
                                      uint32_t reg0, uint32_t reg1,
                                      uint32_t ref, uint32_t mask,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index 47cde5459469..aae92ca87914 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -1724,7 +1724,6 @@ int amdgpu_vm_flush_compute_tlb(struct amdgpu_device 
*adev,
  {
        uint64_t tlb_seq = amdgpu_vm_tlb_seq(vm);
        bool all_hub = false;
-       int xcc = 0, r = 0;
WARN_ON_ONCE(!vm->is_compute_context); @@ -1740,13 +1739,8 @@ int amdgpu_vm_flush_compute_tlb(struct amdgpu_device *adev,
            adev->family == AMDGPU_FAMILY_RV)
                all_hub = true;
- for_each_inst(xcc, xcc_mask) {
-               r = amdgpu_gmc_flush_gpu_tlb_pasid(adev, vm->pasid, flush_type,
-                                                  all_hub, xcc);
-               if (r)
-                       break;
-       }
-       return r;
+       return amdgpu_gmc_flush_gpu_tlb_pasid_xccs(adev, vm->pasid, flush_type,
+                                                  all_hub, xcc_mask);
  }
/**

Reply via email to