本文讨论 PCIe 设备通过 IOMMU 访问主机内存时,页表映射不存在或访问权限不足所引发的异常。通过分析 Linux 代码,对比 Intel VT-d、AMD IOMMU 和 ARM SMMUv3 在三种 ATS/PRI 场景下的处理方式,重点回答以下问题:
- 异常发生在 IOMMU Translation、ATS Translation 阶段,还是 PRI Page Request 阶段
- 异常发生时,DMA Request 是否已经发出
- DMA Request 最终会被阻止、终止、重新发起还是继续执行
1.简介
下面总结了三种场景:
无 ATS/ATC:设备直接发送 Untranslated DMA Request,由 IOMMU 完成 Address Translation。
有 ATS/ATC,无 PRI:ATC 未命中后,设备先发送 ATS Translation Request,取得可用 Translation 后再发送 Translated DMA Request。
有 ATS/ATC,有 PRI:ATC 未命中后,设备先发送 ATS Translation Request,若 ATS Translation Completion 失败,则发送 Page Request 请求主机 OS 分配缺失的内存页。
2.结论
| 场景 | Address Translation 路径 | 异常现场 | DMA Request 是否发出 | 异常处理方法 | DMA Request 是否可恢复 |
|---|---|---|---|---|---|
| 无 ATS/ATC | 设备发送 Untranslated DMA Request,由 IOMMU 执行 Address Translation | IOMMU 执行 Address Translation 时找不到有效页表项或者访问权限出错 | 已经发出 | PCIe/RC:IOMMU 阻止或终止 DMA Request。Memory Read Request 可能收到带错误状态的 Completion(UR/CA),也可能最终发生 Completion Timeout,具体由平台决定;Memory Write Request 属于 Posted Request,不会返回 Completion,数据可能被丢弃,并通过平台错误处理或 AER 上报。 Intel VT-d:写入 Fault Recording Register 并触发异常中断。 AMD IOMMU:在 Event Log 中写入 IO_PAGE_FAULT并触发中断;这是普通 Fault Event,不是 PPR Log 中请求内存页的 Page Request。ARM SMMUv3:写入 EVTQ;PCIe 总线不能暂停等待软件处理,必须按 NoStall/Terminate 方式处理。 | DMA Request 通常不可恢复。该 Request 已经在 Translation 阶段失败。 |
| 有 ATS/ATC,无 PRI | ATC 未命中后,设备先发送 ATS Translation Request | IOMMU 执行 Address Translation 时找不到有效页表项或者访问权限出错,此使会返回带错误状态的 ATS Translation Completion | 未发出 | PCIe/设备:ATS Translation Completion 未提供可用 Translation;设备不能填充有效 ATC,也不能发送对应的 Translated DMA Request,通常会将队列或描述符置为失败,或者稍后重新发起任务。 Intel VT-d:Request 不进入 PRQ。 AMD IOMMU:Request 不进入 PPR Log。 ARM SMMUv3:Request 不进入 PRIQ。硬件即使另行记录,也只是与实现相关的普通 Fault/Error,不会触发 OS 缺页处理。 | 没有发出 DMA Request。ATS Translation 失败后,DMA Request 也会失败;如果驱动在错误处理路径中另行建立映射并重新提交任务,那也是新的 DMA Request,不是恢复原 DMA Request。 |
| 有 ATS/ATC,有 PRI | ATS 未取得可用 Translation 后,设备发送 Page Request | Page Request 进入 IOMMU 的专用队列;Intel、AMD 将其上报为缺页异常 I/O Page Fault(IOPF) | 未发出 | PCIe/设备:设备发送 Page Request,并接收 Success、Invalid 或 Failure Response;Success 后重试 ATS,Invalid/Failure 时停止该访问。 Intel VT-d:PRQ → Linux IOPF → Page Group Response Descriptor。 AMD IOMMU:PPR Log → Linux IOPF → COMPLETE_PPR_REQUEST。ARM SMMUv3:架构路径为 PRIQ/CMD_PRI_RESP;当前 Linux 版本将该 Request 视为 Unexpected PRI Request 并返回 DENY。 | 不是恢复一笔已经失败的 DMA Request,而是在 DMA Request 发出前发送 Page Request, 请求主机 OS 分配缺失的内存页。OS 处理成功后,设备重发 ATS Translation Request,收到正确的 ATS Translation Completion 后再发出 DMA Request。 |
3.总结
第一种场景是在IOMMU Translation 阶段产生普通 IOMMU Fault,硬件记录错误并阻止或终止已发出的 Request。
第二种场景没有 PRI,也不进入 PRQ/PPR Log/PRIQ,失败可能只反馈给设备,不一定产生 Linux 可见的普通 IOMMU Fault。因此,前两种场景都不会因该次失败而自动进入 OS 缺页处理流程。
第三种场景具备进入可恢复缺页处理的硬件入口:PCIe 设备发送 Page Request,IOMMU 将其放入专用 Page Request Queue;Intel、AMD 驱动再将其作为 IOPF 交给 OS,OS 进入缺页处理异常,补齐页表后返回 Success。本文所用 ARM SMMUv3 驱动虽然能从 PRIQ 识别 PRI,但当前直接返回 DENY,不进入通用 IOPF。
这里的“可恢复”特指:能否在实际 DMA Request 发出之前,通过 PRI 补齐映射,使设备随后重新取得 ATS Translation 并发送 DMA Request。它不表示 IOMMU 能重试一笔已经失败的原始 DMA Request。
下图比较三种场景的处理流程。蓝色表示设备 Request,黄色表示 Address Translation 或软件处理,绿色表示恢复成功,红色表示失败或终止。