GPU Patch(refine the codes of command kick)分析

2025-12-01 2 0

commit 0328e61d09adebd86db3b30c9b084556890241de
Author: XXX
Date:   Wed Jul 31 12:41:12 2024 +0800

    drm/xdxgpu/gfx: refine the codes of command kick

    By default, ccb buffers are allocated in local video memory, refine
    codes to make sure the ccb write complete before MTS kick

    Signed-off-by: Yajun Li <yajun.li@xdxct.com>
    Change-Id: I58e741f5ecc8b5cd5dbfcbb0c14636d39ba8d75f

diff --git a/xdxgpu/gfx/include/osfunc_common.h b/xdxgpu/gfx/include/osfunc_common.h
index 3a6158cc6..5d110b7e3 100755
--- a/xdxgpu/gfx/include/osfunc_common.h
+++ b/xdxgpu/gfx/include/osfunc_common.h
@@ -229,7 +229,10 @@ size_t StringLCopy(IMG_CHAR *pszDest, const IMG_CHAR *pszSrc, size_t uDataSize);
                if ((c) != 0U) \
                { \
                        (void) memset((a), (b), (c)); \
-                       OSWriteMemoryBarrier(a); \
+                       if ((c) > 4) \
+                               OSWriteMemoryBarrier((IMG_BYTE*)(a) + (c) - 4); \
+                       else \
+                               OSWriteMemoryBarrier((a)); \
                } \
        } while (false)
 /**************************************************************************/ /*!
@@ -249,7 +252,10 @@ size_t StringLCopy(IMG_CHAR *pszDest, const IMG_CHAR *pszSrc, size_t uDataSize);
                if ((c) != 0U) \
                { \
                        (void) memcpy((a), (b), (c)); \
-                       OSWriteMemoryBarrier(a); \
+                       if ((c) > 4) \
+                               OSWriteMemoryBarrier((IMG_BYTE*)(a) + (c) - 4); \
+                       else \
+                               OSWriteMemoryBarrier((a)); \
                } \
        } while (false)
 #endif /* defined(__KERNEL__) */
diff --git a/xdxgpu/gfx/services/server/devices/volcanic/rgxfwutils.c b/xdxgpu/gfx/services/server/devices/volcanic/rgxfwutils.c
index e6f1627ea..1934f034e 100644
--- a/xdxgpu/gfx/services/server/devices/volcanic/rgxfwutils.c
+++ b/xdxgpu/gfx/services/server/devices/volcanic/rgxfwutils.c
@@ -263,6 +263,13 @@ static void _FreeSLC3Fence(PVRSRV_RGXDEV_INFO* psDevInfo)

 static void __MTSScheduleWrite(PVRSRV_RGXDEV_INFO *psDevInfo, IMG_UINT32 ui32Value)
 {
+#ifdef XDXGPU
+       /* Ensure any uncached/WC memory writes are flushed from CPU write buffers
+        * before kicking MTS.
+        */
+       OSWriteMemoryBarrier(NULL);
+#endif
+
        /* This should *NOT* happen. Try to trace what caused this and avoid a NPE
         * with the Write/Read at the foot of the function.
         */
@@ -282,6 +289,7 @@ static void __MTSScheduleWrite(PVRSRV_RGXDEV_INFO *psDevInfo, IMG_UINT32 ui32Val
        (void) OSReadHWReg32(psDevInfo->pvRegsBaseKM, RGX_CR_MTS_SCHEDULE);
 }

+
 /*************************************************************************/ /*!
 @Function       RGXSetupFwAllocation

@@ -3960,7 +3968,6 @@ static PVRSRV_ERROR RGXSendCommandRaw(PVRSRV_RGXDEV_INFO  *psDevInfo,
        OSCachedMemCopy(&pui8KCCB[ui32OldWriteOffset * sizeof(RGXFWIF_KCCB_CMD)],
                        psKCCBCmd, ui32CmdMemCopySize);
 #endif
-
        RGXFwSharedMemCacheOpExec(&pui8KCCB[ui32OldWriteOffset * sizeof(RGXFWIF_KCCB_CMD)], ui32CmdMemCopySize, PVRSRV_CACHE_OP_FLUSH);

        /* If non-NULL pui32CmdKCCBSlot passed-in, return the kCCB slot in which the command was enqueued */
@@ -3986,6 +3993,10 @@ static PVRSRV_ERROR RGXSendCommandRaw(PVRSRV_RGXDEV_INFO  *psDevInfo,
                         __func__, psDevInfo, ui32OldWriteOffset, RGXFWIF_KCCB_RTN_SLOT_NO_RESPONSE, psKCCBCmd->eCmdType));
        }

+#ifdef XDXGPU
+       (void) (*(volatile IMG_UINT32 *)(&pui8KCCB[ui32OldWriteOffset * sizeof(RGXFWIF_KCCB_CMD) + ui32CmdMemCopySize - 4]));
+#endif
+
        /* Memory barrier before KCCB write offset update. */
        OSWriteMemoryBarrier(NULL);

下面是对 该 patch 的全部技术作用与背景 的逐条深入分析,包括:

  • 为什么要改

  • 改了哪些路径

  • 其在 PowerVR 架构(特别是 KCCB / MTS / WC uncached VRAM)的实际影响

  • 为什么要读回最后 4 字节

  • 如何与 DMA、SMMU、FreeList 等问题相关

🔎 总览:这个 patch 的核心目的

确保 KCCB / CCB 等命令 buffer 写入“真的”落到 GPU 可见的内存中,再进行 MTS Kick(即触发 Firmware 读取命令)。 解决 WC/Uncached/VRAM 写入延迟导致 GPU 读取到半写入指令的问题。

在 Imagination GPU 中,KCCB(Kernel CCB)写入后必须保证可见性,否则 FW 会读到断裂或未完成的命令 → TA/3D 卡死、无法上电、HWR 频繁。

patch 的动作本质上:

  • 增强写屏障(memory barrier)

  • 补充了一次读回(last dword readback) 来 flush WC buffer

  • 确保 MTS kick 前保证 command buffer 的内存一致性

  • 对写入地址做特定对齐/最后字取 flush

这些都是为了解决:

  • ✔ VRAM / WC (Write Combine) 回写延迟
  • ✔ Command queue 在 DMA/SMMU 之前未完全可见
  • ✔ GPU firmware 读取“半写入” command 的风险

    📌 第一部分:OSCachedMemSet / OSCachedMemCopy 的修改

patch:

if ((c) > 4)
    OSWriteMemoryBarrier((IMG_BYTE*)(a) + (c) - 4);
else
    OSWriteMemoryBarrier((a));

🧠 原因:WRITECOMBINE 区域的 flush 必须落在最后写入字附近

这是 Intel/ARM CPU 写合并区(WC) 的通用特性:

WC buffer 以 cacheline 为粒度(通常 32/64 字节)

OSWriteMemoryBarrier(addr) 通常实现为 sfence + 指定地址的 load/store

如果 barrier 错误地指向结构开头,而写的关键部分在最后几个字节, CPU 可能不会刷新最后部分

因此:

Barrier 指向最后 4 字节,确保之前的 WC 合并缓冲全部被 flush。

这是非常典型的 GPU MMIO/WC buffer 编程技巧。

📌 第二部分:__MTSScheduleWrite 之前加入 OSWriteMemoryBarrier(NULL)

#ifdef XDXGPU
OSWriteMemoryBarrier(NULL);
#endif

🧠 作用:在执行 MTS Kick 前全局 sfence,强制 flush 所有 WC 写

MTS Kick 是 GPU firmware 的调度触发点:

  • 你写完 KCCB

  • 你 kick MTS

  • GPU FW 立刻尝试从 KCCB 读取命令

如果 WC buffer 未 flush:

  • KCCB 数据不完整 → FW 错误解析 → 卡死/HWR

这个 barrier 的加入确保:

kick 前所有指向 VRAM / Uncached 的 command buffer 都已落地,不会有半写情况。

📌 第三部分:KCCB 写入后加 readback(最关键的 patch)

代码:

#ifdef XDXGPU
(void)(*(volatile IMG_UINT32 *)(
    &pui8KCCB[offset + size - 4]));
#endif

🧠 目的: 强制 CPU 从 WC buffer 提前 flush 写入

这是一条经典手段:

对刚写完的内存进行一次 volatile read,会强制 CPU 将 pending WC buffer 全部回写到 DRAM/VRAM。

CPU 原理:

  • WC buffer 的 flush 时机是松散的(lazy writeback)

  • 但一旦对这个区域执行 Load 操作,需要确保 Load 是正确数据

  • 因此 CPU 必须执行 flush,将合并写全部推入 memory

因此这是一个:

✔ 强制落地(强制同步)的 read barrier ✔ 明显比 OSWriteMemoryBarrier 还要强 ✔ 必须对 buffer 最后的字节读,确保整个写集合 flush ✔ 最常见于 GPU/Linux DMA 驱动

这种技巧你一般会在 AMD/NVIDIA/Intel 的 DMA ring buffer 写入中看到。

📌 第四部分:为什么加在这里?(KCCB → RGXSendCommandRaw)

PowerVR 的 KCCB 是 CPU → Firmware 的命令队列:

流程:

  • CPU 写入 KCCB entry

  • Flush cache / WC

  • Update write offset

  • Kick MTS → Firmware 读取并执行

任何 flush 漏掉会导致:

  • Firmware 读到脏数据

  • Command buffer stride 错乱

  • Firmware 解析失败 → 死循环

  • 可能触发 HWR(你日志里常见)

patch 正是补上 flush 步骤。

📌 第五部分:为什么 patch 能大大降低 TA/3D 卡死、FreeList 错误、SMMU Page Fault?

因为KCCB 是 TA / 3D 的所有命令来源。

如果 KCCB 有问题:

  • TA Kick 信息错误 → TA 卡住

  • Page Table Sync 命令缺失 → SMMU miss

  • FreeList 更新命令丢失 → FreeList slice 不增 → 分配失败

  • 3D CCB Kick 丢失 → 3D 阶段 stall → HWR

这些都是你近期日志出现的问题。

你的 patch 实际是在修复:

根因:VRAM/WC 写入未同步导致的命令队列不一致性。

📌 补充:为何 VRAM / Uncached 区域更容易出问题?

因为:

  • GPU 的 command buffer 不在 normal cached 内存中(通常 WC or Uncached)

  • CPU 写入这类区域不会立即真正写入内存,而是在缓冲区堆积

  • MTS Kick 立即触发 GPU FW 读取,但写入可能还没落地

所以你的命令可能变成:

  • 前半写好了

  • 后半还在 WC buffer

  • GPU FW 读取时 data torn → 卡死

🧩 最终总结:patch 的作用(一句话)

强制 CPU 完整刷新 KCCB/CCB 等命令 buffer 到 VRAM,使 GPU Firmware 不会读取到半写或未完成数据,确保命令一致性,避免卡死、HWR、FreeList 错误和 TA/3D 异常。

相关文章

发布评论