From: Andrew Martin <[email protected]>

Applies to gfx10 (RDNA2) and gfx11 (RDNA3).

The SDMA firmware's saved mid-command state for a preempted COPY_LINEAR
(sdmax_rlcx_midcmd_data0 = source byte offset) has been observed to
overshoot the packet's byte count on gfx10.3 (Navi21, XNACK off) after
rapid quiesce/resume cycles driven by userptr/SVM eviction. On
MAP_QUEUES the engine resumes the copy at src+offset past the buffer:
an unmapped VA turns into a UTCL2 'Page not present' fault, a mapped
one into silent data corruption.

The silent corruption variant produces no dmesg output and is only
detectable through application-level data integrity checks (checksums,
validation). This makes diagnosis extremely difficult as there is no
kernel-level indication of the failure.

Zero the mid-command save area before remapping so the pending packet
re-executes from its packet boundary (idempotent for COPY/GCR/FENCE).
Workaround for field crashes of ROCm workloads on RDNA2/RDNA3.

Signed-off-by: Andrew Martin <[email protected]>
Assisted-by: Claude:Sonnet 4-5
---
 .../drm/amd/amdkfd/kfd_device_queue_manager.c | 37 ++++++++++++++++++-
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c 
b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
index 51ee9c39104b..1507d371f741 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -40,6 +40,8 @@
 #include "amdgpu_ring.h"
 #include "amdgpu_mes.h"
 #include "kfd_debug.h"
+#include "v10_structs.h"
+#include "v11_structs.h"
 
 /* Size of the per-pipe EOP queue */
 #define CIK_HPD_EOP_BYTES_LOG2 11
@@ -1447,6 +1449,37 @@ static int restore_process_queues_nocpsch(struct 
device_queue_manager *dqm,
        return ret;
 }
 
+/*
+ * Zero SDMA mid-command state before MAP_QUEUES. Firmware bug: preempted
+ * COPY_LINEAR can resume at corrupted offset past buffer (silent corruption
+ * or VM fault). Re-executing from packet start is idempotent and safe.
+ */
+static void clamp_sdma_midcmd_state(struct device_queue_manager *dqm,
+                                   struct qcm_process_device *qpd)
+{
+       struct queue *q;
+       uint32_t gc_ver = KFD_GC_VERSION(dqm->dev);
+
+       if (gc_ver < IP_VERSION(10, 1, 1) || gc_ver >= IP_VERSION(12, 0, 0))
+               return;
+
+       list_for_each_entry(q, &qpd->queues_list, list) {
+               if ((q->properties.type != KFD_QUEUE_TYPE_SDMA &&
+                    q->properties.type != KFD_QUEUE_TYPE_SDMA_XGMI) || !q->mqd)
+                       continue;
+
+               if (gc_ver < IP_VERSION(11, 0, 0)) {
+                       struct v10_sdma_mqd *m = q->mqd;
+
+                       memset(&m->sdmax_rlcx_midcmd_data0, 0, 10 * 
sizeof(uint32_t));
+               } else {
+                       struct v11_sdma_mqd *m = q->mqd;
+
+                       memset(&m->sdmax_rlcx_midcmd_data0, 0, 12 * 
sizeof(uint32_t));
+               }
+       }
+}
+
 static int restore_process_queues_cpsch(struct device_queue_manager *dqm,
                                        struct qcm_process_device *qpd)
 {
@@ -1498,9 +1531,11 @@ static int restore_process_queues_cpsch(struct 
device_queue_manager *dqm,
                        }
                }
        }
-       if (!dqm->dev->kfd->shared_resources.enable_mes)
+       if (!dqm->dev->kfd->shared_resources.enable_mes) {
+               clamp_sdma_midcmd_state(dqm, qpd);
                retval = execute_queues_cpsch(dqm,
                                              
KFD_UNMAP_QUEUES_FILTER_DYNAMIC_QUEUES, 0, USE_DEFAULT_GRACE_PERIOD);
+       }
        eviction_duration = get_jiffies_64() - pdd->last_evict_timestamp;
        atomic64_add(eviction_duration, &pdd->evict_duration_counter);
 vm_not_acquired:
-- 
2.43.0

Reply via email to