ATS / PRI / PASID
地址翻译服务与共享虚拟内存机制
概述
PCIe 的 ATS(Address Translation Services,地址翻译服务)、PRI(Page Request Interface,页请求接口)和 PASID(Process Address Space ID,进程地址空间标识)是三个紧密关联的机制,它们共同实现了 PCIe 设备与 CPU 之间的共享虚拟内存(Shared Virtual Memory, SVM)。
设计动机:为什么需要 ATS/PRI/PASID?
传统 DMA 模型中,设备只能访问物理地址,软件需要预先分配物理连续内存并建立 IOMMU 映射。这带来了几个痛点:
- 内存浪费:需要预先分配大块物理连续内存,即使设备暂时不用
- 编程复杂:驱动需手动管理 DMA 缓冲区的映射/取消映射
- 无法按需分页:设备不能像 CPU 一样按需触发缺页中断
- 多进程隔离:多个进程共享设备时无法区分各自的地址空间
ATS/PRI/PASID 让设备能直接使用虚拟地址访问内存,像 CPU 一样按需分页,实现设备与 CPU 的统一虚拟地址空间。
三者之间的关系:
| 机制 | 全称 | 核心功能 |
|---|---|---|
| ATS | Address Translation Services | 设备向 IOMMU 请求虚拟→物理地址翻译 |
| PRI | Page Request Interface | 设备在缺页时向 IOMMU 请求页面 |
| PASID | Process Address Space ID | 标识 TLP 属于哪个进程的地址空间 |
ATS(地址翻译服务)详解
ATS 允许 PCIe 设备在发起 DMA 请求前,先向 Translation Agent(TA,即 IOMMU)查询虚拟地址到物理地址的映射,并将翻译结果缓存在设备本地的 Address Translation Cache(ATC)中。
ATS 工作流程
- 翻译请求:设备向 TA 发送 Translation Request,包含需要翻译的虚拟地址和 PASID
- 翻译响应:TA 查找页表,返回 Translation Completion,包含物理地址、权限和大小
- 缓存翻译:设备将翻译结果存入 ATC
- DMA 访问:设备使用缓存的物理地址发起 Translated Request(带 ATS 标志位的 TLP)
- 失效通知:当映射变化时,TA 向设备发送 Invalidate Request,设备清除 ATC 中对应的缓存项
翻译粒度
ATS 翻译以页为单位,粒度与 IOMMU 页表一致:
- 4KB(标准页)
- 2MB / 1GB(大页 / 巨页)
每次翻译请求可以返回一个页的映射,ATC 缓存后设备可以多次访问该页内的任意地址而无需重新翻译。
ATC 结构
ATC 类似 CPU 的 TLB,缓存虚拟地址到物理地址的映射。其关键字段包括:
| 字段 | 说明 |
|---|---|
| 虚拟地址 | 翻译请求的虚拟地址 |
| 物理地址 | 翻译后的物理地址 |
| PASID | 关联的进程地址空间标识 |
| 权限 | 读/写/执行权限 |
| 页大小 | 4KB / 2MB / 1GB |
| 有效位 | 指示该缓存项是否有效 |
翻译失败处理
如果 TA 发现请求的虚拟地址没有有效映射(页表不存在),会返回翻译失败。此时:
- 如果设备支持 PRI,可以发送页请求让 OS 按需分配页面
- 如果不支持 PRI,设备需要终止相关 DMA 操作并报错
PRI(页请求接口)详解
PRI 让 PCIe 设备能像 CPU 一样触发缺页处理。当设备访问的虚拟地址尚未建立映射时,设备可以通过 PRI 向系统请求分配页面,而不需要中断 DMA 流程。
PRI 工作流程
- 页请求:设备向 TA 发送 Page Request,包含虚拟地址和 PASID
- 请求排队:TA 将请求转发给 OS 的缺页处理程序
- 页面分配:OS 分配物理页面,更新页表,建立映射
- 响应返回:TA 向设备发送 Page Request Response,指示成功或失败
- 重新翻译:设备收到成功响应后,重新发起 ATS 翻译请求获取物理地址
- DMA 执行:设备使用新翻译的物理地址完成 DMA 访问
PRI vs 传统缺页中断
CPU 缺页时会触发异常中断,切换到内核态处理。PRI 的设计避免了类似的中断开销:页请求是异步的消息传递,设备可以在等待响应期间处理其他事务。这种非阻塞设计对高性能 I/O 设备(如 GPU、AI 加速卡)尤为重要。
PRI 消息格式
| 字段 | 说明 |
|---|---|
| PASID | 标识请求来自哪个进程地址空间 |
| 虚拟地址 | 请求分配页面的虚拟地址 |
| 请求类型 | 读/写权限请求 |
| 请求索引 | 用于关联请求和响应 |
PASID(进程地址空间标识)详解
PASID 是一个唯一标识符,用于区分不同进程的地址空间。当多个进程共享同一个 PCIe 设备时(如 GPU 被多个应用使用),设备需要在 DMA 请求中携带 PASID 来标识数据属于哪个进程。
PASID TLP Prefix
PASID 通过 TLP Prefix机制携带。TLP Prefix 是附加在 TLP 前部的扩展字段,不影响标准 TLP 格式。
| 字段 | 位宽 | 说明 |
|---|---|---|
| Prefix Type | [31:29] | PASID Prefix = 0b100(扩展前缀) |
| PASID Type | [28:24] | 标识 PASID TLP Prefix |
| Execute Permission | [23] | 是否需要执行权限 |
| Privileged | [22] | 特权模式访问 |
| PASID Value | [21:0] 或 [31:0] | 20 或 24 位 PASID 值 |
PASID 位宽
PCIe 规范定义 PASID 为 20 位(1M 个进程),ATS Extended Capability 中扩展到 24 位(16M 个进程)。实际支持位宽取决于设备和 IOMMU 实现。
PASID 生命周期
- 分配:OS 为进程分配 PASID,并在 IOMMU 中绑定 PASID 与进程页表
- 下发:驱动将 PASID 传递给设备(通过设备配置寄存器或命令队列)
- 使用:设备在发起带 ATS 的 TLP 时携带 PASID 前缀
- 失效:进程结束时,OS 通知 IOMMU 和设备使该 PASID 关联的所有翻译缓存失效
Capability 结构
ATS Extended Capability
ATS 通过 Extended Capability(扩展能力结构)声明,Capability ID = 0x000F(扩展 ID)。
| 寄存器 | 偏移 | 说明 |
|---|---|---|
| ATS Capability Register | +0x04 | 位[4:0]=Invalidate Queue Depth(最大 32),位[5]=Page Aligned Request |
| ATS Control Register | +0x06 | 位[15]=ATS Enable,位[4:0]=Smallest Translation Unit(STU) |
PRI Extended Capability
PRI 通过独立的 Extended Capability 声明,Capability ID = 0x0013。
| 寄存器 | 偏移 | 说明 |
|---|---|---|
| PRI Control Register | +0x04 | 位[0]=PRI Enable,位[1]=Reset(复位 PRG 索引计数) |
| PRI Status Register | +0x06 | 位[0]=Response Failure, 位[1]=Unexpected Page Request Group |
| PRI Outstanding Page Request Capacity | +0x08 | 设备可容纳的未完成页请求数量 |
| PRI Outstanding Page Request Allocation | +0x0C | 当前已分配的未完成页请求数量 |
PASID Extended Capability
PASID 通过 Extended Capability 声明,Capability ID = 0x001B。
| 寄存器 | 偏移 | 说明 |
|---|---|---|
| PASID Capability Register | +0x04 | 位[12:8]=Max PASID Width(支持的 PASID 位宽),位[1]=Exec 权限支持,位[2]=Priv 模式支持 |
| PASID Control Register | +0x06 | 位[0]=PASID Enable |
应用场景
GPU 统一内存(Unified Memory)
GPU 和 CPU 共享同一虚拟地址空间,GPU 可以直接访问 CPU 端的内存而无需显式拷贝。CUDA 的 Unified Memory 和 Managed Memory 底层即依赖 ATS/PRI/PASID 机制。
// CUDA Unified Memory 示例
cudaMallocManaged(&ptr, size); // 分配统一内存
kernel<<<...>>>(ptr); // GPU 直接访问,按需触发 PRI 缺页
AI 加速卡多租户
多用户共享一张 AI 推理卡时,每个用户对应一个 PASID,IOMMU 负责地址隔离,确保用户 A 的 DMA 请求不会访问用户 B 的内存。
RDMA(Remote Direct Memory Access)
RDMA 网卡使用 ATS 来缓存远程内存区域的翻译,PRI 实现按需页面映射,PASID 区分不同连接的地址空间。
虚拟化设备直通
在虚拟化环境中,虚拟机通过 PASID 标识自己的地址空间,IOMMU 根据 PASID 执行正确的地址翻译和隔离。
Linux 配置
IOMMU 支持
ATS/PRI/PASID 需要 IOMMU 硬件支持。在 x86 平台是 Intel VT-d 或 AMD-Vi,在 ARM 平台是 SMMU。
# 检查 IOMMU 是否启用
dmesg | grep -i -e IOMMU -e DMAR -e AMD-Vi
# 检查设备是否支持 ATS
lspci -vvv -s 01:00.0 | grep -i "Translation Services"
# 检查设备是否支持 PRI
lspci -vvv -s 01:00.0 | grep -i "Page Request"
# 检查设备是否支持 PASID
lspci -vvv -s 01:00.0 | grep -i "PASID"
# 内核启动参数(Intel VT-d)
# intel_iommu=on iommu=pt
# intel_iommu=on sm_on # 启用 Shared Virtual Memory
内核配置
# 内核配置需开启以下选项
CONFIG_IOMMU_SUPPORT=y
CONFIG_INTEL_IOMMU=y # Intel VT-d
CONFIG_AMD_IOMMU=y # AMD-Vi
CONFIG_ARM_SMMU=y # ARM SMMU
CONFIG_PCI_ATS=y # PCIe ATS 支持
CONFIG_PCI_PRI=y # PCIe PRI 支持
CONFIG_PCI_PASID=y # PCIe PASID 支持