有读者问起,IOMMU 只有一个么,被系统里所有 device 共享么?IOMMU 或者 SMMU 是在 device 设备上吗?
IOMMU 是给需要「地址转换」才能访问某段 memory 的 I/O device 用的,这个 memory 可能是 CPU 侧的 system memory,对于 GPU 设备来说, 还可能是自带的「显存」(一般叫 graphic memory 或 video memory)。
现代的 x86 系列 CPU 通常包含一个 IOMMU,有一些高级外设,比如 GPU,也会有自己用于地址翻译的硬件单元(windows-hardware/drivers/display/gpummu-model" class=" wrap external" target="_blank" rel="nofollow noreferrer" data-za-detail-view-id="1043" style="text-decoration-line: none; border-bottom: 1px solid rgb(129, 133, 143); cursor: pointer;">GpuMmu),因为也是 I/O 访问 memory 用的,所以也可以算是一种 IOMMU。在同时包含这两种 IOMMU 的系统上,会涉及到两者的配合问题,尤其在虚拟化场景下,会有多种可能的组合方式。
所以,通常意义上的 IOMMU 只有一个,在 CPU 这边。但是广义上的 IOMMU 可能有多个,在 CPU 或者 device 侧。
一个 CPU 有自己专属的 MMU 单元,由该 CPU 上运行的多个进程共享,所以需要有区分进程的 PCID/ASID。而 IOMMU 是被“多个 I/O 设备 + 多个进程”共享,所以需要有区分设备的 StreamID(ARM)或 Requester ID/BDF(x86),以及区分进程的 SubstreamID(ARM)或 PASID(x86),在查找 I/O page table 之前,得先查找 device table。
有什么用
那什么情况下 I/O 设备需要「地址转换」才能访问某段 memory 呢?前面的文章讲到了直接使用 guest VM(虚拟化环境)和 user process(主机环境)的地址进行 DMA 传输的场景,这里再补充两点:
在介绍 zone 的文章中提到,一些使用 DMA 的外设地址总线有限,只能访问低 16MB(对应 ZONE_DMA)或者低 4GB(对应 ZONE_DMA32) 的物理内存。但如果由于某种原因,就是非要访问超出的地址范围,也不是不可以,只是得做一下搬移,经 DMA 传输的内容,从低地址空间,拷贝到高地址空间。
这里低地址空间就充当了一个二传手,被叫做 bounce buffer。多出的一次拷贝需要 CPU 的参与,可以说是有违 Direct Memory Access 的设计初衷。
在没有 IOMMU 的系统上,bounce buffer 是应该尽力避免的,但有了 IOMMU 的映射能力,访问高位物理地址就不是问题啦。
2. 以上这个是 I/O 外设自己的硬件限制,IOMMU 帮它们打开了。那反过来,如果一个 I/O 设备的总线宽度足够访问所有物理内存,从安全的角度,我是不是也可以限制它们的访问范围(在虚拟化场景下也构成一种 isolation)。这就是 DMA Remapping(内核启动信息中可看到 "DMAR" 的相关打印)。
MMIO 的映射和访问 一文介绍了 CPU 对 I/O 设备的 register/memory 访问,这里算是补充了另一个对角的象限,即 I/O 设备对 CPU 侧内存的访问。
以 PCI 设备为例,它们经过 PCI 总线访问 system memory,使用的是 (PCI) Bus Address,如果没有 IOMMU,BA 基本就等于 CPU 对内存访问使用的 Phyical Address,而如果有 IOMMU,则可以进行从 BA(也叫 IOVA) 到 PA 的转换。
有啥不一样
还有读者认为针对“有了 MMU/EPT,还需要一个 IOMMU”的解释说服力不够。那来看下面这张图吧:
诚然,给 CPU 用的 MMU,和给 I/O 设备用的 IOMMU,都有基础的地址转换功能,以及访问权限控制。但由于它们的功能不完全相同,所以在设计上存在若干差异:
由于 IOMMU 被多个 device 共享,所以相比被单个 CPU 独享的 MMU,需要有更高的并行处理能力。
2. 当 CPU 侧的一个进程触发 page fault 后,会阻塞等待 fault handler 将页面换入或分配。而当一个 I/O 设备访问了 non-present 的系统内存,理论上需要在 fault handler 处理完后,由 IOMMU 通过 PCIe 通知 device 再次尝试,但目前的底层硬件难以满足这一点,所以真遇到了 I/O page fault,IOMMU 的做法往往是直接 abort 这个 page request(参考 2.1.3.2 节)。
The IOMMU provides no direct indication to an I/O device of a failed translation when processing an untranslated posted request.
一个软件层面的规避做法是将用于 DMA 传输的内存 pin 住,既不让它在内存里移动位置,也不让它换出到磁盘,就不会发生 I/O page fault 了。
3. IOMMU 还可以用来实现 Interrupt Remapping(内核启动信息中可看到 "DMAR-IR" 的相关打印)。一个做地址转换的,怎么还和中断扯上关系了?
一是现在大量使用的 MSI 中断的底层实现依赖于 DMA,而 IOMMU 主要就是为 DMA 传输服务的。二是将 device 发来的中断重定向,以便正确地传递给 guest OS 的路径中,涉及到一个映射(或者说转换)的过程,所以也可以由 IOMMU 来完成。
参考:
AMD - Virtualizing IO Through the IO Memory Management Unit
Lenovo - An Introduction to IOMMU Infrastructure in the Linux Kernel
推荐本站淘宝优惠价购买喜欢的宝贝:
本文链接:https://hqyman.cn/post/5628.html 非本站原创文章欢迎转载,原创文章需保留本站地址!
休息一下~~