8.1 勾勒了 amdgpu_cs_ioctl 的九步全景,8.2 讲解了前端的 chunk 解析与作业构建(第 ①—③ 阶段)。本节承接其后,聚焦第 ④ 阶段 amdgpu_cs_parser_bos——整条路径中最关键的节点之一,剖析内核如何在一致的加锁保护下,将本次提交涉及的所有 BO 拉回到 GPU 可访问的内存域,并为后续的页表更新与作业入队奠定基础。
一次命令提交要正确执行,前提是其引用的每一个 BO 都满足两个条件:其一,被稳定地锁定,使得从校验到入队的整个窗口内没有其他线程能够驱逐或搬移它;其二,处于合法的内存域(VRAM 或 GTT),即已完成驻留(resident)并具备有效的 GPU 映射。amdgpu_cs_parser_bos 正是同时达成这两点的环节,其内部由三种机制协同完成——drm_exec 负责加锁、ttm_bo_validate 负责回迁、dma_resv 则作为二者共同操作的锁与栅栏容器。
1. drm_exec:本阶段如何锁定全部 BO
命令提交需要同时锁定的 BO 往往数以百计,且不同提交所引用的集合彼此交叠。若以不一致的顺序逐个加锁,极易形成 ABBA 死锁。DRM 将「无死锁地一次性锁定一组 dma_resv」这一通用问题抽象为 drm_exec 框架——其底层基于 ww_mutex(wait/wound 互斥锁,DRM 的 reservation_ww_class 采用 Wait-Die 算法),把「预留—竞争—回退—重试」的完整逻辑封装为一组宏。该框架的原理与实现属于同步机制范畴,本专栏在 6.3.3 drm_exec — 多 BO 无死锁加锁框架 中已专文详述;本节仅从提交流程的应用视角说明它如何被使用。
在展开加锁前,parser_bos 首先取得一把贯穿全函数的外层锁 mutex_lock(&p->bo_list->bo_list_mutex),以防 bo_list 在整个提交期间被并发修改;该锁在函数返回(无论成功或失败)时释放。随后进入 drm_exec,锁定所有对象的核心结构如下:
drm_exec_until_all_locked(&p->exec) {
r = amdgpu_vm_lock_pd(&fpriv->vm, &p->exec, 1 + p->gang_size);
drm_exec_retry_on_contention(&p->exec);
if (unlikely(r))
goto out_free_user_pages;
amdgpu_bo_list_for_each_entry(e, p->bo_list) {
r = drm_exec_prepare_obj(&p->exec, &e->bo->tbo.base,
TTM_NUM_MOVE_FENCES + p->gang_size);
drm_exec_retry_on_contention(&p->exec);
if (unlikely(r))
goto out_free_user_pages;
e->bo_va = amdgpu_vm_bo_find(vm, e->bo);
}
if (p->uf_bo) {
r = drm_exec_prepare_obj(&p->exec, &p->uf_bo->tbo.base,
TTM_NUM_MOVE_FENCES + p->gang_size);
drm_exec_retry_on_contention(&p->exec);
...
}
}
就本阶段而言,只需把握 drm_exec 的三个使用要点(其内部回退机制见 6.3.3):
| drm_exec_until_all_locked(&p->exec) | 外层循环:若循环体内任一对象加锁遭遇竞争,则回退所有已持有的锁并从头重执,直至一次性锁定全部对象 |
| amdgpu_vm_lock_pd | 首先锁定该进程 VM 的页目录 BO(PD)——第 ⑥ 阶段页表更新将修改它,故必须一并纳入锁集 |
| drm_exec_prepare_obj(exec, &bo->tbo.base, N) | 锁定单个 BO 的 dma_resv,并为其预留 N 个 fence slot(TTM_NUM_MOVE_FENCES + gang_size) |
| drm_exec_retry_on_contention | 检测到锁竞争时跳回外层循环头触发回退 |
| p->uf_bo | user-fence BO:若提交请求将完成信号写入某 BO,则该 BO 也须锁定 |
循环体内在锁定每个 BO 的同时,还通过 amdgpu_vm_bo_find 查出其 bo_va 映射句柄并缓存至 e->bo_va,供第 ⑥ 阶段 vm_handling 直接复用,省去届时的重复查找。
值得强调的是 fence slot 预留(drm_exec_prepare_obj 的第三参数)。第 ⑧ 阶段 submit 在 notifier_lock 保护下回写 fence 时禁止任何内存分配,因此所需的 dma_resv fence slot 必须在此刻提前预留妥当。gang 提交(一次提交多个 job)会为每个 job 各占一个 slot,故预留数含 gang_size。
bo_list 各 BO
VM 页目录
drm_exec
parser_bos
bo_list 各 BO
VM 页目录
drm_exec
parser_bos
#mermaid-svg-1UGtrY3HDNMBrXlb{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1UGtrY3HDNMBrXlb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1UGtrY3HDNMBrXlb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1UGtrY3HDNMBrXlb .error-icon{fill:#552222;}#mermaid-svg-1UGtrY3HDNMBrXlb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1UGtrY3HDNMBrXlb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1UGtrY3HDNMBrXlb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1UGtrY3HDNMBrXlb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1UGtrY3HDNMBrXlb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1UGtrY3HDNMBrXlb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1UGtrY3HDNMBrXlb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1UGtrY3HDNMBrXlb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1UGtrY3HDNMBrXlb .marker.cross{stroke:#333333;}#mermaid-svg-1UGtrY3HDNMBrXlb svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1UGtrY3HDNMBrXlb p{margin:0;}#mermaid-svg-1UGtrY3HDNMBrXlb .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1UGtrY3HDNMBrXlb text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-1UGtrY3HDNMBrXlb .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-1UGtrY3HDNMBrXlb .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-1UGtrY3HDNMBrXlb .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-1UGtrY3HDNMBrXlb .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-1UGtrY3HDNMBrXlb #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-1UGtrY3HDNMBrXlb .sequenceNumber{fill:white;}#mermaid-svg-1UGtrY3HDNMBrXlb #sequencenumber{fill:#333;}#mermaid-svg-1UGtrY3HDNMBrXlb #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-1UGtrY3HDNMBrXlb .messageText{fill:#333;stroke:none;}#mermaid-svg-1UGtrY3HDNMBrXlb .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1UGtrY3HDNMBrXlb .labelText,#mermaid-svg-1UGtrY3HDNMBrXlb .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-1UGtrY3HDNMBrXlb .loopText,#mermaid-svg-1UGtrY3HDNMBrXlb .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-1UGtrY3HDNMBrXlb .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-1UGtrY3HDNMBrXlb .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-1UGtrY3HDNMBrXlb .noteText,#mermaid-svg-1UGtrY3HDNMBrXlb .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-1UGtrY3HDNMBrXlb .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1UGtrY3HDNMBrXlb .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1UGtrY3HDNMBrXlb .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-1UGtrY3HDNMBrXlb .actorPopupMenu{position:absolute;}#mermaid-svg-1UGtrY3HDNMBrXlb .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-1UGtrY3HDNMBrXlb .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-1UGtrY3HDNMBrXlb .actor-man circle,#mermaid-svg-1UGtrY3HDNMBrXlb line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-1UGtrY3HDNMBrXlb :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
alt
[发生竞争]
loop
[每个 BO]
loop
[until_all_locked]
全部对象一次性锁定成功后退出循环
amdgpu_vm_lock_pd
1
锁定 PD 的 dma_resv
2
drm_exec_prepare_obj(bo, 预留 N slot)
3
锁定 dma_resv
4
retry_on_contention → 回退全部锁,重来
5
2. userptr:加锁之前的页面抓取
对于用户指针型 BO(userptr,即以进程虚拟地址注册、无独立显存后备的 BO),parser_bos 在进入 drm_exec 加锁循环之前先行抓取其后备页面:
amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
e->range = amdgpu_hmm_range_alloc(NULL);
r = amdgpu_ttm_tt_get_user_pages(bo, e->range);
...
for (i = 0; i < bo->tbo.ttm->num_pages; i++)
if (bo->tbo.ttm->pages[i] != hmm_pfn_to_page(...))
userpage_invalidated = true;
e->user_invalidated = userpage_invalidated;
}
关键点:
- HMM range:通过 amdgpu_hmm_range_alloc 分配一段 HMM range,再由 amdgpu_ttm_tt_get_user_pages 走 hmm_range_fault 路径抓取当前有效的物理页。HMM 使内核得以在不 pin 住页面的前提下追踪其变化——若 CPU 侧发生迁移或换出,mmu_notifier 会通知驱动。
- 失效标记:将新抓取的页与 BO 当前记录的页逐一比对,若不一致则置 user_invalidated,留待加锁之后处理。
- 顺序考量:get_user_pages 可能触发缺页、睡眠乃至内存回收,故不能在持有 dma_resv 锁时执行;因此该步骤必须前置于 drm_exec 循环。
加锁完成后,才对被标记失效的 userptr 做校验与页面回填:
amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
usermm = amdgpu_ttm_tt_get_usermm(e->bo->tbo.ttm);
if (usermm && usermm != current->mm)
return –EPERM; /* 禁止跨进程引用他人的 userptr */
if (amdgpu_ttm_tt_is_userptr(...) && e->user_invalidated) {
amdgpu_bo_placement_from_domain(e->bo, AMDGPU_GEM_DOMAIN_CPU);
ttm_bo_validate(&e->bo->tbo, &e->bo->placement, &ctx);
amdgpu_ttm_tt_set_user_pages(e->bo->tbo.ttm, e->range);
}
}
即:先做归属校验(userptr 只能由注册它的进程引用),再对失效者重新绑定新页。此处 userptr 与 SVM/HMM 的缺页处理同源,详见第十章。
3. ttm_bo_validate:把 BO 拉回合法内存域
锁定就绪后,parser_bos 依次校验所有对象的驻留状态。真正的搬移由 amdgpu_cs_bo_validate 完成,其内部封装 ttm_bo_validate:
static int amdgpu_cs_bo_validate(void *param, struct amdgpu_bo *bo)
{
struct ttm_operation_ctx ctx = {
.interruptible = true,
.no_wait_gpu = false,
.resv = bo->tbo.base.resv,
};
...
if (bo->tbo.pin_count)
return 0; /* 已 pin,无须搬移 */
/* 依据 move 配额选择目标域 */
if (p->bytes_moved < p->bytes_moved_threshold && ...)
domain = bo->preferred_domains;
else
domain = bo->allowed_domains;
retry:
amdgpu_bo_placement_from_domain(bo, domain);
r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx);
p->bytes_moved += ctx.bytes_moved;
if (r == –ENOMEM && domain != bo->allowed_domains) {
domain = bo->allowed_domains; /* 降级重试 */
goto retry;
}
return r;
}
要点:
- move 节流(throttling):p->bytes_moved 与 bytes_moved_threshold 构成一次提交的搬移预算。预算充足时使用 BO 的 preferred_domains(通常为高带宽的 VRAM);预算耗尽则退让至 allowed_domains(含 GTT),以避免单次提交把整块 VRAM 耗尽而饿死其他进程。可见 VRAM(bytes_moved_vis)另有独立配额。
- 域降级重试:若首选域因 -ENOMEM 无法容纳,则降级到 allowed_domains 再试一次——这保证了即便 VRAM 紧张,提交仍能以 GTT 驻留的方式成功,而非直接失败。
- ctx.resv:将 ttm_bo_validate 的操作绑定到该 BO 已锁定的 dma_resv,使 TTM 内部的驱逐等待与本阶段的锁保持一致。
parser_bos 分两轮触发校验:先经 amdgpu_vm_validate 校验 VM 自身的页表相关 BO,再遍历 drm_exec 已锁定的每个对象逐一 amdgpu_cs_bo_validate:
r = amdgpu_vm_validate(p->adev, &fpriv->vm, NULL, amdgpu_cs_bo_validate, p);
...
drm_exec_for_each_locked_object(&p->exec, index, obj) {
r = amdgpu_cs_bo_validate(p, gem_to_amdgpu_bo(obj));
...
}
4. 三者协同的全景
至此可以看清 drm_exec、ttm_bo_validate、dma_resv 三者的分工与耦合:
| dma_resv | 每个 BO 的锁 + fence 容器 | 既是 drm_exec 锁定的对象,也是 ttm_bo_validate 依据的 ctx.resv,还是 ⑧ 阶段回写 fence 的落点 |
| drm_exec | 无死锁地一次性锁定全部 BO 的 dma_resv | until_all_locked + prepare_obj + retry_on_contention,并预留 fence slot |
| ttm_bo_validate | 将 BO 回迁至合法内存域并建立驻留 | 由 amdgpu_cs_bo_validate 封装,配合 move 节流与域降级 |
三者以 dma_resv 为交汇点:drm_exec 锁定它、ttm_bo_validate 在它的保护下搬移、submit 最终向它回写完成 fence。这一设计使得"锁定—验证—回写"在同一把锁的语义下前后连贯,无缝衔接。
#mermaid-svg-qmZvOX3ugTwqMsK6{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qmZvOX3ugTwqMsK6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qmZvOX3ugTwqMsK6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qmZvOX3ugTwqMsK6 .error-icon{fill:#552222;}#mermaid-svg-qmZvOX3ugTwqMsK6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qmZvOX3ugTwqMsK6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qmZvOX3ugTwqMsK6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qmZvOX3ugTwqMsK6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qmZvOX3ugTwqMsK6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qmZvOX3ugTwqMsK6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qmZvOX3ugTwqMsK6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qmZvOX3ugTwqMsK6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qmZvOX3ugTwqMsK6 .marker.cross{stroke:#333333;}#mermaid-svg-qmZvOX3ugTwqMsK6 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qmZvOX3ugTwqMsK6 p{margin:0;}#mermaid-svg-qmZvOX3ugTwqMsK6 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-qmZvOX3ugTwqMsK6 .cluster-label text{fill:#333;}#mermaid-svg-qmZvOX3ugTwqMsK6 .cluster-label span{color:#333;}#mermaid-svg-qmZvOX3ugTwqMsK6 .cluster-label span p{background-color:transparent;}#mermaid-svg-qmZvOX3ugTwqMsK6 .label text,#mermaid-svg-qmZvOX3ugTwqMsK6 span{fill:#333;color:#333;}#mermaid-svg-qmZvOX3ugTwqMsK6 .node rect,#mermaid-svg-qmZvOX3ugTwqMsK6 .node circle,#mermaid-svg-qmZvOX3ugTwqMsK6 .node ellipse,#mermaid-svg-qmZvOX3ugTwqMsK6 .node polygon,#mermaid-svg-qmZvOX3ugTwqMsK6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qmZvOX3ugTwqMsK6 .rough-node .label text,#mermaid-svg-qmZvOX3ugTwqMsK6 .node .label text,#mermaid-svg-qmZvOX3ugTwqMsK6 .image-shape .label,#mermaid-svg-qmZvOX3ugTwqMsK6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-qmZvOX3ugTwqMsK6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qmZvOX3ugTwqMsK6 .rough-node .label,#mermaid-svg-qmZvOX3ugTwqMsK6 .node .label,#mermaid-svg-qmZvOX3ugTwqMsK6 .image-shape .label,#mermaid-svg-qmZvOX3ugTwqMsK6 .icon-shape .label{text-align:center;}#mermaid-svg-qmZvOX3ugTwqMsK6 .node.clickable{cursor:pointer;}#mermaid-svg-qmZvOX3ugTwqMsK6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qmZvOX3ugTwqMsK6 .arrowheadPath{fill:#333333;}#mermaid-svg-qmZvOX3ugTwqMsK6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qmZvOX3ugTwqMsK6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qmZvOX3ugTwqMsK6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qmZvOX3ugTwqMsK6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qmZvOX3ugTwqMsK6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qmZvOX3ugTwqMsK6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qmZvOX3ugTwqMsK6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qmZvOX3ugTwqMsK6 .cluster text{fill:#333;}#mermaid-svg-qmZvOX3ugTwqMsK6 .cluster span{color:#333;}#mermaid-svg-qmZvOX3ugTwqMsK6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qmZvOX3ugTwqMsK6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qmZvOX3ugTwqMsK6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-qmZvOX3ugTwqMsK6 .icon-shape,#mermaid-svg-qmZvOX3ugTwqMsK6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qmZvOX3ugTwqMsK6 .icon-shape p,#mermaid-svg-qmZvOX3ugTwqMsK6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qmZvOX3ugTwqMsK6 .icon-shape .label rect,#mermaid-svg-qmZvOX3ugTwqMsK6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qmZvOX3ugTwqMsK6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qmZvOX3ugTwqMsK6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qmZvOX3ugTwqMsK6 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
锁定
ctx.resv 保护下搬移
add_fence 至预留 slot
预留 slot 供
驻留就绪后交由
BO 的 dma_resv(锁 + fence 容器)
drm_exec无死锁加锁
ttm_bo_validate回迁至合法域
submit ⑧回写完成 fence
5. 收尾:user-fence BO 与 GDS 资源
校验通过后,parser_bos 完成两项收尾工作:
- user-fence BO 的 GART 映射:若存在 uf_bo,调用 amdgpu_ttm_alloc_gart 为其建立 GART 映射,并将其 GPU 偏移累加到 gang_leader->uf_addr,使 GPU 完成时能将信号写入该地址。
- GDS / GWS / OA 全局资源:通过 amdgpu_job_set_resources 把 bo_list 中的 GDS(Global Data Share)、GWS(Global Wave Sync)、OA(Ordered Append)对象绑定到各 job。
随后 parser_bos 返回,控制权交回 amdgpu_cs_ioctl 主流程,进入第 ⑤ 阶段 patch_jobs。
错误回退:上述任一步骤失败均跳转至 out_free_user_pages——释放各 userptr 的 HMM range、解锁 bo_list_mutex 后返回;而 drm_exec 已持有的 BO 锁则由其框架在上层统一 backoff,与 8.1 所述整体回滚一致,确保不残留半锁定状态。
6. 小结
- amdgpu_cs_parser_bos 是命令提交中"锁定 + 验证"的核心阶段,同时保证 BO 被稳定锁定且处于合法内存域;
- drm_exec 无死锁地一次性锁定全部 dma_resv,并为后续回写 fence 预留 slot;
- userptr 型 BO 在加锁之前经 HMM range 抓取页面并标记失效,加锁后再做归属校验与页面回填;
- ttm_bo_validate(经 amdgpu_cs_bo_validate 封装)在 move 节流与域降级策略下将 BO 回迁至 VRAM 或 GTT;
- 三种机制以 dma_resv 为交汇点协同,使"锁定—验证—回写"在同一锁语义下前后连贯。
下一节 8.4 将进入依赖与同步点,阐述命令提交如何收集 in-fence(显式依赖 syncobj / 隐式依赖 dma_resv)并生成 out-fence,对应第 ③ ⑦ 阶段的依赖 chunk 解析与 amdgpu_cs_sync_rings。至于第 ⑥ 阶段 amdgpu_cs_vm_handling 涉及的 GPUVM 页表更新,属于 GPU 地址空间管理范畴,留待第九章 GPUVM 专门展开。
网硕互联帮助中心




评论前必须登录!
注册