Linux GPU/NPU加速器总体架构:完整软件栈
第2章:GPU/NPU完整软件栈——从Application、Runtime、UMD、KMD到Firmware和Hardware
上一章解释了CPU、GPU、NPU为何形成不同的执行、调度和内存模型。本章沿一项任务的生命周期继续向下:一个API调用怎样变成设备命令,怎样穿过用户态与内核态,怎样被固件和硬件执行,完成结果又怎样返回应用。
1 | |
这不是所有设备都必须严格实现的七层协议,而是一张责任地图。真实软件可能合并相邻层,也可能增加编译服务、守护进程或多个固件处理器。
0. 源码基线与边界
1 | |
主要坐标:
1 | |
本章分析稳定的软件边界,不把某个厂商的私有协议描述成Linux通用接口。
2.1 为什么GPU/NPU软件栈会这么深
加速器同时涉及高级API、编译、命令生成、内存虚拟化、多进程隔离、调度、电源、故障恢复和异步完成。把所有职责塞入一层,会让更新速度、安全边界和硬件控制相互冲突。
1 | |
2.2 Application到底负责什么
Application表达业务目标:绘制场景、执行Compute Kernel、运行神经网络。它管理业务数据和算法流程,但不应了解Doorbell寄存器、GPU页表格式或NPU Tile配置。
2.3 Framework和Programming API是什么
Framework提供Tensor、Graph、Scene等高级对象;Vulkan、OpenGL、OpenCL等Programming API定义可移植的设备操作语义。
1 | |
2.4 Runtime到底是什么
Runtime把编程模型落到设备使用流程,可能负责设备枚举、队列、内存API、同步、后端选择和调用编译器。它更靠近Programming Model,而KMD更靠近系统与硬件控制。
2.5 Runtime为什么不能等同于Driver
Runtime和UMD边界不是统一标准。某些项目中Runtime调用独立UMD;另一些库同时承担两种角色。判断时应看责任而非文件名:是否实现高级API语义、是否生成设备命令、是否通过内核UAPI访问设备。
2.6 UMD是什么
UMD(User Mode Driver)是用户态中了解具体GPU/NPU命令格式和能力的软件。典型GPU路径:
1 | |
RADV是UMD,AMDGPU是KMD。
2.7 为什么需要User Mode Driver
Shader编译、Pipeline状态和命令生成变化快、复杂且与API强相关。放在用户态可以独立升级、隔离崩溃、使用普通开发工具,并避免扩大内核攻击面。
2.8 UMD通常负责哪些事情
1 | |
UMD不能因此绕过安全:KMD仍控制设备文件、可访问对象、地址空间和特权动作。
2.9 Shader、Kernel和Graph编译在哪里发生
GPU通常在用户态把GLSL/SPIR-V等编译为设备ISA;NPU编译器通常在用户态完成Graph Fusion、量化、Tiling、内存规划和Codegen。KMD不适合承担持续演进的高级编译优化。
2.10 Command Buffer通常在哪里构建
现代GPU常由UMD在用户映射的Buffer中批量构建Command Buffer,再一次提交给KMD。这样减少系统调用并把API状态翻译留在用户态。
1 | |
KMD仍通过对象句柄、VM映射、提交规则和硬件隔离限制其能力;不同硬件的验证强度也不同。
2.11 KMD是什么
KMD(Kernel Mode Driver)运行于内核,连接Linux设备模型、DRM/accel公共框架和具体硬件。它是用户请求进入共享设备的特权边界。
2.12 为什么KMD必须放在内核态
IOMMU配置、中断、电源域、设备复位、特权寄存器和跨进程资源仲裁不能交给任意进程,否则一个Client即可破坏其他Client或整个系统。
2.13 KMD通常负责哪些事情
1 | |
2.14 Device Ownership
多个进程打开同一设备时,内核用Device FD及其file-private对象确定请求主体,再把Context、Buffer、VM和Queue关联到该Client。关闭FD触发生命周期清理,但被共享或仍在执行的对象可能由引用计数延后销毁。
2.15 Process与Context Isolation
1 | |
隔离依赖KMD、GPU MMU/IOMMU、PASID和固件/硬件权限共同实现。Context ID本身不是安全边界,必须与地址翻译和访问检查结合。
2.16 Memory Management职责如何分层
1 | |
一个Buffer可能同时有应用对象、UMD对象、KMD BO、CPU VA、GPUVA、IOVA和物理Backing,不能把它们混成一个“地址”。
2.17 Command Submission职责如何分层
1 | |
“Validate”具体做什么高度依赖硬件和UAPI,不能假设KMD会逐条解析所有现代GPU命令。
2.18 Firmware为什么出现
现代设备拥有大量Queue、Context、Engine、电源状态和错误状态机。Firmware能够屏蔽硬件代际差异,并在更靠近设备的位置完成低延迟控制。
2.19 Firmware和KMD是什么关系
KMD负责OS侧资源与安全边界,通过Mailbox、共享Ring、Doorbell等协议向Firmware发请求;Firmware验证其协议范围内的消息、编程设备内部资源并报告完成或错误。
1 | |
2.20 Firmware Scheduler是什么
Firmware Scheduler可能管理Firmware可见Context、逻辑Queue、硬件Queue Slot和设备内部优先级。它掌握的状态比KMD更接近硬件,但通常不了解完整的Linux进程策略。
2.21 Hardware Scheduler是什么
Hardware Scheduler直接在Engine、CU/SM、Wave/Warp或Tile等资源上选择执行单元。它的时间尺度更短,使用硬件状态做决策,Linux无法逐个调度这些最细粒度对象。
2.22 三层Scheduler的区别
| 层 | 常见对象 | 主要视角 |
|---|---|---|
| Kernel Scheduler | Job、Entity、Context | Client公平、优先级、依赖、超时 |
| Firmware Scheduler | FW Queue、Context、HW Slot | 设备内部资源、抢占、电源/故障 |
| Hardware Scheduler | Wave/Warp、Workgroup、Tile | 即时就绪状态和执行资源 |
同名不等于同一个算法或调用链。
2.23 UMD和KMD之间通过什么通信
1 | |
FD提供身份和生命周期锚点,系统调用提供权限检查与内核入口。
2.24 ioctl到底解决什么问题
设备需要CREATE_CONTEXT、CREATE_BO、VM_BIND、SUBMIT、WAIT、QUERY等非通用文件操作。调用链:
1 | |
UAPI一旦发布必须长期兼容,因此结构大小、保留字段、错误码和并发语义都很重要。
2.25 mmap为什么重要
mmap让用户态直接访问受控的虚拟映射,可用于Buffer、Command Ring、Instruction Buffer或Doorbell,避免频繁复制和系统调用。
它不等于暴露物理地址:KMD的mmap回调、VMA、页表、访问属性和对象生命周期仍控制映射。
2.26 poll、wait和event如何用于异步完成
设备执行期间CPU可继续工作。完成后KMD signal Fence、更新共享状态或排队Event,并唤醒poll/wait等待者。忙轮询只适用于极短且明确受控的低延迟场景。
2.27 DRM Device Node是什么
DRM为用户态导出字符设备节点,常见:
1 | |
打开后,DRM Core创建drm_file等file-private状态,驱动在其上关联Client对象。
2.28 Primary Node和Render Node是什么
Primary Node保留显示、DRM Master和部分特权/历史接口;Render Node只开放标记为可安全用于渲染的接口,不提供Modesetting和全局特权控制。
2.29 为什么Render Node对GPU计算很重要
离屏渲染和GPGPU不应依赖显示服务器认证。Render Node通过文件权限提供非特权GPU入口,并减少全局名称和显示资源带来的攻击面。主线要求驱动显式把驱动私有ioctl标记为Render允许。
2.30 NPU的/dev/accel模型
Linux accel Core复用DRM基础设施,但以独立设备类别和/dev/accel/accel*节点隔离纯计算加速器,避免图形用户栈误识别。其基本模式仍是FD + ioctl + mmap/poll。
2.31 一次GPU任务从Application到Hardware
1 | |
2.32 一次NPU任务从Application到Hardware
1 | |
2.33 一次Memory Allocation如何跨越多层
1 | |
分配、放置、映射和同步是不同动作,成功分配物理Backing不代表设备地址已经可用。
2.34 一次Command Submission如何跨越多层
提交包含命令Buffer、引用对象、依赖和输出同步点。KMD解析UAPI、固定生命周期、建立依赖并排队;Scheduler/Firmware决定何时进入硬件。提交成功只表示工作被接受,不表示已经完成。
2.35 一次Completion如何反向返回Userspace
1 | |
错误完成也必须传播,避免应用永久等待。
2.36 谁负责Policy,谁负责Mechanism
1 | |
Policy可能分散在Runtime、KMD和Firmware;分析代码时要找到真正拥有全局信息和最终决定权的层。
2.37 为什么不是所有逻辑都放进Kernel
编译器、Pipeline优化和Graph变换体积大、变化快、容易处理不可信输入。放入内核会扩大攻击面、增加崩溃影响,并被内核UAPI兼容承诺束缚。
2.38 为什么也不能把所有逻辑都放Userspace
用户态不能安全独占全局设备、编程IOMMU、处理中断、控制电源或复位其他Client共享的硬件。公平、隔离和恢复要求可信KMD参与。
2.39 Firmware-first架构为什么越来越常见
Firmware-first把Queue、Context、功耗和硬件状态机更多地下沉到设备控制器,便于支持复杂硬件与低延迟决策。代价是协议兼容、可观测性和调试难度上升;KMD日志可能只能看到“请求已发出”,真正阻塞发生在Firmware内部。
2.40 真实案例:Mesa、RADV和AMDGPU
1 | |
具体提交接口随版本和硬件演进,但UMD/KMD责任边界长期稳定。
2.41 真实案例:AMD XDNA NPU
主线文档描述的概念路径:
1 | |
MERT服务特权管理请求,ERT服务Workload Context;DMA在Host DDR与Memory Tile间搬运数据。这是KMD—Firmware—Hardware分工的现实样本。
2.42 Linux内核视角下的软件栈边界
内核看到的不是Vulkan Draw或神经网络Conv语义,而是:
1 | |
因此后续源码阅读应从open/ioctl/mmap/poll入口追踪这些对象的创建、所有权、状态变化和销毁。
2.43 本章实验
2.43.1 观察设备节点
1 | |
2.43.2 用strace观察边界
1 | |
预测:大量复杂用户态逻辑最终收敛为少量Linux系统调用。
2.43.3 查找DRM ioctl分发
1 | |
2.43.4 观察XDNA分层
1 | |
实验结论应写成“观察→对象→所在层→下一层接口”,而不是只记录命令输出。
2.44 性能与工程意义
端到端延迟可分解为:
1 | |
GPU Busy低可能是提交批次太小、依赖等待或编译卡顿;NPU利用率低可能是Tiling、DMA、DDR或Partition策略问题。性能定位的第一步是Layer Classification,而不是先改内核。
2.45 常见误区
误区1:Runtime就是Driver
边界可能重叠,但职责不同:Runtime实现编程模型,UMD理解具体设备,KMD控制系统资源。
误区2:UMD只是薄封装
现代UMD可能包含庞大的编译器、状态跟踪和命令生成逻辑。
误区3:KMD负责Shader或Graph编译
通常错误,这类工作主要在用户态。
误区4:KMD只是写寄存器
现代KMD还管理VM、BO、Context、Scheduler、Sync、IRQ、Power和Recovery。
误区5:Firmware只在启动时运行
很多现代设备Firmware长期参与命令、调度、功耗和故障处理。
误区6:三个Scheduler是同一个Scheduler
它们运行层次和调度对象都不同。
误区7:mmap就是暴露物理地址
错误,它仍是KMD控制的虚拟映射。
误区8:ioctl返回成功代表任务完成
通常只代表提交被接受;真正完成由Fence/Event表达。
2.46 博客总结
现代GPU/NPU软件栈深,是因为高级编程模型、编译、系统安全、设备控制和硬件执行必须采用不同更新速度与信任边界:
1 | |
官方阅读入口:
- Linux DRM Userland Interfaces
- Linux Compute Accelerators Introduction
- AMD XDNA NPU Documentation
- Linux 7.3-rc6源码
2.47 学完检查题
- Application、Framework、Runtime和UMD分别解决什么问题?
- 为什么Runtime与UMD不是永远严格分开的两个库?
- Shader/Graph编译为什么主要位于用户态?
- UMD构建Command Buffer后,KMD还必须保证什么?
- 哪些能力决定KMD不能被完全移到用户态?
- Device FD如何帮助建立Client身份和生命周期?
- BO、CPU VA、GPUVA、IOVA和物理Backing有何区别?
- 提交成功为什么不等于执行完成?
- Kernel、Firmware、Hardware Scheduler分别管理什么?
- Firmware-first的收益和调试代价是什么?
- Primary Node与Render Node的权限模型有何区别?
- accel Core为什么复用DRM又使用独立设备节点?
- 一次Completion怎样从硬件传播到Userspace?
- RADV和AMDGPU分别处于哪一层?
- AMD XDNA中Mailbox、ERT、Partition和Tile是什么关系?
- 如何用
strace证明UMD/KMD边界真实存在? - GPU Busy低时,为什么不应首先断定硬件算力不足?
- 请画出Memory、Submit、Completion三条跨层路径。
下一章将进一步拆解同一个Memory、Context和Scheduler功能在Runtime、UMD、KMD与Firmware中分别保存什么状态、执行什么Policy和提供什么Mechanism。