概述

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 工作流程

  1. 翻译请求:设备向 TA 发送 Translation Request,包含需要翻译的虚拟地址和 PASID
  2. 翻译响应:TA 查找页表,返回 Translation Completion,包含物理地址、权限和大小
  3. 缓存翻译:设备将翻译结果存入 ATC
  4. DMA 访问:设备使用缓存的物理地址发起 Translated Request(带 ATS 标志位的 TLP)
  5. 失效通知:当映射变化时,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 工作流程

  1. 页请求:设备向 TA 发送 Page Request,包含虚拟地址和 PASID
  2. 请求排队:TA 将请求转发给 OS 的缺页处理程序
  3. 页面分配:OS 分配物理页面,更新页表,建立映射
  4. 响应返回:TA 向设备发送 Page Request Response,指示成功或失败
  5. 重新翻译:设备收到成功响应后,重新发起 ATS 翻译请求获取物理地址
  6. 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 生命周期

  1. 分配:OS 为进程分配 PASID,并在 IOMMU 中绑定 PASID 与进程页表
  2. 下发:驱动将 PASID 传递给设备(通过设备配置寄存器或命令队列)
  3. 使用:设备在发起带 ATS 的 TLP 时携带 PASID 前缀
  4. 失效:进程结束时,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 MemoryManaged 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 支持
理解检测
1. ATS 的核心作用是什么?
ATS(Address Translation Services)允许设备向 IOMMU(Translation Agent)查询虚拟地址到物理地址的映射,并将结果缓存在设备的 ATC(Address Translation Cache)中,从而避免每次 DMA 都经过 IOMMU 翻译。PASID 负责地址空间标识,PRI 负责缺页请求。
2. 当设备访问的虚拟地址在 IOMMU 页表中没有映射时,且设备支持 PRI,正确的处理流程是什么?
支持 PRI 的设备在翻译失败时不会直接报错,而是通过 PRI 向系统发送 Page Request。OS 收到请求后按需分配物理页面、更新页表,然后通过 TA 返回成功响应。设备收到响应后重新发起 ATS 翻译请求获取新的物理地址映射,最后完成 DMA。这是非阻塞的异步流程。
3. 在多进程共享 GPU 的场景中,PASID 的作用是什么?
PASID(Process Address Space ID)是进程地址空间标识。当多个进程共享同一个 PCIe 设备时,设备在每个 DMA 请求的 TLP Prefix 中携带 PASID,IOMMU 根据 PASID 选择对应的进程页表进行地址翻译,从而实现进程间的地址隔离。这是共享虚拟内存(SVM)多租户场景的关键机制。