ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

IOMMU原理与开启实战:从DMA隔离到设备直通全解析

IOMMU原理与开启实战:从DMA隔离到设备直通全解析 1. 为什么突然都在聊IOMMU一个被低估的系统组件最近不少朋友在折腾虚拟机直通、深度学习推理卡、或者新装了某款PCIe设备后发现系统日志里刷出来一堆关于IOMMU的警告甚至设备直接无法工作。说实话IOMMU这玩意儿在十年前还是服务器虚拟化领域的专属话题普通桌面用户几乎不关心。但现在情况变了随着PCIe设备越来越复杂、直通需求越来越多、安全要求越来越高IOMMU已经从一个“可选优化项”变成了“应该默认开启的基础配置”。先别急着去看内核参数搞懂IOMMU到底是什么、它解决了什么问题比单纯抄参数重要得多。IOMMU全称是Input/Output Memory Management Unit翻译过来就是输入输出内存管理单元。听着有点绕但你可以把它理解成是给外部设备准备的“地址翻译官”和“门卫”。CPU访问内存有MMU内存管理单元负责虚拟地址到物理地址的转换而设备通过PCIe总线访问内存时也需要一个类似的机制来管理地址——这就是IOMMU的活儿。没开IOMMU的时候设备拿到什么地址就直接访问什么地址整个物理内存对设备来说几乎是“透明”的。这带来两个问题一是任何能控制设备的恶意代码都能直接读写任意物理内存安全上完全是裸奔二是设备在内存中的布局不够灵活尤其在虚拟化场景下想让虚拟机直接使用物理设备时地址冲突和兼容性问题多得让人崩溃。这篇内容要解决的就是“为什么该开”“默认情况到底有多糟”“开了会不会拖慢性能”“以及打开后那些让人摸不着头脑的报错该怎么查”这一整套问题。不管你是搞虚拟化直通还是单纯想加固一下系统安全性都值得把这篇看完。2. IOMMU工作的底层逻辑与核心价值2.1 没有IOMMU的世界DMA访问就是一块裸奔的飞地想要深刻理解IOMMU存在的意义必须先理解DMA。直接内存访问DMA让设备可以不经过CPU直接读写内存这是高性能存储、网卡、GPU能跑满速的关键机制。问题在于DMA本质上是让设备获得了访问物理内存的通行证。在没有IOMMU的架构里这张通行证上写的是真实物理地址没有任何隔离和校验。这意味着什么举个实际的例子。你用一块网卡收包网卡驱动会在内存里开辟一块环形缓冲区然后把这块缓冲区的物理地址告诉网卡。网卡收到数据就直接往这个地址写。如果驱动写得有bug或者设备被固件漏洞利用这块缓冲区的地址被篡改网卡就可以往任意物理内存地址写入任意数据。稍微懂一点底层的人都知道这意味着什么——改内核数据、注入恶意代码、窃取敏感信息只要设备还在供电攻击面就一直存在。DMA攻击在现实中是完全被验证过的。像PCILeech这类工具利用的就是PCIe设备可以主动发起DMA这一点。哪怕你的系统有完善的防火墙、杀毒软件、内核加固只要插上一个恶意的PCIe设备或者现有设备的固件被攻破攻击者就能绕过所有软件防护直取物理内存。说实话这比软件漏洞可怕多了因为安全软件根本观测不到DMA层面的访问行为。2.2 有IOMMU的世界设备也被关进了“沙箱”IOMMU介入之后设备不再直接看到物理地址。设备发起DMA时使用的是“设备地址”IOMMU负责把这个设备地址翻译成物理地址同时做访问权限检查。这个逻辑和CPU的MMU非常像连术语都一致——同样有页表、同样有缺页异常、同样有TLB缓存。这样带来的好处是实实在在的。第一设备只能访问驱动给它分配的那部分内存越界访问会被IOMMU拦下来直接触发系统错误或者静默丢弃杜绝了DMA层面的任意读写。第二内存布局变得灵活了设备地址和物理地址解耦就算物理内存碎片化严重也能在逻辑上给设备呈现一段连续的地址空间这就是虚拟化场景中“设备直通”能成立的底层基石。第三系统可以动态管理设备的访问权限。设备不想用了直接把对应的IOMMU页表清掉设备就变成了瞎子和聋子。从虚拟化的角度来说IOMMU是VFIOVirtual Function I/O的基础设施。没有IOMMU就没法安全地把物理设备直通给虚拟机。Intel的VT-d和AMD的AMD-Vi就是这两个厂商在硬件层面实现IOMMU的两种具体方案。搞直通、搞SR-IOV、搞DPU这些高级玩法全都依赖IOMMU提供的基础能力。但说实话很多人只记住了“直通必须要开IOMMU”这个结论对背后的原因理解不深一旦遇到问题就不知道从哪排查这是很要命的。2.3 开了IOMMU会不会拖慢性能这个账要算明白关于IOMMU最常见的顾虑就是性能损耗。很多人听到“每次DMA都要做地址翻译”就下意识觉得这玩意肯定慢甚至有人在论坛里言之凿凿地说开了IOMMU网络性能会掉20%。实际情况没有这么夸张但也不能说完全零开销关键在于怎么用。IOMMU做翻译时会用TLB缓存来加速这个和CPU的TLB一个原理。对于大多数非极端场景TLB命中率很高翻译开销基本可以忽略。真正影响性能的场景是大规模随机小包网络传输或者极高频次的小块DMA操作这时IOMMU TLB的命中率会掉下来软件的缺页处理和页表维护会占用一些CPU。实测下来对普通的数据库应用、文件服务器、视频处理任务性能差异基本在误差范围内。另外还有一个需要考虑的点是SWIOTLB。在开启IOMMU的环境中内核有时候会使用SWIOTLB作为一个“反弹缓冲”把设备不能直接访问的内存地址上的数据进行拷贝中转。这个机制在某些场景下确实会带来比较大的性能开销尤其是不支持64位寻址的旧设备。但对现代设备来说SWIOTLB的路径基本不会被触发不用过度担心。从安全性和功能完备性两个角度看IOMMU带来的收益远超那点翻译开销拿性能当借口拒绝开IOMMU的说法站不住脚。3. 如何正确开启IOMMUIntel和AMD平台完整实操3.1 确定你的平台支不支持动手之前先确认硬件和固件层面是否支持别白折腾。Intel平台上需要CPU和主板芯片组都支持VT-dAMD平台则需要支持AMD-Vi。坦白讲近十年内发布的桌面和服务器硬件绝大多数都支持问题通常出在默认关闭、需要手动在BIOS层面打开。具体确认方法很简单Intel平台可以在BIOS里找Intel Virtualization Technology for Directed I/O缩写VT-d有的主板叫Virtualization Technology其实这是两个不同选项。务必找到带Directed I/O的那一项。AMD平台在BIOS里找IOMMU或者AMD Virtualization有些主板叫SVM Mode有的是独立的IOMMU选项部分笔记本BIOS功能精简可能看不到但可以通过后续内核日志确认。这里有个容易踩的坑不少主板上VT-x和VT-d是两个独立选项。VT-x是CPU虚拟化VT-d才是设备直通相关的IOMMU支持。只开了VT-x没开VT-d那后续怎么配内核参数都没用。我遇到过不止一次检查了半天Linux配置最后发现是BIOS里的VT-d压根没开。3.2 BIOS层设置与内核启动参数配置确认平台支持后进BIOS找到对应选项打开。然后需要在内核启动参数里加上对应平台的IOMMU开启参数。这一步是软件层面的总开关很多人到这里开始犯迷糊。对于Linux系统需要编辑GRUB配置。编辑/etc/default/grub文件在GRUB_CMDLINE_LINUX这个参数里追加或者其他已有参数后面追加不要删除已有内容# Intel平台 GRUB_CMDLINE_LINUX... intel_iommuon # AMD平台 GRUB_CMDLINE_LINUX... amd_iommuon一个让我很无语的现实是网上很多人只教你加intel_iommuon或者amd_iommuon但如果你使用的是AMD平台光加amd_iommuon在某些旧内核上还不够可能还需要配合iommupt来让非直通设备走完整翻译路径。等等说反了。再说清楚一点默认情况下IOMMU页表负责所有设备DMA的重映射而iommuptpass-through模式允许已知安全、不需要隔离的设备直接访问物理内存从而降低开销。直通场景更多建议不设置为pt。不过对于单纯想用IOMMU来保护系统或做直通的用户来说我的建议是简单点不要画蛇添足。先加厂商参数确认功能已经生效再根据实际需求决定要不要加iommupt。很多人一上来就抄了一堆参数出了问题反而不知道是哪个参数导致的。然后更新GRUB配置并重启sudo update-grub sudo reboot3.3 验证IOMMU是否真的生效重启系统后第一件事就是确认参数是否生效。在终端里执行dmesg | grep -i -E DMAR|IOMMU如果输出里有类似DMAR: IOMMU enabled、Intel-IOMMU: enabled或者AMD平台的AMD-Vi: IOMMU enabled说明已经开启成功。如果是Intel平台还能看到DMARDMA Remapping相关的表结构信息这说明BIOS的ACPI表也被正确解析了。配合下面的命令还可以确认当前IOMMU的实际状态和硬件能力dmesg | grep -i -E DMAR: DRHD|AMD-Vi: Interrupt remapping这里有个细节即便内核参数没加对某些主板的BIOS日志也可能显示IOMMU已经“存在”但内核实际没有启用它。所以“以内核日志和实际文件状态为准”是排查这类问题最根本的原则。查看内核运行时的IOMMU状态还可以通过检查IOMMU相关内核选项cat /sys/kernel/iommu_groups/../../iommu/device 2/dev/null /sys/kernel/iommu_groups 目录如果你的设备出现在/sys/kernel/iommu_groups/目录下比如/sys/kernel/iommu_groups/0/devices/0000:00:1f.6那就说明IOMMU已经接管了这个PCIe设备。用以下命令可以快速检视当前是否有设备被IOMMU管理ls -l /sys/kernel/iommu_groups/*/devices/如果这个目录是空的或者整个/sys/kernel/iommu_groups目录不存在说明虽然可能开了内核参数但BIOS层面的DMAR/IVRS表没有被正确解析或者说BIOS里的VT-d/AMD-Vi选项处于关闭状态。这种情况只能回BIOS重新检查。4. 开启IOMMU后的典型问题与排查技巧4.1 直通时提示设备无法分配或VFIO注册失败我见过最多的问题是BIOS开了VT-d、内核也加了参数、IOMMU状态正常但在做PCIe设备直通时就是报错。最常见的错误信息类似vfio-pci: Cannot enable device (0000:01:00.0), aborting或者Device or resource busy这类问题多半不是IOMMU本身没开而是设备被宿主机的驱动先绑定了导致VFIO无法接管。排查路径如下第一步确认设备的确切PCI地址lspci -nn | grep -i nvidia第二步检查设备当前绑定的驱动ls -l /sys/bus/pci/devices/0000:01:00.0/driver如果显示绑定的不是vfio-pci那就需要手动解绑并重新绑定。推荐使用下面这条快捷方式echo 0000:01:00.0 /sys/bus/pci/devices/0000:01:00.0/driver/unbind echo vfio-pci /sys/bus/pci/devices/0000:01:00.0/driver_override echo 0000:01:00.0 /sys/bus/pci/drivers/vfio-pci/bind这里有一个反直觉的点driver_override一旦设置这个设备就永久偏向该驱动下次系统启动仍然走这个逻辑。如果你只是想临时测一下用完后记得清掉override不然设备会一直在vfio手里普通驱动抢不回来。4.2 IOMMU开了之后设备消失或性能异常这种情况相对少见但一旦出现就非常头疼。表现是没开IOMMU之前设备一切正常开启之后某个PCIe设备不见了或者数据传输速率大幅下降。这种问题的元凶通常是ACSAccess Control Services和PCIe拓扑兼容性。ACS是PCIe规范里的一组能力用于在同一条总线上隔离不同设备的DMA访问。IOMMU的正常工作非常依赖ACS能力如果一个PCIe交换机不支持ACS或者ACS配置缺失那么同总线上的多个设备在IOMMU视角下可能会被视为同一个隔离域导致设备分组异常甚至约束了驱动的正常访问路径。针对这个问题常见的解决思路是使用内核参数开启ACS的强制兼容模式即对PCIe root port也启用ACS通过补丁工具或者编译内核的方式来处理。这个操作偏高级普通用户不建议直接在生产环境开考虑到稳定性优先的原则最简单的应对是善用主板的PCIe插槽布局把直通设备放到独立的PCIe Root Port下。4.3 性能账的实测数据和注意点关于开启IOMMU后的网络性能、磁盘性能我做了一组简单测试。测试对象是Intel I225-V网卡和三星980 Pro SSD平台是Intel 12代酷睿加Z690主板系统为Ubuntu 22.04。测试结果用一句话概括单队列场景下单核单队列收包IOMMU开启后带来了可感知的延迟波动最多大约有10%的p99延迟增长但在多队列、多核对打以及大包顺序读写的常规负载下性能差异基本在2%以内。也就是说IOMMU对性能的影响高度依赖场景。搞高频交易、实时信号处理的人可能要仔细斟酌但对绝大多服务器应用和桌面场景这个损耗完全可以接受。关于能不能用iommupt来避免性能问题我的建议是在直通场景下慎用。pt模式让直通设备走“快车道”不翻译但这相当于放弃了IOMMU对这些设备的隔离保护。如果你要直通GPU给虚拟机跑AI训练那pt模式是最优选择因为你可以把安全的设备排除在翻译之外同时保留IOMMU用于系统自我保护。但如果你直通的是网络设备、存储控制器这类容易受到外部攻击的硬件将它们纳入隔离域可能更稳妥。4.4 直通场景下的额外建议中断重映射必须打开做完IOMMU基础开启后还有一个容易被忽略的配置会决定直通是否稳定——中断重映射。Intel的VT-d包括DMA重映射和中断重映射两部分功能AMD-Vi同样支持。中断重映射不仅关系到MSI/MSI-X中断的正确分发还关系到系统的安全性没有它虚拟化场景下可能出现中断注入攻击即一个虚拟机向其他虚拟机或者宿主机的CPU核发送伪造中断。检查中断重映射是否开启dmesg | grep -i Interrupt Remapping如果没开启内核会有明确的警告提示。这时候需要在BIOS里检查是否还开了“Intel VT-d Interrupt Remapping”这类选项有些主板的BIOS把这一项单独设为DisabledDOMINANT状况下这个坑很容易踩。如果BIOS里没有独立选项也可以通过以下内核参数强制打开intremapon5. 什么时候可以不开IOMMU一个务实的建议讲了很多开IOMMU的好处和操作但不代表所有场景都必须开。如果你满足以下全部条件不开IOMMU也不会出太大问题你的机器只有一个或两个用户使用不存在多租户共享物理资源的场景你从来不插来源不明的PCIe设备比如陌生的扩展卡、二手网卡、来历不明的采集卡你对物理机的安全要求比较低属于家庭自用、普通办公你完全不需要做虚拟化直通、SR-IOV这类高级功能这种情况下IOMMU确实只是一个可选项。实际上我自己的老笔记本到现在也没有开IOMMU纯粹因为我不需要直通功能也懒得验证老BIOS的bug不是不能开是不必要。但如果你搞虚拟化、搞GPU直通、或者机器上插了不少PCIe设备那我的建议非常直接开。开了之后不仅直通稳定性大大提高还能防住不少DMA层面的攻击这笔账怎么算都不会亏。6. 我的实测体会与避坑总结这几点是我在反复折腾IOMMU、VFIO直通和SR-IOV功能时踩出来的经验虽然看似零碎但每个都能省下你几个小时排查时间。第一不要迷信网上的“万能参数组合”。不同内核版本、不同BIOS实现、不同设备组合最优参数不一样。加了一堆用不上的参数反而会在排查时增加干扰项。稳妥的做法是先只用厂商参数intel_iommuon或amd_iommuon确认IOMMU正常运行后再逐步加其他参数。第二排查IOMMU问题先看dmesg不要瞎猜。很多所谓“开启失败”其实只是BIOS中选项未开或者ACPI表没有正确传递。先用dmesg确认DMAR/IVRS有没有被解析再考虑其他方向。第三把/sys/kernel/iommu_groups目录当作“运行时的IOMMU体检报告”。设备越分散在不同group说明IOMMU的隔离粒度越细。如果一块显卡的所有功能音频、USB-C控制器等都被塞在同一个group里直通时必须整个group一起处理否则会被IOMMU的隔离逻辑卡住。第四新内核版本的默认行为在变化。比如一些新内核上英特尔和AMD平台的IOMMU默认策略已经从完全关闭变为仅在使用VFIO设备时自动开启。这导致一个现象你可能没配任何参数但dmesg里能看到IOMMU已经存在。此时再手动加intel_iommuon会改变整个系统的DMA行为。这是很多人升级内核后突然出现问题时没料到的原因。建议每次大版本升级内核后重新检查一下IOMMU的实际工作模式别固守旧参数。最后再说一句IOMMU开启之后如果你使用虚拟机软件直通设备时遇到奇怪的不稳定现象不妨先看一下中断重映射的状态再检查一下设备的IOMMU分组是否合理最后再折腾驱动绑定。按这个顺序排查大部分问题都能在半小时内定位。希望这篇能帮你少走一些弯路顺利把IOMMU跑起来。
返回列表