PowerVR Rogue 架构 DDK 的 FreeList

2025-11-27 4 0

✅ FreeList 是什么?为什么存在?

在 PowerVR GPU 中,所有的渲染命令会被最终“打包”到一个叫 Parameter Buffer(PB) 的区域,用于 Tiling 阶段对场景进行 binning。 但是 PB 需要反复分配小块空间,不能每次 malloc/free,因此 PowerVR 设计了一个高效的 FreeList 对应的 CCB/Kick 流程图 来管理 PB 内存。

FreeList 的特点:

特点 说明
管理的是 PB 内存块(Parameter Buffer Blocks) PB 被分成固定大小的 Block(通常 256B 或 512B)
使用 链表结构(FreeList)跟踪可用 Block 分配就是 pop,释放就是 push
Firmware 和 Host 共享 FreeList Host 初始化,FW 动态分配和回收
场景级别(per-TA/3D workload)的关键资源 如果耗尽 → GPU hang / OOM / Kick 重试
提供 自动 grow / shrink 机制 Host 动态增加/减少 block pool

✅ FreeList 的用途:Parameter Buffer 的 Block 分配器

Parameter Buffer 中记录:

  • Geometry lists
  • ISPs state
  • Varying + attribute stream 数据
  • Tile lists
  • Tiling commands

每条渲染指令都会写入 PB 中,而 PB 又被 分成多个 block —— FreeList 管理的就是这些 block。

+-------------------------------+
|   Parameter Buffer (PB)       |
|  +--------+--------+--------+ |
|  | Block0 | Block1 | Block2 | |
|  +--------+--------+--------+ |
|        ↑  ↑  ↑                |
|        free blocks            |
+-------------------------------+
            |
            v
      FreeList 管理

GPU FW 会从 FreeList 中请求:

Block = FreeList_AllocBlock()

工作完成后:

FreeList_FreeBlock(Block)

✅ FreeList 的核心数据结构(Host 侧)

在 DDK(drivers/gpu/drm/imagination/)中对应:

struct PVR_FW_FREE_LIST
{
    u32 ui32ListSize;         // 当前 block 数量
    u32 ui32MaxListSize;      // 最大 block 数量
    u32 ui32GrowSize;         // 自动扩容块数
    u32 ui32GrowThreshold;    // 阈值
    u32 ui32CurrentFree;      // 当前剩余 block 数
    ...
    phys_addr_t paBase;       // PB 物理地址基址
};

FreeList 实际上是一段连续的 PB pool + 一个可用块链表。

✅ FreeList 分配机制(Firmware 视角)

★ 分配过程(Alloc)

  1. GPU FW 请求 block

  2. FreeList 检查链表是否为空

  3. 若有 → pop 一个 block

  4. 若没有 → 通知 Host 进行 Grow

  5. 若 Host 无法扩容 → Kick 失败 → Fence error → GPU hang

伪代码:

block = freelist->head;
if (!block) {
    FWSignalGrow();
    return ERROR_NO_BLOCK;
}
freelist->head = block->next;
return block;

✅ FreeList 回收机制(Free)

Pipeline(TA/3D/Compute)完成一个段落后,FW 将使用完毕的 block 通过 MTS(Memory Transfer Service)通知 Host 回收:

freelist->head = freed_block;

因为 PB 是环形结构,FW 会在 fence 到达后批量回收。

✅ FreeList Grow / Shrink 机制(关键)

PowerVR PB 是可自动扩容的(与其他 GPU 不同)。

Grow 条件:

当 FW 分配时可用量 < GrowThreshold:

current_free < grow_threshold → Host 分配“GrowSize”个 block

Host 执行:

  • 分配新的物理 memory

  • 添加到 FreeList

  • 更新 FW 表

Shrink 条件:

在场景切换 OR GPU 空闲时:

current_free > high_water_mark → Host 回收部分 block

这样避免长期占用大量 PB 内存。

✅ FreeList Overflow / Underflow 的常见问题

✔ underflow(最常见)

症状:

No free PB blocks → TA stalled → Timeout → GPU RESET

原因:

  • 批次太多 geometry

  • ParameterBufferSize 过小

  • FreeList Grow 阈值配置太低

  • 驱动缺少 Grow 回调

✔ overflow

实际上 FreeList 不会 overflow(因为 block 数是固定池),但可能:

  • Host shrink 太多

  • 导致 FW 缺块

  • again → hang

✅ DDK 中 FreeList 主要函数

Host 侧关键代码(DDK):

pvr_freelist_create()
pvr_freelist_grow()
pvr_freelist_shrink()
pvr_freelist_destroy()
pvr_pb_allocate()
pvr_pb_free()

Firmware 侧(Rogue FW 中):

FreeListAlloc()
FreeListFree()
FreeListGrowRequest()

✅ FreeList 与 TA/3D 的关系(非常重要)

一个 FreeList 可以被多个 TA/3D Job 共享:

TA1 ───┐
TA2 ───┼──► 同一个 FreeList
3D1 ───┘

因此:

  • 大量并发 Kick → PB 需求激增

  • Grow 失败 → GPU 卡死

✅ 【总览流程图】

CCB Job → Firmware → FreeList 分配 → TA/3D 执行 → FreeList 回收 这是内部 GPU pipeline 的真实流程,不是概念化图。同时会标注:

H- ost 驱动(内核)执行点

  • Firmware(Microkernel / usc FW)执行点

  • TA / 3D pipeline

  • FreeList Grow / Free / Underflow

  • CCB 命令产生的 PB Block 使用点

┌────────────────────────────────────────────────────────────────────┐
│                       Host (Kernel Driver)                          │
└────────────────────────────────────────────────────────────────────┘

          [1] 用户层提交绘制任务
                       │
                       ▼
          [2] pvr_submit_job() 生成 TA/3D CCB 命令
                       │
                       ▼
          [3] 将命令写入 CCB(Client Control Buffer)
                       │
                       ▼
          [4] 写 Doorbell → 通知 Firmware 有新的 Kick
                       │
                       ▼
───────────────────────────────────────────────────────────────────────────
                Firmware (Rogue Microkernel)
───────────────────────────────────────────────────────────────────────────
                       ▼
          [5] FW 读取 CCB 命令
                       │
                       ▼
          [6] 解析 TA/3D Kick 命令
                       │
                       ▼
          [7] 准备参数缓冲区(Parameter Buffer)
                       │
                       ▼
          [8] FreeList_AllocBlock() ← 请求 PB Block
               │
               ├─ 有可用 block → 分配
               │
               └─ 无可用 block → FreeListGrowRequest → Host
                       │
                       ▼
───────────────────────────────────────────────────────────────────────────
                      Host FreeList Grow 处理
───────────────────────────────────────────────────────────────────────────
                       │
            [9] pvr_freelist_grow() 分配更多 block
                       │
                       ▼
            [10] 更新 FW Shared FreeList Table
                       │
                       ▼
───────────────────────────────────────────────────────────────────────────
                         回到 Firmware
───────────────────────────────────────────────────────────────────────────
                       │
                       ▼
          [11] FW 继续消耗 PB block,执行 Tiling
                       │
                       ▼
          [12] TA 结束 → 释放 TA Blocks 到 FreeList(batched)
                       │
                       ▼
          [13] 启动 3D → 再次使用 FreeList 分配 block
                       │
                       ▼
          [14] 3D 完成 → FreeList_FreeBlock()
                       │
                       ▼
          [15] 如果达到 shrink 条件 → shrink request → Host
                       │
                       ▼
───────────────────────────────────────────────────────────────────────────
                     Host(FreeList Shrink)
───────────────────────────────────────────────────────────────────────────
                       │
                       ▼
         [16] 回收多余 PB block → 更新 FW FreeList Table
                       │
                       ▼
───────────────────────────────────────────────────────────────────────────
                     Firmware → Signal Fence
───────────────────────────────────────────────────────────────────────────
                       │
                       ▼
         [17] FW 发送 Fence → Host 完成 job
graph TD

    CPU[CPU Driver] --> CCB[Write TA Kick to CCB]
    CCB --> FW1[FW Detects CCB update]


    FW1 --> FW2[FW Parses TA Kick]
    FW2 --> TA_Start[FW Schedules TA Pass]


    TA_Start --> FL_Req["TA Requests PB Pages<br/>FreeList Alloc"]

    FL_Req -->|FreeList OK| PMR_Alloc[PMR Alloc Physical Pages]
    PMR_Alloc --> SMMU_Map["SMMU Maps Pages<br/>Update PageTables"]
    SMMU_Map --> TLB_Update["TLB Updates/Invalidate"]
    TLB_Update --> TA_Run["TA Executes Pass<br/>Write PB"]

    FL_Req -->|"FreeList Empty / Fail"| TA_OOM["TA OOM / Kick Failed"]
    TA_OOM --> FW_OOM[FW Returns Error]
    FW_OOM --> CPU_OOM[CPU Receives TA_oom]


    TA_Run --> TA_Done[TA Pass Complete]
    TA_Done --> FW3[FW Schedule 3D Pass]


    FW3 --> D3_Run["3D Reads PB via SMMU/TLB<br/>and Executes"]
    D3_Run --> D3_Done[3D Pass Complete]


    D3_Done --> FL_Reclaim[FW Marks Pages Reclaimable]
    FL_Reclaim --> PMR_Ref[PMR Refcount-- per page]

    PMR_Ref -->|All refs done| FL_Free[FreeList Returns Pages]
    PMR_Ref -->|Still referenced| FL_Delay[Delayed Free]


    FL_Free --> FW_Comp[FW Completes Kick]
    FL_Delay --> FW_Comp

    FW_Comp --> CPU_Done["CPU Receives Fence / Complete Signal"]

🔥 深度细化:每一步的实际驱动函数调用

🔷 【Host → CCB → Kick】

pvr_submit_job()
    ↓
pvr_ccb_send_cmd()
    ↓
ccb_write_packet()
    ↓
pvr_sync_kick() / pvr_3dctx_kick()
    ↓
doorbell_write() → 通知 Firmware

🔷 【Firmware → FreeList block 分配】

ProcessCCB()
    ↓
ProcessTAKick() / Process3DKick()
    ↓
FW_ParameterManager_AllocPBBlock()
    ↓
FreeList_AllocBlock()
    ↓
    ├─ block available → return
    └─ no free block → FreeListGrowRequest → Host

🔷 【Host → Grow FreeList】

pvr_freelist_grow()
    ↓
Alloc new PB chunks
    ↓
Add to freelist->free_stack
    ↓
Update FW freelist table via FWCCB

🔷 【3D / TA 使用 PB】

FW writes parameter buffer
    ↓
binner / uTile Scheduler consumes PB blocks

🔷 【回收 block】

FW_ParameterManager_FreePBBlock()
FW_FreeListReturnBlock()

回收不会立即 push,而是通过 “freeBlockList” 批处理,由ProcessFreeListReturns()完成。

✅ 结合硬件架构的最终总结

FreeList 解决的问题:

问题 FreeList 方案
PB 是固定 block,需要反复分配 提供可复用的 Block 链表
GPU 固件需快速 alloc/free 固件本地链表结构
场景复杂度会变化 支持自动 grow/shrink
多 Job 并发 FreeList 支持共享
Block 耗尽导致 GPU 锁死 Grow + 回调机制

FreeList 是 PowerVR 参数缓冲区的核心生命线,一旦耗尽或创建错误,整个 GPU pipeline 会立即停滞。

FreeList Underflow 时真正发生的事情(调试 GPU hang 必看)

FW AllocBlock → freelist->freecount &#61;&#61; 0 → underflow
    ↓
Send Grow request to Host
    ↓
Host busy → grow pending
    ↓
FW cannot continue PB writes
    ↓
TA stall → TA timeout
    ↓
GPU Watchdog triggers EoC (End of Context)
    ↓
Device Reset

📌 FAQ:常见 FreeList 问题与 GPU 卡死场景

场景 结果
Grow 请求 Host 未响应 TA/3D job hang
PB block 物理映射 SMMU 出错 FW 无法写入 PB → Fence 不到达
FreeList 被多个 context 争抢 快速耗尽
grow_threshold 太低 高频 underflow

相关文章

发布评论