第 12 篇 dma-mapping 的分层架构:从映射接口到 dma_map_ops

发布时间:2026/10/22 2:12:20

第 12 篇 dma-mapping 的分层架构:从映射接口到 dma_map_ops 本文属于第三册“内核抽象dma-mapping 接口的设计原则”回答第 0 篇提出的问题 12一次 DMA 映射调用如何被分发至 direct、iommu、swiotlb 等不同后端第 8 至 11 篇讨论的是驱动如何使用 dma-mapping 接口映射契约的选择、所有权的交接、分散内存的表示与寻址能力的协商立足点都是驱动可见的 API。这些调用在驱动看来形式各异进入内核后却会收敛到同一套分发与后端代码。本篇转入接口背后的实现说明一次映射调用从驱动入口到最终执行要经过哪些层次以及内核据以在不同后端之间分发的依据。本文首先给出 dma-mapping 子系统的分层结构然后分析分发层选择后端的判断条件以及dma_map_ops在当前内核中的地位接着说明这些判断条件在设备初始化的哪些阶段建立最后讨论 SWIOTLB 在这一结构中的位置。1. 问题回顾前几篇多次出现同一段分发代码第 6 篇 §4 给出了dma_map_phys()选择直接映射或 IOMMU 的片段第 8 篇 §3.2、第 9 篇 §4.2 与第 10 篇 §4 分别给出了分配、同步与 scatterlist 映射的分发。第 11 篇则表明SWIOTLB 中转既可能发生在直接映射路径上也可能发生在 IOMMU 路径上。这些片段说明了“分发”的存在但没有回答以下问题驱动调用的接口与具体实现之间有几个层次分发依据存储在何处在何时设置dma_map_ops与直接映射、IOMMU 是什么关系SWIOTLB 是否是一个独立的后端。2. 分层结构图 1 DMA 驱动的分层实现结构2.1 接口层驱动使用的接口定义在include/linux/dma-mapping.h中多为内联函数作用是将不同形式的参数统一为物理地址staticinlinedma_addr_tdma_map_single_attrs(structdevice*dev,void*ptr,size_tsize,enumdma_data_directiondir,unsignedlongattrs){if(dev_WARN_ONCE(dev,is_vmalloc_addr(ptr),rejecting DMA map of vmalloc memory\n))returnDMA_MAPPING_ERROR;debug_dma_map_single(dev,ptr,size);returndma_map_page_attrs(dev,virt_to_page(ptr),offset_in_page(ptr),size,dir,attrs);}dma_map_single()将内核虚拟地址转换为页与页内偏移dma_map_page_attrs()再将其转换为物理地址最终都进入dma_map_phys()。scatterlist、同步与分配接口也各自汇聚到少数几个入口函数。调用形式的多样性由此被吸收在接口层无论驱动传入的是虚拟地址、页加偏移还是 scatterlist分发层与后端只需面对物理地址这一种输入不必为每种调用形式各写一套处理逻辑。接口层只做“参数形式到物理地址”的轻量换算将物理地址翻译为设备可用的 DMA 地址则留给后端§2.3。2.2 分发层入口函数位于kernel/dma/mapping.c承担三项工作检查参数例如方向是否合法、设备是否设置了掩码选择后端调用与后端无关的公共钩子包括CONFIG_DMA_API_DEBUG的检查、跟踪点与 KMSAN 的标记。公共钩子集中在分发层因此无论设备使用哪个后端调试检查都同样生效。2.3 后端层后端负责将物理地址转换为设备可用的 DMA 地址并完成必要的 cache 维护。当前内核有三类后端dma-directkernel/dma/direct.cDMA 地址由物理地址按固定规则换算得到用于未启用 IOMMU 或 IOMMU 处于直通模式的设备。iommu-dmadrivers/iommu/dma-iommu.c分配 IOVA 并建立 I/O 页表用于挂接到 DMA 类型 domain 的设备。dma_map_ops的实现由体系结构或虚拟化平台提供的一组回调用于前两者无法覆盖的场景。2.4 辅助设施SWIOTLB、CMA 与原子内存池、设备专用的一致性内存区域、体系结构的 cache 维护函数以及 IOMMU 驱动都位于后端之下由后端按需调用分发层并不直接选择它们。3. 后端的选择3.1 判断条件dma_map_phys()的分发部分如下staticbooldma_go_direct(structdevice*dev,dma_addr_tmask,conststructdma_map_ops*ops){if(use_dma_iommu(dev))returnfalse;if(likely(!ops))returntrue;#ifdefCONFIG_DMA_OPS_BYPASSif(dev-dma_ops_bypass)returnmin_not_zero(mask,dev-bus_dma_limit)dma_direct_get_required_mask(dev);#endifreturnfalse;}...if(dma_map_direct(dev,ops)||...)addrdma_direct_map_phys(dev,phys,size,dir,attrs,true);...elseif(use_dma_iommu(dev))addriommu_dma_map_phys(dev,phys,size,dir,attrs);elseif(ops-map_phys)addrops-map_phys(dev,phys,size,dir,attrs);图 2 后端选择逻辑判断依据是struct device中的三项内容dev-dma_iommu为真时使用 iommu-dma否则get_dma_ops()返回空时使用 dma-direct返回非空时使用其回调但若 ops 设置了dma_ops_bypass且掩码足以覆盖全部内存仍使用 dma-direct。dma_map_direct()与dma_alloc_direct()分别以流式映射掩码与一致性分配掩码调用dma_go_direct()因此同一设备的映射与分配可能走不同的后端。get_dma_ops()的实现取决于配置#ifdefCONFIG_ARCH_HAS_DMA_OPSstaticinlineconststructdma_map_ops*get_dma_ops(structdevice*dev){if(dev-dma_ops)returndev-dma_ops;returnget_arch_dma_ops();}#elsestaticinlineconststructdma_map_ops*get_dma_ops(structdevice*dev){returnNULL;}#endifinclude/linux/dma-map-ops.h节选未选择CONFIG_ARCH_HAS_DMA_OPS的配置中get_dma_ops()恒为空编译器可以删除 ops 分支分发只剩直接映射与 IOMMU 两条路径。x86 仅在启用 AMD GART 或 Xen 时选择该选项此时以全局变量dma_ops作为体系结构默认值。3.2dma_map_ops的地位struct dma_map_ops包含分配、映射、scatterlist、同步与能力查询等回调与驱动可见的接口一一对应。它曾是所有非直接映射路径的统一抽象iommu-dma 也曾以一组dma_map_ops的形式接入。当前内核将 iommu-dma 改为由dev-dma_iommu标志选择、由分发层直接调用与 dma-direct 的处理方式一致。这一改动消除了 IOMMU 路径上的间接调用开销也使许多常见配置完全不再需要dma_map_ops。因此dma_map_ops现在只用于前两类后端无法覆盖的场景在本文所用的内核树中实现主要有以下几类类别例子用途虚拟化xen_swiotlb_dma_ops、xen_grant_dma_opsXen 客户机的物理地址不等于机器地址或须经授权表共享内存早期 IOMMUx86gart_dma_opsPowerPC、SPARC、PA-RISC、Alpha 的 IOMMU尚未迁移到通用 IOMMU 框架的平台特殊用途dma_dummy_ops固件声明设备不支持 DMA 时使所有映射失败驱动不应直接使用dma_map_ops头文件dma-map-ops.h开头即说明它只供后端实现及相关代码包含。3.3 绕过 ops部分平台的 IOMMU 可以为设备提供覆盖全部物理内存的直通窗口或固定映射掩码足够宽的设备因而可以绕过 IOMMU 的逐次映射。PowerPC 的dma_iommu_dma_supported()在驱动设置掩码时检查这一能力满足时将dev-dma_ops_bypass置为真此后该设备的映射走 dma-direct。这说明dma_set_mask()的结果可以改变分发路径第 11 篇 §2.2 中dma_set_mask()末尾调用dma_setup_need_sync()正是为了按新的路径重新判断同步能否跳过。4. 分发依据的建立图 3 判断分发依据分发依据在驱动开始映射之前已经确定各项在以下阶段设置IOMMU 探测设备时。IOMMU 核心为设备所在的组确定默认 domain 后调用voidiommu_setup_dma_ops(structdevice*dev,structiommu_domain*domain){if(dev_is_pci(dev))dev-iommu-pci_32bit_workaround!iommu_dma_forcedac;dev-dma_iommuiommu_is_dma_domain(domain);if(dev-dma_iommuiommu_dma_init_domain(domain,dev))gotoout_err;return;out_err:pr_warn(Failed to set up IOMMU for device %s; retaining platform DMA ops\n,dev_name(dev));dev-dma_iommufalse;}只有 DMA 与 DMA-FQ 类型的 domain 使dma_iommu为真IDENTITY 类型第 6 篇 §5.1使其为假设备随即走直接映射。初始化失败时同样回退到直接映射或平台的 ops。驱动绑定之前。驱动核心在调用驱动的 probe 函数之前调用总线的dma_configure回调。PCI 总线的实现pci_dma_configure()按主桥的固件描述调用of_dma_configure()或acpi_dma_configure()后者处理 IOMMU 的关联并调用arch_setup_dma_ops()记录设备是否一致arm64 在此为 Xen 客户机安装 Xen 的 ops。ACPI 声明设备不支持 DMA 时设备被设置为dma_dummy_ops。驱动 probe 时。驱动设置掩码ops 可能借此设置绕过标志§3.3dma_setup_need_sync()按最终路径设置dma_skip_sync。映射与解除映射必须走同一后端否则解除映射时无法找到对应的 IOVA 或 SWIOTLB 槽位。因此分发依据在驱动工作期间不应改变。管理员可以通过/sys/kernel/iommu_groups/n/type修改组的默认 domain 类型但除了从 DMA 改为 DMA-FQ 以外内核要求组内设备均未绑定驱动修改后再对各设备重新调用iommu_setup_dma_ops()。5. SWIOTLB 的位置问题 12 将 swiotlb 与 direct、iommu 并列但在分发层面SWIOTLB 并不是一个独立的后端dma-direct 在地址超出掩码、平台强制中转或 kmalloc 缓冲区不满足 cache 行对齐要求时调用swiotlb_map()iommu-dma 在不受信任的设备或需要对齐的 kmalloc 小缓冲区映射未对齐 IOMMU 页的区域时调用iommu_dma_map_swiotlb()Xen 的xen_swiotlb_dma_ops是一组以 SWIOTLB 为核心的 ops但它作为dma_map_ops被选择而非因为 SWIOTLB。if(dev_use_swiotlb(dev,size,dir)iova_unaligned(iovad,phys,size)){...physiommu_dma_map_swiotlb(dev,phys,size,dir,attrs);...}...iova__iommu_dma_map(dev,phys,size,prot,dma_mask);将 SWIOTLB 设计为后端可调用的设施而不是与之并列的后端原因在于中转与地址翻译相互独立数据先复制到中转缓冲区中转缓冲区的地址再按后端的规则转换为 DMA 地址可以直接使用物理地址也可以再经 IOMMU 翻译。若作为独立后端就无法与 IOMMU 组合使用。6. 常见误区6.1 认为启用 IOMMU 硬件后所有设备都走 iommu-dma决定后端的是设备所在组的默认 domain 类型而非 IOMMU 是否存在。以iommu.passthrough1或iommupt启动或将组设为 IDENTITY 时设备走直接映射VFIO 等声明driver_managed_dma的驱动绑定后pci_dma_configure()不再为设备启用默认 domain设备的地址空间由驱动自行管理。6.2 认为dma_map_ops是所有 DMA 映射的必经之路在 x86 与 arm64 的常见配置中设备通常没有 ops映射由分发层直接调用 dma-direct 或 iommu-dma。以 ops 作为分析的起点会在这些平台上找不到实际执行的代码。6.3 在驱动中直接调用后端函数dma_direct_map_phys()、iommu_dma_map_phys()等函数假设调用者已经完成路径选择绕过分发层也会绕过调试检查与跟踪点。驱动应只使用dma-mapping.h中的接口需要了解后端的能力时使用dma_get_merge_boundary()、dma_max_mapping_size()等查询接口。7. 小结本文回答了问题 12要点如下dma-mapping 分为四层。接口层将参数统一为物理地址分发层检查参数、选择后端并调用公共钩子后端层由 dma-direct、iommu-dma 与dma_map_ops的实现组成SWIOTLB、CMA、cache 维护与 IOMMU 驱动作为辅助设施由后端调用。分发依据存储在struct device中。dma_iommu为真时使用 iommu-dma否则无 ops 时使用 dma-direct有 ops 时调用其回调设置了绕过标志且掩码足够宽时仍使用 dma-direct。dma_map_ops已退居次要地位。iommu-dma 改为由分发层直接调用ops 主要服务于 Xen 与尚未迁移的早期 IOMMU。分发依据在驱动开始映射之前确定。IOMMU 探测、总线的dma_configure与驱动设置掩码依次建立这些字段映射与解除映射必须走同一后端因此驱动工作期间不改变。SWIOTLB 是辅助设施而非后端。它由 dma-direct 与 iommu-dma 按需调用因而可以与地址翻译组合使用。至此第三册讨论的都是单个驱动与单个设备之间的映射。相机采集的帧需要交给 GPU 处理、再交给编码器压缩时同一块内存须同时被多个驱动映射给各自的设备。不同设备如何以零拷贝方式共享同一块内存即为问题 13。系列导航上一篇《第 11 篇 DMA 掩码与寻址能力协商dma_set_mask与 SWIOTLB》下一篇《第 13 篇 DMA-BUF跨驱动、跨设备的缓冲区共享》
延伸阅读

更多相关文章

2026/10/22 2:12:20

上图:2014年10月阿拉斯加费尔班克斯附近冰冻湖泊的航拍照片

ABoVE: Aerial Photographs of Frozen Lakes near Fairbanks, Alaska, October 2014简介本数据集包含美国阿拉斯加州费尔班克斯地区21个湖泊的高分辨率正射影像。航拍照片拍摄于2014年10月8日,即湖冰形成三天后。这些照片用于识别湖冰上的孔洞,这些孔洞指…

2026/10/22 2:12:20

vLLM 开源大语言模型推理服务引擎深度解析

vLLM 是目前最流行的高吞吐大语言模型(LLM)推理服务引擎之一,核心创新 PagedAttention 解决了 KV Cache 显存碎片化问题,配合连续批处理(Continuous Batching)把 GPU 利用率拉到接近上限,并通过…

2026/10/22 2:12:20

怕 AI Agent 乱删文件?Tank-OS 把它锁进不可变系统

我最近在琢磨一件事:公司里要是铺开一堆 AI Agent(比如 OpenClaw),它们能读邮件、改文件、调 API,真出事了谁来兜?翻了下最近的资料,发现 Red Hat 一位工程师 Sally OMalley 已经把这个痛点做成…

2026/10/22 3:27:32

Kubernetes集群参数调优实战:解决Pod反复重启与驱逐问题

不少维护自建集群的同行都有过这种体验:集群规模不算大,资源水位看着也正常,可一到流量高峰期,Pod就开始以一种十分“随机”的姿态反复重建、Pending、ContainerCreating。这种时候,K8s参数优化往往才是真正要做的功课…

2026/10/22 3:27:32

VB.NET连接MySQL导入CSV的完整避坑指南

简介:本资源是一份面向VB.NET初学者与数据迁移开发者的实战型代码示例包,聚焦解决Windows平台下连接MySQL数据库并批量导入CSV文件的核心需求,适用于教学演示、小型数据处理系统开发及数据库对接实训场景。压缩包共29个文件,含6个…

2026/10/22 3:27:32

零基础搭建Deep Agents:从任务拆解到工具调用的最小方案

这段时间我一直在折腾怎么把手头那些重复性的事情交给程序去做,最后整理出来一套Deep Agents的最小可运行方案。说实话,刚开始我也以为这类“自主智能体”门槛很高,要被框架、状态机、Token 计算这些概念围殴一圈,结果真上手之后发…

2026/10/22 3:27:32

学生信息管理系统:生产级数据一致性与字段级权限实践

简介:这是一套面向计算机专业初学者与课程设计学生的C学生信息管理系统实现源码,聚焦于多学段(小学、中学、大学)学生数据建模与基础CRUD统计功能开发。系统支持按学段动态扩展字段:小学生含语数英三科成绩&#xff0c…

2026/10/22 3:27:32

公共服务AI落地难?6个提示工程技巧让Agent更可靠

公共服务AI落地难?这类问题我这两年听到的频率越来越高。很多团队拿着很强的基座模型,在内部Demo里跑得挺顺,一进真实业务场景就各种翻车——回答不准确、流程走不通、责任没人敢担、用户不买账。问题往往不在模型本身,而在于怎么…

2026/10/22 3:22:31

SpringBoot+Vue前后端分离学生评教系统设计与实现

从零搭一套高校学生评教系统,SpringBootVue前后端分离,该怎么做?这篇文章从我实际开发的一套模拟项目出发,把整个设计思路、表结构、核心接口、权限控制、前端实现到踩坑记录全都捋一遍,新手可以直接照着路子走。作为一…

2026/10/21 4:53:08

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/22 0:01:49

电商智能运营体系从0到1:架构设计、落地步骤与避坑指南

前两年我们团队接了一个电商平台的运营中台项目,目标很直白:把过去靠运营拍脑袋做的选品、定价、发券、推活动这些事,逐步用数据算法和自动化流程代替。项目名字内部就叫“电商智能运营体系”。最近有不少朋友问我当时是怎么设计的、踩过哪些…

2026/10/22 0:01:49

若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定

最近一段时间,我一直想把后台管理系统里那些机械重复的增删改查代码交给AI去写。框架用的是若依,按理说这类业务代码模式非常固定,应该是AI最擅长的事情。可真试下来却不是那么回事——你让AI生成一个用户管理模块,它给出的Contro…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑