附录 B DMA 接口汇总表

本附录按调用环节汇总专栏中讨论过的 DMA 相关接口,范围涵盖内核 dma-mapping 接口、散列表、dma-buf 与 P2PDMA、用户页面与 HMM、IOMMU 内核接口,以及 VFIO、iommufd、io_uring 与 RDMA verbs 等用户态接口。接口名称以本专栏所依据的内核源码树为准;成对出现的接口(映射与解除映射、分配与释放)合并为一行。接口在不同内核版本中的签名可能变化,使用时应以目标内核的头文件为准。

一、设备寻址能力声明

驱动在探测阶段向 dma-mapping 层声明设备的寻址能力与分段约束,后续的分配与映射均以此为依据。

接口 作用与要点
dma_set_mask() / dma_set_coherent_mask() 分别设置流式映射与一致性分配所用的 DMA 地址掩码;平台无法满足时返回错误,驱动应回退到更窄的掩码
dma_set_mask_and_coherent() 同时设置上述两种掩码,是多数驱动的常用写法
dma_get_required_mask() 返回覆盖系统全部内存所需的掩码,供驱动判断是否有必要启用更宽的寻址模式
dma_addressing_limited() 判断设备掩码能否覆盖全部内存;返回真表示部分映射可能经由 swiotlb 反弹
dma_set_max_seg_size() 声明设备单个 DMA 段的最大长度,约束散列表合并的结果
dma_set_seg_boundary() 声明 DMA 段不得跨越的地址边界
dma_set_min_align_mask() 要求 swiotlb 反弹时保留原地址在该掩码范围内的低位偏移
dma_max_mapping_size() 返回单次映射允许的最大长度;启用 swiotlb 时受反弹缓冲区段长限制
dma_opt_mapping_size() 返回不致明显降低映射效率的建议长度,供驱动确定传输粒度
dma_get_merge_boundary() 返回 IOMMU 能够合并相邻段的边界粒度;返回 0 表示不支持合并

二、一致性分配

一致性缓冲区在其生命周期内由 CPU 与设备同时访问,无需显式同步,适用于描述符环、完成队列等小数据量、且生命周期较长的控制结构。

接口 作用与要点
dma_alloc_coherent() / dma_free_coherent() 分配一致性缓冲区,同时返回 CPU 虚拟地址与 DMA 地址;在非一致平台上通常以不可缓存映射实现
dma_alloc_attrs() / dma_free_attrs() 带属性的通用分配接口,dma_alloc_coherent() 是其不带属性的形式
dmam_alloc_coherent() 托管版本,驱动解除绑定时自动释放
dma_mmap_attrs() 把一致性缓冲区映射到用户态地址空间
dma_pool_create() / dma_pool_alloc() / dma_pool_free() / dma_pool_destroy() 在一致性内存上切分固定大小的小块,可指定对齐与不得跨越的边界,适用于大量小型描述符

三、非一致与非连续分配

此类接口分配的内存可被 CPU 缓存访问,所有权交接须由驱动显式同步。

接口 作用与要点
dma_alloc_noncoherent() / dma_free_noncoherent() 分配可缓存的 DMA 缓冲区,返回 CPU 虚拟地址;访问前后须调用 dma_sync_single_for_*()
dma_alloc_pages() / dma_free_pages() 与上一行语义相同,但返回 struct page,便于纳入页面管理
dma_alloc_noncontiguous() / dma_free_noncontiguous() 分配物理上可不连续的内存,以 sg_table 返回;IOMMU 处于翻译模式时可获得连续的 IOVA

四、流式映射

流式映射把已有的内核内存临时交给设备访问,映射期间缓冲区归设备所有,结束后必须解除映射。

接口 作用与要点
dma_map_single() / dma_unmap_single() 映射一段线性映射区内的内核虚拟地址;不适用于 vmalloc 地址
dma_map_page() / dma_unmap_page() 以页面与页内偏移指定缓冲区,可映射高端内存页
dma_map_phys() / dma_unmap_phys() 以物理地址指定缓冲区,是流式映射的统一入口,其余单段映射接口均可归结到此;配合 DMA_ATTR_MMIO 可映射没有 struct page 的 MMIO 区域
dma_map_resource() / dma_unmap_resource() 映射设备 MMIO 物理地址,用于设备访问另一设备的 BAR,不执行 cache 维护
dma_mapping_error() 检查映射返回的 DMA 地址是否有效;每次单段映射后都必须调用

五、散列表

散列表描述由多段内存组成的缓冲区。映射后的 DMA 段数可能少于原始段数,遍历时须使用映射后的段数。

接口 作用与要点
sg_alloc_table() 分配并初始化 sg_table
dma_map_sg() / dma_unmap_sg() 映射散列表,返回映射后的 DMA 段数,失败时返回 0;解除映射时须传入映射前的原始段数
dma_map_sgtable() / dma_unmap_sgtable() 以 sg_table 为参数的版本,成功返回 0、失败返回负的错误码,并把映射后的段数记录在表中,可避免两种段数混用
for_each_sg() / for_each_sgtable_dma_sg() 遍历散列表;后者按映射后的 DMA 段遍历
sg_dma_address() / sg_dma_len() 读取段的 DMA 地址与长度,二者只在映射后有效,不可与 CPU 侧的页面与长度混用

六、同步与所有权交接

流式映射与非一致分配的缓冲区在 CPU 与设备之间交接所有权时,须调用同步接口完成必要的 cache 维护或 swiotlb 拷贝。

接口 作用与要点
dma_sync_single_for_cpu() / dma_sync_single_for_device() 把单段缓冲区的所有权交还 CPU 或交给设备
dma_sync_single_range_for_cpu() / dma_sync_single_range_for_device() 只同步映射中的一个子区间,用于接收缓冲区按实际长度同步
dma_sync_sg_for_cpu() / dma_sync_sg_for_device() 散列表版本
dma_sync_sgtable_for_cpu() / dma_sync_sgtable_for_device() 以 sg_table 为参数的散列表版本
dma_need_sync() 判断某一映射是否需要同步;返回假时驱动可在热路径上省略同步调用
page_pool 的 PP_FLAG_DMA_MAP / PP_FLAG_DMA_SYNC_DEV 由 page_pool 代为完成页面的 DMA 映射,以及页面回收时交给设备的同步,使映射在页面循环使用期间保持有效

七、两步 IOVA 映射

两步接口把 IOVA 分配与页表填充分开,使多段物理内存映射到一段连续 IOVA,并只做一次 IOTLB 同步。

接口 作用与要点
dma_iova_try_alloc() 预分配一段 IOVA;设备不经 IOMMU 翻译时返回假,调用者应回退到逐段流式映射
dma_iova_link() / dma_iova_unlink() 把一段物理内存映射到已分配 IOVA 的指定偏移,或解除该映射
dma_iova_sync() 对已建立的全部链接一次完成 IOMMU 映射同步
dma_use_iova() 判断当前状态是否使用了两步 IOVA 映射,供解除映射路径选择对应的处理

八、方向与属性

接口 作用与要点
DMA_TO_DEVICE / DMA_FROM_DEVICE / DMA_BIDIRECTIONAL 数据流向,决定映射与同步时执行写回还是失效;映射与同步、解除映射须使用相同方向
DMA_ATTR_SKIP_CPU_SYNC 映射或解除映射时跳过 cache 维护,由驱动在合适时机自行同步
DMA_ATTR_WEAK_ORDERING 允许平台对该映射的访问放宽排序,在 PCIe 上对应宽松排序
DMA_ATTR_WRITE_COMBINE 一致性分配时以写合并属性建立 CPU 映射
DMA_ATTR_NO_KERNEL_MAPPING 一致性分配时不建立内核虚拟映射,适用于 CPU 不访问的大缓冲区
DMA_ATTR_FORCE_CONTIGUOUS 要求分配物理连续的内存
DMA_ATTR_NO_WARN 分配或映射失败时不输出告警,适用于设有回退路径的调用
DMA_ATTR_MMIO 表明被映射的物理地址不是普通系统内存(通常为 MMIO),dma-mapping 层不对其执行 cache 维护;用于点对点映射,PCI 设备须先经 P2PDMA 接口判定是否适用

九、内存屏障

接口 作用与要点
dma_wmb() 保证 CPU 对一致性内存的写入先于后续写入对设备可见,典型用法是在置位描述符所有权位之前
dma_rmb() 保证读到设备写回的状态位之后,再读取同一描述符的其余字段

十、跨设备共享与点对点

接口 作用与要点
dma_buf_export() / dma_buf_fd() 导出方把缓冲区封装为 dma-buf,并取得可交给用户态的文件描述符
dma_buf_get() / dma_buf_put() 导入方由文件描述符取得 dma-buf 引用,或释放该引用
dma_buf_attach() / dma_buf_detach() 静态导入方挂接到 dma-buf;映射期间缓冲区保持固定
dma_buf_dynamic_attach() 动态导入方挂接,提供 dma_buf_attach_ops,在缓冲区即将移动时经 invalidate_mappings() 接收通知
dma_buf_map_attachment() / dma_buf_unmap_attachment() 取得导入设备视角下的 sg_table,其中的 DMA 地址由导出方按导入设备生成
dma_buf_pin() / dma_buf_unpin() 动态导入方固定缓冲区,使导出方在解除固定之前不再移动它;仅限扫描输出等有限场景
dma_buf_begin_cpu_access() / dma_buf_end_cpu_access() 界定 CPU 访问 dma-buf 的区间,由导出方完成相应的 cache 维护
dma_buf_vmap() / dma_buf_vunmap() 取得 dma-buf 的内核虚拟映射
pci_p2pdma_distance() 计算 P2P 内存提供方与各访问方之间的拓扑距离;返回负值表示不支持点对点传输

十一、用户页面与设备页表镜像

接口 作用与要点
pin_user_pages() / pin_user_pages_fast() / pin_user_pages_remote() 固定用户态页面供 DMA 访问;长期固定须带 FOLL_LONGTERM
hmm_range_fault() 读取进程页表在指定区间的快照,必要时触发缺页,供设备页表镜像使用
mmu_interval_notifier_insert() / mmu_interval_read_begin() / mmu_interval_read_retry() 注册区间失效通知,并以序列号检测快照期间是否发生了页表变化
migrate_vma_setup() / migrate_vma_pages() / migrate_vma_finalize() 在系统内存与设备私有内存之间分三步迁移页面

十二、IOMMU 内核接口

接口 作用与要点
iommu_map() / iommu_unmap() 在指定 IOMMU 域中建立或解除 IOVA 到物理地址的映射,由 dma-iommu 层或 VFIO 等直接管理域的使用者调用
iommu_sva_bind_device() / iommu_sva_unbind_device() 把设备绑定到进程地址空间,使设备以进程虚拟地址发起 DMA
iommu_sva_get_pasid() 取得绑定对应的 PASID,由驱动写入设备的工作描述符

十三、用户态接口

接口 作用与要点
VFIO_GROUP_SET_CONTAINER 传统 VFIO 接口中把 IOMMU group 加入容器
VFIO_IOMMU_MAP_DMA / VFIO_IOMMU_UNMAP_DMA 在容器的 IOMMU 域中建立或解除用户态虚拟地址到 IOVA 的映射,内核同时固定对应页面
VFIO_DEVICE_BIND_IOMMUFD 把 VFIO 设备文件绑定到 iommufd
IOMMU_IOAS_MAP iommufd 中在 I/O 地址空间对象内建立映射
IORING_REGISTER_BUFFERS 向 io_uring 预先注册缓冲区,页面固定一次,避免每次 I/O 重复固定
IORING_OP_SEND_ZC 零拷贝发送;第一个完成事件只表示数据已交给协议栈,缓冲区须在收到带 IORING_CQE_F_NOTIF 标志的通知之后才能复用
IORING_REGISTER_ZCRX_IFQ 注册零拷贝接收所用的网络接口队列与内存区域
ibv_reg_mr() 注册 RDMA 内存区域,固定页面并取得本地与远端访问密钥
ibv_reg_dmabuf_mr() 以 dma-buf 文件描述符注册内存区域,用于网卡直接访问 GPU 显存
ibv_post_send() / ibv_post_recv() 向队列对提交发送或接收工作请求

十四、典型调用顺序

  • 一致性缓冲区:dma_set_mask_and_coherent() → dma_alloc_coherent() → CPU 与设备共同访问,必要处以 dma_wmb() / dma_rmb() 排序 → dma_free_coherent()。
  • 发送方向的流式映射:CPU 填充数据 → dma_map_single() 或 dma_map_page() → dma_mapping_error() → 设备读取 → 完成中断 → dma_unmap_single() 或 dma_unmap_page()。
  • 接收方向的循环缓冲区:dma_map_page()(DMA_FROM_DEVICE)→ 设备写入 → dma_sync_single_for_cpu() → CPU 处理 → dma_sync_single_for_device() → 缓冲区重新交给设备,直至最终 dma_unmap_page()。
  • 散列表:sg_alloc_table() 并填充 → dma_map_sgtable() → for_each_sgtable_dma_sg() 以 sg_dma_address() / sg_dma_len() 填写设备描述符 → 设备访问 → dma_unmap_sgtable()。

系列导航:

相关推荐
DeeplyMind2 天前
第 7 篇 IOMMU 的内部机制:I/O 页表、IOVA 分配与 IOTLB
dma·iommu
mounter6256 天前
Linux 内核热升级(Live Update)技术详解:PCI、VFIO 与 IOMMU 子系统的演进与实战解析引言
linux·linux kernel·kernel·iommu·vfio
橘色的喵1 个月前
从文件系统到 SD 卡:数据写入的四层路径
dma·文件系统·sd·rtos·数据拷贝
虎王物联2 个月前
STM32 HAL库串口DMA空闲中断:高效接收不定长Modbus数据帧
stm32·单片机·嵌入式硬件·dma·modbus
xz驱动分享2 个月前
Linux DMA 子系统学习笔记
linux·dma·rk3588·嵌入式软件
2CM_Embed3 个月前
F28379D DMA从零理解(上):DMA是什么、为什么需要它、架构与触发原理详解
dma·c2000·f28379d
业余程序员plus3 个月前
PCIe ACS机制分析
p2p·pcie·acs·iommu·redirect·egress control·violation
fffzd4 个月前
STM32:串口--DMA
stm32·单片机·嵌入式硬件·串口·dma·回调函数
bbaydnog4 个月前
嵌入式面试高频题第5弹:DMA原理、看门狗机制、低功耗模式,这3个搞不懂简历直接被筛
面试·dma·嵌入式