Linux 6.6内 IOMMU 深度解析(五):设备挂载 — 从 PCI 枚举到 IOMMU 接管
2026/8/10 8:58:05 网站建设 项目流程

〇、这篇文章在系列中的位置

这篇填补中间的空白:PCI 设备被发现后,IOMMU 怎么接管它——group 怎么分、domain 怎么配、DMA 翻译从何时生效。


一、全景时间线

pci_scan_single_device()
PCI 设备枚举

pci_init_capabilities()
读 ACS Capability

device_add()
触发 BUS_NOTIFY_ADD_DEVICE

iommu_bus_notifier()
IOMMU 核心介入

iommu_probe_device()

ops->device_group()
创建/确定 iommu_group
ACS 决定隔离边界

iommu_setup_default_domain()
分配 + 挂载 default domain

ops->attach_dev()
驱动写 DTE/ContextEntry
DMA 翻译生效

ops->probe_finalize()
安装 iommu_dma_ops
设备 DMA API 可用

关键时序:IOMMU 接管发生在 PCI 驱动绑定之前。设备驱动probe()被调用时,dma_map_single()等 DMA API 已经走 IOMMU 路径了。


二、阶段 1:总线通知 — IOMMU 怎么"发现"新设备

IOMMU 核心在iommu_subsys_init()时注册了一个总线通知链:

// drivers/iommu/iommu.c (v6.6, line 192-229)staticint__initiommu_subsys_init(void){// 注册 bus notifier,监听所有 IOMMU-capable bus 的设备增删事件for(i=0;i<ARRAY_SIZE(iommu_buses);i++)bus_register_notifier(iommu_buses[i],&nb[i]);// iommu_buses[] 包含 &pci_bus_type(CONFIG_PCI)}

当 PCI 设备被pci_device_add()device_add()添加到设备模型时,内核发出BUS_NOTIFY_ADD_DEVICE

// drivers/pci/probe.c (v6.6, line 2539-2583)// pci_device_add():// pci_init_capabilities(dev) // ← ACS Cap 在这里读取// device_add(&dev->dev) // ← 触发 BUS_NOTIFY_ADD_DEVICE

通知回调:

// drivers/iommu/iommu.c (v6.6, line 1793-1808)staticintiommu_bus_notifier(structnotifier_block*nb,unsignedlongaction,void*data){structdevice*dev=data;switch(action){caseBUS_NOTIFY_ADD_DEVICE:returniommu_probe_device(dev);// ★ 新设备caseBUS_NOTIFY_REMOVED_DEVICE:iommu_release_device(dev);// ★ 设备移除}}

补充路径 — 重放(replay):当 IOMMU 驱动注册时(iommu_device_register()),已存在的设备需要补一刀。bus_iommu_probe()遍历总线上所有已注册设备,重新走一遍__iommu_probe_device()


三、阶段 2:iommu_group — 隔离的基本单元

3.1 group 是什么

IOMMU 的硬件隔离以group为单位。一个 group 内的所有设备共享同一个 IOMMU domain(同一套页表),彼此之间没有硬件隔离。不同 group 之间 IOMMU 保证 DMA 隔离。

group 的划分由ops->device_group()回调决定。Intel 和 AMD 都委托给核心的pci_device_group()

// drivers/iommu/intel/iommu.c (v6.6, line 4491)// intel_iommu_device_group() → pci_device_group(dev)// drivers/iommu/amd/iommu.c (v6.6, line 1959)// amd_iommu_device_group() → pci_device_group(dev)

3.2 ACS:决定隔离边界的关键

pci_device_group()的核心逻辑依赖ACS(Access Control Services)——PCIe 的一项能力,控制设备间的 P2P(peer-to-peer)DMA 路由:

// drivers/iommu/iommu.c (v6.6, line 1532)#defineREQ_ACS_FLAGS(PCI_ACS_SV|PCI_ACS_RR|PCI_ACS_CR|PCI_ACS_UF)// Source Request Completion Upstream// Validation Redirect Redirect Forwarding

四项全部启用时,设备间的 P2P DMA 被硬件阻断——它们可以分属不同的 group。

3.3 group 划分的三条规则

无 ACS

有 ACS

PCI Device

① DMA alias?
(PCIe-to-PCI bridge,
SR-IOV VF)

与 alias 源头
同 group

② Multi-function 且
无 ACS?

同 Slot 的兄弟
Function 合并到一个 group

③ 上游 bridge
有 ACS?

与上游 bridge
同 group

独立 group
iommu_group_alloc()

规则③的代码:

// drivers/iommu/iommu.c (v6.6, line 1671-1683)// 向上游遍历 bridge,找到第一个 ACS 能阻断 P2P 的点for_each_pci_bridge_above(dev,bus){if(pci_acs_path_enabled(bus->self,NULL,REQ_ACS_FLAGS))break;// ACS 保护,在此处划界// 否则 bridge 和下游设备必须在同一个 group}

典型场景

场景ACS分组结果
单功能网卡,上游 Root Port 有 ACS独立 group
多功能 NIC(PF+VF),function 间无 ACS同一 group(PF 和 VF 无法隔离)
PCIe-to-PCI bridge 后的传统 PCI 设备所有下游设备 + bridge 同一 group
SR-IOV VF,PF 在另一个 group与 PF 共享 alias group

3.4struct iommu_group(iommu.c:50-66)

structiommu_group{structkobjectkobj;// /sys/kernel/iommu_groups/N/structlist_headdevices;// 该 group 内的 struct group_device 列表structmutexmutex;structiommu_domain*default_domain;// DMA API 使用的 domainstructiommu_domain*blocking_domain;// 阻断 domain(用于 VFIO 切换)structiommu_domain*domain;// 当前挂载的 domainvoid*iommu_data;char*name;intid;// group 编号};

每个 group 在 sysfs 下有对应目录:

/sys/kernel/iommu_groups/0/ ├── devices/ (属于该 group 的设备 symlink) ├── reserved_regions/ (RMRR/unity map 保留区域) └── type (DMA / DMA-FQ / identity)

四、阶段 3:default domain — DMA API 的翻译表

4.1 domain 类型选择

设备 probe 时,IOMMU 核心自动分配一个default domain

// drivers/iommu/iommu.c (v6.6, line 192-209)// iommu_subsys_init():// 若非 passthrough 且非 strict:// iommu_def_domain_type = IOMMU_DOMAIN_DMA_FQ ← 默认 flush-queue 模式// 每个设备可能覆盖:// - 不可信设备 (external GPU, Thunderbolt): 强制 IOMMU_DOMAIN_DMA (无 FQ)// - ops->def_domain_type(): Intel/AMD 驱动级覆盖

三种默认类型:

类型行为适用场景
IOMMU_DOMAIN_DMA严格映射,每次 unmap 立即 invalidate IOTLB不可信设备、调试
IOMMU_DOMAIN_DMA_FQFlush-queue 模式,批量延迟 invalidation默认,吞吐优先
IOMMU_DOMAIN_IDENTITY1:1 直通映射(iommu.passthrough=1性能优先,无隔离

4.2 分配和挂载

// drivers/iommu/iommu.c (v6.6, line 2944-3042)iommu_setup_default_domain(group,target_type)│ ├─iommu_group_alloc_default_domain(group,type)│ └─__iommu_domain_alloc(bus,type)│ └─ ops->domain_alloc(type)// ★ Intel: alloc_domain()// AMD: protection_domain_alloc()│ └─__iommu_group_set_domain_internal(group,new_domain)└─foreach device in group:└─__iommu_attach_device(domain,dev)└─ domain->ops->attach_dev(domain,dev)// ★ 写硬件

4.3 Intel 驱动侧:attach_dev做了什么

// drivers/iommu/intel/iommu.c (v6.6, line 4107)// intel_iommu_attach_device()// → prepare_domain_attach_device() // 检查 AGAW/页表级别兼容性// → domain_context_mapping() // ★ 写 Context Entry:// ① 在该设备对应的 Context Entry 中填入:// - Present = 1// - DomainID = domain->did// - SLPTPTR = page table root (SL)// 或 PASIDDIR = PASID table (FL)// ② iommu_flush_write_buffer()// ③ iommu_flush_context() // Invalidate Context Cache

4.4 AMD 驱动侧:attach_dev做了什么

// drivers/iommu/amd/iommu.c (v6.6, line 2200)// amd_iommu_attach_device()// → attach_device(dev, domain)// → 填 DTE:// V=1, DomainID, RootPtr, Mode, IR/IW, TV// → iommu_flush_device_table() // Invalidate Device Table Cache// → iommu_completion_wait() // 等 CmdBuf 执行完毕

五、阶段 4:probe_finalize — DMA API 正式上线

attach 完成后,设备还需要安装 DMA opsdma_map_single()等 API 才能走 IOMMU 路径:

// drivers/iommu/iommu.c (v6.6, line 565-577)intiommu_probe_device(structdevice*dev){ret=__iommu_probe_device(dev,NULL);// group + domain + attachif(ret)returnret;ops->probe_finalize(dev);// ★ 安装 DMA ops}

Intel 和 AMD 的probe_finalize逻辑相同:

// 例: drivers/iommu/amd/iommu.c (v6.6, line 1937)staticvoidamd_iommu_probe_finalize(structdevice*dev){set_dma_ops(dev,NULL);// 清掉旧的 DMA ops(可能来自 swiotlb)iommu_setup_dma_ops(dev,0,U64_MAX);// 安装 iommu_dma_ops}// drivers/iommu/dma-iommu.c (v6.6, line 1630)// iommu_setup_dma_ops():// → iommu_dma_init_domain() // 初始化 IOVA 空间// → dev->dma_ops = &iommu_dma_ops // 替换 DMA ops

从此刻起,设备驱动调dma_map_single()iommu_dma_map_page()iommu_map()→ 驱动map_pages回调 → 硬件页表更新。


六、完整调用链

pci_scan_single_device() └─ pci_device_add() // drivers/pci/probe.c:2539 ├─ pci_init_capabilities() // read ACS cap (probe.c:2484) └─ device_add(&dev->dev) // probe.c:2583 └─ bus_notify(BUS_NOTIFY_ADD_DEVICE) // drivers/base/core.c:3604 └─ iommu_bus_notifier() // iommu.c:1793 └─ iommu_probe_device() // iommu.c:565 ├─ __iommu_probe_device() │ ├─ iommu_init_device() │ │ ├─ ops->probe_device(dev) // 驱动: 分配 device_domain_info / iommu_dev_data │ │ ├─ iommu_device_link() │ │ └─ ops->device_group(dev) // → pci_device_group() │ │ ├─ pci_acs_path_enabled() // ACS 划界 │ │ ├─ get_pci_function_alias_group() // 多功能合并 │ │ └─ iommu_group_alloc() // 新 group │ │ │ ├─ iommu_setup_default_domain() │ │ ├─ __iommu_domain_alloc() // → ops->domain_alloc() │ │ └─ __iommu_group_set_domain_internal() │ │ └─ __iommu_attach_device() │ │ └─ ops->attach_dev() // ★ 写 ContextEntry / DTE │ │ │ └─ iommu_dma_set_pci_32bit_workaround() │ └─ ops->probe_finalize(dev) └─ iommu_setup_dma_ops(dev) // 安装 iommu_dma_ops pci_bus_add_device() // drivers/pci/bus.c:334 └─ device_attach(&dev->dev) // PCI 驱动 probe (此时 iommu 已就绪)

七、记住三点

① 时序:IOMMU 在设备驱动 probe之前接管。驱动probe()调 DMA API 时 IOMMU 已配好。

② 隔离:group 是 IOMMU 隔离的基本单元,ACS 是划分 group 边界的硬件依据。同一 group 内的设备共享页表,无硬件隔离。

③ 挂载probe_device → device_group → alloc_domain → attach_dev → probe_finalize,五步走完,DMA 翻译生效。


八、关键文件索引

文件(v6.6)关键函数/结构
drivers/iommu/iommu.ciommu_bus_notifier,iommu_probe_device,__iommu_probe_device,pci_device_group,iommu_setup_default_domain,struct iommu_group
drivers/iommu/intel/iommu.cintel_iommu_probe_device,intel_iommu_attach_device,domain_context_mapping
drivers/iommu/amd/iommu.camd_iommu_probe_device,amd_iommu_attach_device,attach_device
drivers/iommu/dma-iommu.ciommu_setup_dma_ops,iommu_dma_ops
drivers/pci/probe.cpci_device_add,pci_init_capabilities
drivers/pci/pci.cpci_acs_enabled,pci_acs_path_enabled,pci_acs_init
include/linux/iommu.hstruct iommu_ops,struct iommu_domain,struct dev_iommu

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询