Pascal GPU 如何从“零”开始获取页表项的完整链条,涵盖页表的生成、结构、存储以及 GPU 硬件遍历页表的具体步骤:
1. 页表的生成与生命周期 (软件层:操作系统 & NVIDIA UVM 驱动)
- 责任方:主机 CPU 上的操作系统 (OS)和NVIDIA Unified Virtual Memory (UVM) 驱动。
- 核心目标:为每个进程维护一个多级页表结构,将进程的虚拟地址 (VA)映射到物理地址 (PA)。这个页表是系统全局的,CPU 和 GPU (通过 UVM)共享同一套映射。
- 生成过程:
- 进程创建:当应用程序进程启动时,OS 内核为其分配一个顶级页目录 (Page Global Directory, PGD)的物理页框 (Page Frame)。在 x86-64 上,PGD 的基址存储在 CPU 的
CR3寄存器中(进程上下文的一部分)。 - 内存分配:当进程通过
malloc()、mmap()等系统调用分配内存时:- OS 内核在进程的 VA 空间分配一段虚拟地址范围。
- OS 为这段 VA 分配物理页框(可能来自物理内存池,或触发缺页中断从磁盘加载)。
- OS遍历或创建进程的多级页表结构(PGD -> PUD -> PMD -> PT),在最终级的页表 (Page Table, PT)中设置对应的页表项 (Page Table Entry, PTE)。
- PTE 关键内容:
- Present Bit:1 表示物理页存在且映射有效。
- 物理页框号 (Page Frame Number, PFN):指向目标物理页的基址。
- 权限位 (RWX):读/写/执行权限。
- 其他标志位:如 Dirty, Accessed, User/Supervisor 等。
- UVM 驱动的介入:
- UVM 驱动作为内核模块,拦截OS 的内存管理操作(特别是与 GPU 共享内存相关的部分)。
- 驱动确保为 GPU 可访问的内存分配的物理页框位于 GPU 能直接访问的物理地址范围内(例如,通过
cudaMallocManaged分配的统一内存)。 - 驱动维护一个特殊的进程上下文结构,其中包含该进程的
CR3值(即 PGD 的 HPA)以及其他 GPU MMU 所需的状态信息。 - 当 GPU 需要为该进程执行计算时,UVM 驱动会将这个进程上下文(包含
CR3)加载到 GPU 的特定 MMU 上下文寄存器中(通常通过写 GPU 的 MMIO 空间实现)。
- 进程创建:当应用程序进程启动时,OS 内核为其分配一个顶级页目录 (Page Global Directory, PGD)的物理页框 (Page Frame)。在 x86-64 上,PGD 的基址存储在 CPU 的
2. 页表在主机内存中的结构 (硬件视角)
- 存储位置:页表的所有级别(PGD, PUD, PMD, PT)都存储在主机 (CPU) 的物理内存 (DRAM)中。
- 物理地址:页表本身也占据物理内存页框,因此每个页表项(无论是 PGD 项、PUD 项、PMD 项还是 PTE 项)都有一个唯一的主机物理地址 (Host Physical Address, HPA)。
- 多级结构 (以 x86-64 4级页表为例):
- PGD (Page Global Directory):顶级目录。一个进程一个 PGD。
CR3寄存器存储其 HPA。 - PUD (Page Upper Directory):第二级目录。一个 PGD 项指向一个 PUD。
- PMD (Page Middle Directory):第三级目录。一个 PUD 项指向一个 PMD。
- PT (Page Table):第四级目录。一个 PMD 项指向一个 PT。一个 PT 包含 512 个 PTE (假设 4KB 页)。
- PTE (Page Table Entry):最终项,包含目标物理页框的 PFN 和标志位。
- PGD (Page Global Directory):顶级目录。一个进程一个 PGD。
- 页表项格式:Pascal GPU 的 MMU 硬件被设计为理解 CPU 架构(如 x86-64 或 ARMv8)定义的页表项格式。例如,一个 x86-64 PTE 是 64 位宽,包含 PFN、Present、RW、User/Supervisor、Accessed、Dirty 等位。GPU 硬件知道如何解析这些位来获取下一级页表的 HPA 或最终的物理页 PFN。
3. GPU 硬件如何“拿”页表项 (Pascal PWT 的遍历流程)
这是最关键的部分!当 GPU SM 请求一个 VA 翻译,但 TLB 和 PTC 都未命中时,Page Walk Engine (PWT)接管:
获取起点:PGD 基址 HPA (CR3):
- PWT 从当前加载的 MMU 上下文寄存器中读取
CR3值。这个值是 UVM 驱动在调度该 GPU 进程时写入的,是PGD 表的起始 HPA。 CR3是遍历的绝对起点!
- PWT 从当前加载的 MMU 上下文寄存器中读取
计算 PGD 索引:
- PWT 硬件根据VA (Virtual Address)的特定位域(在 x86-64 中通常是
VA[47:39])计算出在 PGD 表中的索引 (Index)。 - 目标 PGD 项的 HPA = PGD Base HPA (CR3) + Index * sizeof(PGD_Entry)
- PWT 硬件根据VA (Virtual Address)的特定位域(在 x86-64 中通常是
发起 MRd TLP 获取 PGD 项:
- PWT 生成一个PCIe Memory Read (MRd) TLP请求。
- 请求地址 (Address):上一步计算出的目标 PGD 项的 HPA。
- 请求长度 (Length):通常是一个完整的缓存行(如 64 字节),可能包含多个相邻的 PGD 项(预取优化)。
- 该 TLP 通过 PCIe 总线发送到主机。
- (可选 IOMMU 步骤):如果系统启用了 IOMMU/SMMU,这个 HPA 会被当作 IOVA 处理,由 IOMMU 翻译成真正的 SPA 后再访问主机内存。
- 主机内存响应:主机内存控制器读取该 HPA (或 SPA) 处的数据(即包含目标 PGD 项在内的缓存行数据),封装成Completion with Data (CplD) TLP返回给 GPU。
解析 PGD 项,获取 PUD 基址 HPA:
- GPU PWT 硬件接收到返回的数据。
- 它从数据中提取出目标PGD 项。
- 解析 PGD 项:
- 检查 Present 位:如果为 0,触发 Page Fault 上报驱动。
- 如果有效,从 PGD 项中提取出下一级页表 (PUD) 的基址 HPA。这个 HPA 指向一个完整的 PUD 表所在的物理页框。
计算 PUD 索引:
- 根据 VA 的下一个位域(如
VA[38:30])计算在 PUD 表中的索引。 - 目标 PUD 项的 HPA = PUD Base HPA (从 PGD 项来) + Index * sizeof(PUD_Entry)
- 根据 VA 的下一个位域(如
发起 MRd TLP 获取 PUD 项:
- 重复步骤 3:生成 MRd TLP,请求地址为目标 PUD 项的 HPA,获取包含该 PUD 项的数据。
解析 PUD 项,获取 PMD 基址 HPA:
- 解析返回的 PUD 项,检查有效位,提取出PMD 表的基址 HPA。
计算 PMD 索引:
- 根据 VA 的位域(如
VA[29:21])计算在 PMD 表中的索引。 - 目标 PMD 项的 HPA = PMD Base HPA + Index * sizeof(PMD_Entry)
- 根据 VA 的位域(如
发起 MRd TLP 获取 PMD 项:
- 获取目标 PMD 项。
解析 PMD 项,获取 PT 基址 HPA:
- 解析 PMD 项,提取出PT (Page Table) 的基址 HPA。
计算 PT 索引:
- 根据 VA 的位域(如
VA[20:12])计算在 PT 表中的索引。 - 目标 PTE 的 HPA = PT Base HPA + Index * sizeof(PTE)
- 根据 VA 的位域(如
发起 MRd TLP 获取 PTE:
- 获取目标PTE (Page Table Entry)。
解析最终 PTE:
- PWT 解析返回的 PTE:
- 检查 Present 位:如果为 0,触发 Page Fault 上报驱动。
- 检查权限位:如果 GPU 请求的访问类型(读/写)超出权限,触发 Permission Fault。
- 如果有效且权限足够,从 PTE 中提取出目标物理页框的 PFN (Page Frame Number)。
- 最终物理地址 (PA) = (PFN << PAGE_SHIFT) + VA 的页内偏移 (VA[11:0])
- PWT 解析返回的 PTE:
更新缓存 & 完成请求:
- 将最终有效的 PTE 加载到TLB (L1/L2)中。
- 将遍历过程中获取到的中间页表项 (PGD, PUD, PMD)和 PTE 缓存到Page Table Cache (PTC)中,加速未来对相同或邻近 VA 的访问。
- 将最终得到的 PA 提供给最初发起请求的 SM,使其能够访问物理内存。
关键点总结:Pascal 如何“拿”页表
- 软件奠基:OS 和 UVM 驱动在主机内存中创建和维护标准的多级页表结构,并将进程的顶级目录基址 (CR3 HPA)告知 GPU。
- 硬件遍历:GPU 的Page Walk Engine (PWT)是执行遍历的专用硬件。
- 按级索骥:PWT 严格按照 CPU 定义的页表结构和VA 位域划分,逐级计算索引:
PGD Index -> PUD Index -> PMD Index -> PT Index
- 地址计算:每一级目标项的 HPA 都是:
当前级基址 HPA (来自寄存器或上一级项) + 当前级索引 * 项大小
- PCIe 获取:每一级缺失项的 HPA 都被用来发起标准的 PCIe Memory Read (MRd)事务,从主机物理内存中读取该项(通常附带一个缓存行)。
- 硬件解析:GPU 硬件理解 CPU 页表项格式,解析每一级返回的项,提取出下一级基址 HPA或最终物理页框号 (PFN)。
- 缓存优化:获取到的页表项(包括中间项和 PTE)被缓存在 GPU 本地的PTC和TLB中,极大减少后续遍历需求。
- 故障处理:遍历过程中遇到的无效项或权限错误会触发页故障中断,由 UVM 驱动处理。
整个过程的核心是:GPU 硬件利用驱动提供的起始点 (CR3 HPA),结合对 VA 的位域解析和对标准页表项格式的理解,像“爬梯子”一样,通过发起一系列 PCIe 内存读请求,逐级获取主机内存中的页表项,最终找到目标物理地址。Pascal 的专用 PWT 硬件和 PTC 缓存使这个过程高效且对 SM 计算单元透明。