
1. 先说清楚IOMMU是什么为什么你非要开它干过虚拟化或者玩过显卡直通的兄弟对IOMMU这个词肯定不陌生。简单点说IOMMUInput/Output Memory Management Unit就是硬件层面的内存管理单元专门管外部设备的DMA访问。CPU有MMU管理虚拟内存IOMMU干的是类似的活只不过它管的是PCIe设备、网卡、磁盘控制器这些外设对内存的访问。没开IOMMU的时候设备想要读写内存直接通过DMA操作物理地址操作系统根本没机会介入。这在安全性上是个大坑——万一设备驱动有漏洞攻击者可以借DMA直接读写物理内存轻松绕过所有软件层的保护。开了IOMMU之后设备只能访问IOMMU页表里映射好的地址相当于给设备加了一层“权限门禁”。但大多数人开IOMMU目的不是安全问题而是为了做显卡直通、NVMe直通、SR-IOV这类硬核玩法。没有IOMMU虚拟机里的PCIe设备直通根本跑不起来因为宿主机没法把设备DMA安全地隔离到不同虚拟机。我就是因为要在KVM里直通一张GPU跑深度学习任务才被迫把这套东西彻底吃透的。这篇就结合我踩过的坑把Linux下开启IOMMU的完整方法、原理、常见故障一次说清。不管你是Intel家的VT-d还是AMD家的IOMMU不管是Ubuntu还是CentOS还是国产发行版照着做基本都能成。2. 硬件与固件层面的IOMMU开系统之前先确认BIOS里有没有很多人折腾半天grub参数改了一堆dmesg里就是看不到IOMMU的消息最后发现是BIOS里根本没开虚拟化相关的开关。这一步不是废话真的很多人栽在这。2.1 Intel平台的VT-d与AMD平台的IOMMU到底哪里不同先明确一个容易混淆的点IOMMU是通用叫法Intel平台具体的硬件实现叫VT-dVirtualization Technology for Directed I/OAMD平台叫AMD-Vi在Linux内核里统一用IOMMU子系统来管理。两者的内核参数不一样——Intel要用intel_iommuonAMD要用amd_iommuon这个区分至关重要。如果平台和参数对不上比如Intel的机器填了amd_iommuon那基本等于白填。内核启动时会去检测相应厂商的ACPI DMA Remapping表检测不到就跳过也不报错你的参数就无声无息地失效了。BIOS里的入口也各不相同。常见的位置包括Intel平台Advanced → Virtualization TechnologyVT-x/VT-d或者Advanced → System Agent Configuration → VT-dAMD平台Advanced → SVM Mode这是CPU虚拟化IOMMU开关常写作IOMMU、NB-SB IOMMU、或者Enabled有些主板的VT-d藏在极深的菜单里得把BIOS显示模式调成Advanced模式才看得到。还有少数主板默认VT-d是关闭的单纯开了VT-x是不够的。我的华硕工作站主板就遇到过Intel虚拟化技术显示EnabledVT-d另一个选项默认Disabled把VT-d打开后内核日志里立刻就干净了。2.2 怎么确认BIOS已经正确开启最快的确认方式是进系统后执行dmesg | grep -i -e DMAR -e IOMMU如果BIOS和内核参数都对了你应该能看到类似这样的输出DMAR: IOMMU enabled DMAR: ACPI DMAR table present DMAR: Host address width 46AMD平台也类似有AMD-Vi: IOMMU performance counters supported这类消息。还有一条命令可以用来查硬件层面的支持情况lspci -v | grep -i -e AMD-Vi -e Intel VT-d能看到Kernel driver in use相关的标志说明设备层面没问题。这时候再回到BIOS继续排查别在软件层瞎折腾。3. 系统内开启IOMMUgrub参数与内核启动项配置硬件层面准备好了就该改内核启动参数了。目前主流的Linux发行版都用GRUB2引导Solaris、BSD那些暂时不讨论咱们聚焦Linux。3.1 修改GRUB配置的核心操作步骤先看当前内核参数里有没有IOMMU相关的字眼cat /proc/cmdline假设输出里没有intel_iommuon也没有iommupt那就需要往GRUB的启动参数里加。找到你的GRUB配置主文件。Debian/Ubuntu系通常是/etc/default/grubRHEL/CentOS系也是/etc/default/grub但很多国产发行版用的也是这套方案。打开这个文件找到这一行GRUB_CMDLINE_LINUX_DEFAULTquiet splash改成这样以Intel平台为例GRUB_CMDLINE_LINUX_DEFAULTquiet splash intel_iommuon iommupt如果只想在特定内核启动项生效可以改GRUB_CMDLINE_LINUX那行它是加到所有启动项的包括recovery模式。改完之后Debian系和Ubuntu系执行sudo update-grubRHEL系及部分国产发行版执行sudo grub2-mkconfig -o /boot/grub2/grub.cfg老一点的CentOS 7如果是传统BIOS引导路径是/boot/grub/grub.cfg命令相应变一下。3.2 这些参数到底是什么意思intel_iommuon、amd_iommuon、iommuptintel_iommuon这个参数指定Intel平台启用IOMMU驱动。有的内核发行版默认是off需要显式打开。AMD平台对应的是amd_iommuon注意有些老的AMD主板还需要加iommupt才能正常透传设备。如果两个厂商的参数都填了厂商不符的那个参数会被内核忽略不影响启动。iommupt的意思是Pass-Through模式。没加这个参数时Linux默认用IOMMU做DMA remapping并做设备隔离加了它之后对于那些不必做地址翻译的设备直接以物理地址直通方式映射可以减少IOMMU翻译开销。做虚拟化直通时iommupt几乎是必须的因为它可以最大程度保证透传到VM里的设备能拿到接近原生的DMA性能。这些参数可以叠加组合。以Intel平台为例常规组合是intel_iommuon iommupt如果还要做SR-IOV或者vfio绑定还可以在后面加一些别的参数后面小节会详细说。提示改grub之前建议先备份配置文件。我见过有人参数写错导致系统进不去的时候没法直接改回原样备份一下能省不少事。3.3 不同发行版下的更新差异与EFI引导的特殊情况UEFI引导的机器grub配置文件路径和执行命令跟Legacy BIOS环境还有区别。在UEFI模式下Ubuntu/Debian用sudo update-grub自动更新到/boot/efi/EFI/ubuntu/grub.cfg这和传统路径不同。Rocky/RHEL系的UEFI机器grub2-mkconfig -o /boot/efi/EFI/rocky/grub.cfg这样的命令更常见。建议先执行下面命令查一下实际路径efibootmgr -v有些机器装了多个EFI启动项更新错了路径等于白改。还有一类特殊发行版比如openSUSE用的是update-bootloaderArch系用的是grub-mkconfig -o /boot/grub/grub.cfg。所有发行版的共同逻辑是修改了/etc/default/grub之后必须重建引导配置否则重启后参数无效。4. 验证IOMMU是否真正生效别只看dmesg改完参数重启后一定要验证是否真的生效。这步不能省因为有时候参数看起来加了实际上因为内核或固件问题并没有成功启用。4.1 重启后的三个核心检查命令首先用dmesg查启动日志确认DMAR/IOMMU初始化成功的消息dmesg | grep -i -e DMAR -e IOMMUIntel平台应该能看到DMAR: IOMMU enabled DMAR-IR: Enabled IRQ remappingAMD平台看到类似AMD-Vi: Lazy IO/TLB flushing enabled也行。第二步看内核启动参数是否带上了cat /proc/cmdline第三步看sysfs节点ls /sys/kernel/iommu_groups/如果IOMMU正常工作这里会列出很多分组比如0000:00:00.0这样带域名总线的编号。如果/sys/kernel/iommu_groups是空的或不存在说明IOMMU没起来。4.2 分组查看与设备直通前的准备确认/sys/kernel/iommu_groups/里的分组信息对后续直通极其有用。组内不同的设备是否属于同一个IOMMU group决定了能否单独直通某个设备。有些PCIe设备尤其是老平台会把好几个设备挂在一个group里想做单设备直通是不可能的只能把整组设备都直通给虚拟机。查看某个具体设备的IOMMU group编号readlink /sys/bus/pci/devices/0000:01:00.0/iommu_group输出会告诉你它在哪个group。如果多个设备指向同一个group说明它们共享一个IOMMU domain直通时需要一起处理。从虚拟化直通的最佳实践来说IOMMU group划分越细越好相关设备的隔离度越高。新平台一般都能做到每个PCIe设备一个group老平台差的也有整条总线一个group的。想查自己平台支持度直接在/sys/kernel/iommu_groups/下每个编号目录里看设备列表就行。4.3 装了VFIO之后怎么确认设备绑定成功做GPU直通的话一般还要把设备绑到vfio-pci驱动上此时验证IOMMU是否生效可以额外看dmesg | grep -i vfio如果输出里有例如vfio-pci 0000:01:00.0: enabling device (0100 - 0102)之类的消息说明vfio成功接管了设备。还可以查lspci -nnk -s 01:00.0Kernel driver in use如果显示vfio-pci就说明绑定成功。注意此处01:00.0要替换成你实际的目标设备号。5. 实操记录一次从Intel平台到AMD平台的完整IOMMU开启经历前面说得偏理论我拿自己实际操作的案例走一遍完整流程大家可以直接照着抄。5.1 备机阶段确认硬件型号与固件状态我之前在一台Intel Xeon工作站的机器上做DockerKVM直通实验。主板是华硕的Workstation板子CPU是Xeon W系列。拿到机器后先重启进BIOS找到Advanced菜单下的System Agent Configuration把VT-d从Disabled改成Enabled保存并重启。进系统后我先确认固件有没有解析到DMAR表dmesg | grep -i DMAR如果BIOS没开这条命令大概率什么都不会打印。我开完之后输出有DMAR: ACPI DMAR table present DMAR: IOMMU enabled这说明固件层面已经OK剩下的就是内核参数了。5.2 修改grub参数并更新引导执行sudo cp /etc/default/grub /etc/default/grub.bak sudo vim /etc/default/grub把GRUB_CMDLINE_LINUX_DEFAULT那行改成GRUB_CMDLINE_LINUX_DEFAULTquiet splash intel_iommuon iommupt保存后执行sudo update-grub重启后再次查看cat /proc/cmdline dmesg | grep -i -e DMAR -e IOMMU ls /sys/kernel/iommu_groups/三条命令全部符合预期IOMMU确实开了。5.3 AMD平台对照参数差异与额外注意事项后来我公司那边有一台AMD EPYC的服务器也要做设备直通参数就变成了GRUB_CMDLINE_LINUX_DEFAULTquiet splash amd_iommuon iommupt除了参数名的差异AMD平台还有个常见的坑部分主板BIOS里的IOMMU开关选项位于North Bridge配置里名字不统一有时候显示为SVM或NB-SB IOMMU。如果amd_iommuon不生效优先回BIOS翻这些选项。另外AMD平台如果有多颗CPU注意检查ACPI表的解析是否完整。执行dmesg | grep -i ACPI看看有没有警告如果有ACPI相关的报错可能需要升级主板固件或BIOS到最新版本才能正常启用。5.4 开启失败时同步检查内核模块个别发行版默认没加载vfio相关内核模块IOMMU起了但直通设备时还是会报错。建议确认这几个模块已经加载lsmod | grep -i vfio lsmod | grep -i iommu如果缺少vfio模块手动加载sudo modprobe vfio sudo modprobe vfio-pci想让它们在开机时自动加载可以在/etc/modules文件里添加vfio和vfio-pci或者在/etc/modprobe.d/下新建conf文件配置别名。6. 实战场景解析IOMMU开启后怎么配合KVM做设备直通很多人折腾IOMMU的核心目标就一个把物理设备的全功能透传给虚拟机用。这个场景里IOMMU只是第一关后面的配置链条还长。6.1 把GPU绑定到vfio-pci驱动开启IOMMU后要直通GPU得让它的驱动从nvidia/amdgpu改成vfio-pci。查询设备编号lspci -nn | grep -i nvidia拿到类似01:00.0 VGA compatible controller [0300]: NVIDIA ... [10de:1e04]这样的信息后在/etc/modprobe.d/vfio.conf中写options vfio-pci ids10de:1e04,10de:10f7后面那个是GPU的Audio Function的device ID经常被遗漏。如果不加直通时会寄掉。然后更新initramfssudo update-initramfs -u重启后查看lspci -nnk -s 01:00.0确认驱动是vfio-pci。6.2 virt-manager或命令行创建直通虚拟机在virt-manager里添加PCI设备选到目标GPU直接Assign即可。命令行方式则是在XML配置里加上hostdev modesubsystem typepci managedyes source address domain0x0000 bus0x01 slot0x00 function0x0/ /source /hostdev此时如果IOMMU没开或者vfio没绑对虚拟机启动时通常报Failed to assign device或者Device is not available这就可以回头排查上面几个环节。6.3 直通之外的IOMMU收益安全隔离与性能调优除了直通IOMMU还能做DMA攻击隔离。比如有外接设备接入时经过IOMMU的隔离恶意设备没法直接读全物理内存。服务器上插了不可信的外设时保持IOMMU开启是个很好的安全习惯。性能方面默认开启IOMMU会有少量DMA翻译开销但加了iommupt可以缓解。对于高速网卡、NVMe SSD这种高吞吐设备如果感到性能下降明显可以结合iommupt和适当的队列参数做优化测试。7. 常见问题速查表与实际排查建议我把平时遇到最多的IOMMU相关问题整理成了一张表基本覆盖了90%的坑。现象可能原因排查方法dmesg里完全没有DMAR/IOMMU字样BIOS里VT-d/IOMMU未开启进BIOS确认对应开关找Advanced菜单下相关项dmesg有DMAR但iommu_groups为空内核启动参数未生效检查/proc/cmdline确认参数进入启动项更新grub参数加了但提示Unknown option平台与参数名不匹配Intel用intel_iommuonAMD用amd_iommuon重启后参数被覆盖grub更新未生效或改错文件确认是修改/etc/default/grub后重新mkconfig/update-grub直通设备报Failed to assign devicevfio-pci未绑定设备或IOMMU group限制查lspci -nnk确认驱动查readlink确认分组直通成功后虚拟机内设备性能差缺少iommupt参数在grub参数里追加iommupt更新后重启启动时出现ACPI错误固件表不完整或BIOS较老升级BIOS/主板固件到最新版本系统无法启动误加无效参数进recovery模式/grub启动菜单删掉错误参数恢复备份配置7.1 一个容易被忽视的问题命令行引导模式与Intermix架构有些高端服务器允许多个PCIe Root Complex和独立IOMMU单元。这种平台下不同Root Complex可能对应不同IOMMUiommupt对某些Root生效但对另一些没有完全生效。遇到这种平台建议用iommu.passthrough1这种内核参数替代iommupt试试它是对整个IOMMU子系统全局生效的。iommu.passthrough1另外intel_iommuon还可以配intel_iommuigfx_off来排除内置核显IOMMU避免核显因为IOMMU导致显示异常。需要注意这是针对Intel核显特殊场景的选项一般不直通核显的话不用加。7.2 进入紧急模式救援的实操细节如果参数真的写错了导致起不来重启后在grub菜单按e编辑启动项找到linux开头那一行删掉或改正错误参数按CtrlX启动。进系统后把/etc/default/grub恢复备份并重新生成配置永久修复。7.3 动态运行时开启IOMMU的可能边界有些场景不想重启系统能不能运行时开启内核里IOMMU必须在引导早期初始化绝大多数情况下无法在已经启动的系统里热开启。少数虚拟化平台比如云环境允许在grub里预留好参数但裸金属上基本做不到运行时切换。根据我个人经验真想用IOMMU最靠谱的路径还是引导阶段一次性配好参数重启后验证后续再微调模块和vfio绑定别试图绕过重启这个步骤省不了多少时间还容易把自己整焦虑。8. 最后补充几个我踩坑换来的实用技巧第一个技巧是dmesg日志不一定完整。如果你用的发行版用了systemd-journald可能dmesg输出的内容没有完全对应上一次启动的记录。建议习惯性使用journalctl -b | grep -i -e DMAR -e IOMMU-b表示当前启动查历史启动可以用-b -1、-b -2。我在排查系统启动问题的时候发现dmesg和journalctl的输出在某种情况下有错位用journalctl -b更准确。第二个技巧是核对IOMMU group信息时可以一次性把所有设备列出来#!/bin/bash for g in /sys/kernel/iommu_groups/*; do echo IOMMU Group $(basename $g): ls -l $g/devices/ | grep -oE 0000:[0-9a-f:\.] done这样一眼就能看到哪些设备在同一个group里。做直通规划前先执行一遍能少走很多弯路。第三个技巧是如果用的是国产发行版或者定制内核有的发行版会默认开启strict模式IOMMU对某些设备的兼容性要求会更高。遇到不识别设备的情况可以在grub参数里临时加iommusoft试试。这是一个用软件模拟IOMMU的逻辑兼容性会好一些性能差一点但至少能把系统跑起来再做进一步排查。做IOMMU配置这件事本质上就是一次“硬件固件-引导参数-内核子系统-设备驱动”这四层链路协同的过程。每一层都要确认到位问题定位就很容易。真要说有什么心得那就是每次只改一个参数、重启一次验证一次别图快一次性堆一堆参数进去。出了问题你就搞不清楚是哪个参数在起作用排查起来反而更慢。我一开始就是图省事Intel的、AMD的、iommupt的、vfio的一次全加进去结果系统起不来恢复配置又查了半天最后才明白稳扎稳打才是最快的路径。