当前位置: 首页 > news >正文

DMA-API(map和unmap)调用流程分析(十一)

1.简介

当使用IOMMU且使能DMA-IOMMU中间层时,使用DMA API接口alloc、free、map、unmap内存时,底层都会调用到IOMMU API,最终调用到SMMUv3驱动,完成内存的map和unmap。调用流程如下图所示。

DMA-API调用流程

2.dma_map_single

从下图可以看出,dma_map_single有三种映射内存的方式。第一种是使用direct方式映射内存。第二种使用iommu方式映射内存,第三种使用dma_map_ops回调函数映射内存。本文只介绍前两种。

dma_map_single

2.1.dma_direct_map_page

dma_direct_map_page方式映射内存流程如下图所示。

  1. 如果满足下面的条件之一,则需要使用swiotlb_map映射内存。swiotlb_map会在内部分配一个bounce buffer,然后将源数据拷贝到bounce buffer中,然后clean bounce buffer的cache,最后返回bounce buffer的DMA地址。
    1. 强制使用swiotlb bounce,即is_swiotlb_force_bounce返回true
    2. 传入的地址DMA无法访问或者kmalloc分配的内存需要bounce buffer,并且设备拥有swiotlb内存池。
  2. 如果不满足1的条件,说明传入的地址DMA可以访问,只需要clean cache即可,最后返回传入地址的DMA地址。

dma_direct_map_page

2.2.iommu_dma_map_page

如果使用IOMMU,则调用iommu_dma_map_page函数映射内存。执行流程如下:

  1. 如果起始物理地址和长度没有按页对齐,IOMMU无法直接映射,则需要使用bounce page(会按页对齐),即走swiotlb_tbl_map_single的流程。
  2. clean需要映射内存的cache。
  3. 调用__iommu_dma_map映射内存,内部首先分配IOVA,然后调用SMMU驱动映射IOVA和物理内存。IOMMU映射内存的流程参考DMA-API(alloc和free)调用流程分析(十) 第2.2.节。

iommu_dma_map_page

3.dma_unmap_single

从下图可以看出,dma_unmap_single有三种unmap内存的方式。第一种是使用direct方式unmap内存。第二种使用iommu方式unmap内存,第三种使用dma_map_ops回调函数方式unmap内存。本文只介绍前两种。dma_map_singledma_unmap_single函数相互匹配,当使用direct方式map内存时,则必须使用direct方式unmap内存,IOMMU则类似。
dma_unmap_single

3.1.dma_direct_unmap_page

如果dma_direct_map_page函数使用swiotlb方式映射内存,则dma_direct_unmap_page函数也要用swiotlb方式unmap内存,否则只需要invalidate cache。swiotlb方式unmap内存时,首先要invalidate cache,然后将swiotlb内存中的数据拷贝到源缓冲区中,最后释放swiotlb内存。
dma_direct_unmap_page

3.2.iommu_dma_unmap_page

iommu_dma_unmap_page函数unmap内存的流程如下图所示,具体如下:

  1. 由于invalidate cache需要使用物理内存,因此需要调用iommu_iova_to_phys将IOVA转换成物理地址。
  2. invalidate cache。
  3. 调用IOMMU API unmap内存。流程可参考DMA-API(alloc和free)调用流程分析(十) 第3.2.节。
  4. 如使用swiotlb内存,则还需要将swiotlb bounce buffer中的数据拷贝到原缓冲中,然后释放swiotlb bounce buffer。

iommu_dma_unmap_page

当使用IOMMU的情况下,调用iommu_iova_to_phys函数将IOVA转换成物理地址。转换的功能最终在arm_lpae_iova_to_phys函数中实现,其会从遍历pgd页表开始遍历,最终计算得到IOVA对应的物理地址。

[drivers/iommu/io-pgtable-arm.c]
static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,unsigned long iova)
{......do {/* Valid IOPTE pointer? */if (!ptep)return 0;/* 获取IOVA的第lvl级页表索引,加上ptep的基地址,就能得到对应页表项的地址 */ptep += ARM_LPAE_LVL_IDX(iova, lvl, data);pte = READ_ONCE(*ptep);  /* 获取第lvl级页表的下一级页表 *//* Valid entry? */if (!pte)return 0;/* 如果是叶子页表,即最后一级页表,则表明找到iova的物理 */if (iopte_leaf(pte, lvl, data->iop.fmt))goto found_translation;/* 从页表项中提取下一级页表的地址 */ptep = iopte_deref(pte, data);} while (++lvl < ARM_LPAE_MAX_LEVELS);  // 下一级页表/* Ran out of page tables to walk */return 0;
/* 找到最后一级页表地址 */
found_translation:/* ARM_LPAE_BLOCK_SIZE计算block/page描述符映射的内存大小* (ARM_LPAE_BLOCK_SIZE - 1)获取了block/page描述符映射内存大小的掩码* iova与验码计算,得到了物理地址的业内便宜*/iova &= (ARM_LPAE_BLOCK_SIZE(lvl, data) - 1);/* iopte_to_paddr获取最后一级页表映射的物理内存页基地址,加上偏移最终得到物理地址 */return iopte_to_paddr(pte, data) | iova;
}

4.dma_map_sg

从下图可以看出,dma_map_sg有三种映射内存的方式。第一种是使用direct方式映射内存。第二种使用iommu方式映射内存,第三种使用dma_map_ops回调函数映射内存。本文只介绍前两种。
dma_map_sg

4.1.dma_direct_map_sg

dma_direct_map_sg函数的执行流程如下所示,其遍历scatterlist数组中的每一个page,调用dma_direct_map_page函数映射,具体可参考2.1节。
dma_direct_map_sg

4.2.iommu_dma_map_sg

iommu_dma_map_sg映射内存的流程如下图所示,具体步骤如下:

  1. 如果设备是不可信设备,或者scatterlist数组中有缓冲区没有按DMA要求的长度对齐,则走iommu_dma_map_sg_swiotlb映射流程。映射完成后直接返回,不走后面的流程。每一个scatterlist映射后的DMA地址(IOVA地址)保存在dma_address中,因此整个scatterlist数组映射后的IOVA地址并不连续。
    1. 首先给第一个scatterlist设置SG_DMA_SWIOTLB标志,表示scatterlist使用swiotlb bounce buffer。
    2. 遍历每一个scatterlist,调用iommu_dma_map_page进行映射,具体可参考2.2节。
  2. 首先调用iommu_dma_sync_sg_for_device同步缓存,若需要swiotlb(长度没有对齐的情况第一步已经处理,这里通常不需要swiotlb),则还需要分配bounce buffer并且拷贝数据。然后遍历scatterlist数组,计算需要分配的IOVA长度。接着分配IOVA,最后调用iommu_map_sg映射scatterlist数组,内部会针对每一个scatterlist,调用__iommu_map映射内存。整个scatterlist数组映射后的IOVA地址连续。IOMMU映射内存的流程参考DMA-API(alloc和free)调用流程分析(十) 第2.2.节。
    iommu_dma_map_sg

5.dma_unmap_sg

从下图可以看出,dma_unmap_sg有三种unmap内存的方式。第一种是使用direct方式unmap内存。第二种使用iommu方式unmap内存,第三种使用dma_map_ops回调函数unmap内存。本文只介绍前两种。
dma_unmap_sg

5.1.dma_direct_unmap_sg

dma_direct_unmap_sg函数执行流程如下图所示,主要工作有:

  1. 如果DMA地址是总线地址(即scatterlist中的dma_flags设置了SG_DMA_BUS_ADDRESS标志),则不需要unmap,直接清除掉SG_DMA_BUS_ADDRESS即可。pci_p2pdma缓冲区地址通常会设置SG_DMA_BUS_ADDRESS标志。
  2. 否则需要走unmap流程。遍历每一个scatterlist,invalidate cache,如果使用swiotlb内存,还需要拷贝数据和释放swiotlb内存。
    dma_direct_unmap_sg

5.2.iommu_dma_unmap_sg

iommu_dma_unmap_sgiommu_dma_map_sg流程刚好相反,执行流程如下:

  1. 第一个scatterlist如果设置了SG_DMA_SWIOTLB标志,则说明使用swiotlb内存(IOVA不连续),则遍历每一个scatterlist,调用iommu_dma_unmap_page解除映射。完成之后直接返回。
  2. 遍历scatterlist数组,invalidate cache。
  3. 遍历scatterlist数组,计算IOVA的起始地址和结束地址(IOVA不连续)。
  4. 调用__iommu_dma_unmap函数unmap内存。流程可参考DMA-API(alloc和free)调用流程分析(十) 第3.2.节。
    iommu_dma_unmap_sg

参考资料

  1. linux6.12 source code.
  2. Arm ® System Memory Management Unit Architecture Specification version 3.

文章转载自:

http://djuw20Jp.rydhq.cn
http://EdR1cqU6.rydhq.cn
http://4O2qsbSJ.rydhq.cn
http://xN3figEV.rydhq.cn
http://RU6zuTtK.rydhq.cn
http://g1WUGu7W.rydhq.cn
http://52sUssws.rydhq.cn
http://mtp0EIXh.rydhq.cn
http://0hpPASty.rydhq.cn
http://N0cN0qKS.rydhq.cn
http://fUWuulDH.rydhq.cn
http://ClG5Er1v.rydhq.cn
http://rbwvM3lr.rydhq.cn
http://w8cWIBLJ.rydhq.cn
http://YIQLbilp.rydhq.cn
http://hMpzNiDC.rydhq.cn
http://lmt49O8O.rydhq.cn
http://cXpEdDXz.rydhq.cn
http://JZirsOES.rydhq.cn
http://2j3qc1kh.rydhq.cn
http://LO5IcIsO.rydhq.cn
http://s218W5hR.rydhq.cn
http://tTOFEWqu.rydhq.cn
http://XtDUXswP.rydhq.cn
http://RzSSsaBV.rydhq.cn
http://FJbpOumB.rydhq.cn
http://zWyMPmj7.rydhq.cn
http://XCWgcJvg.rydhq.cn
http://CL2LmDBc.rydhq.cn
http://RHDA3idp.rydhq.cn
http://www.dtcms.com/a/383941.html

相关文章:

  • LeetCode 1898.可移除字符的最大数目
  • LeetCode算法日记 - Day 42: 岛屿数量、岛屿的最大面积
  • 局域网文件共享
  • llamafactory 部署教程
  • Linux链路聚合工具之ifenslave命令案例解析
  • 资金方视角下的链改2.0:拉菲资本的观察与判断
  • AIPex:AI + 自然语言驱动的浏览器自动化扩展
  • < JS事件循环系列【四】> 事件循环补充概念:从执行细节到性能优化
  • MySQL从入门到精通:基础、安装与实战管理指南
  • 解决:Ubuntu、Kylin、Rocky系统中root用户忘记密码
  • javascript文本长度检测与自动截取,用于标题长度检测
  • 解锁 DALL・E 3:文生图多模态大模型的无限可能
  • 深入理解 LVS-DR 模式与 Keepalived 高可用集群
  • 数据库学习MySQL系列4、工具一 Navicat Premium 图形化软件的使用详细教程
  • RL【10-2】:Actor - Critic
  • MATLAB学习文档(十六)
  • 滑动窗口概述
  • 【C++语法】模版初阶
  • 机械制造工艺指南
  • Wi-Fi技术——Power SAVE模式
  • leetcode39(相同的树)
  • C++(虚函数表原理和菱形继承)
  • 【STM32项目开源】STM32单片机智能语音风扇控制系统
  • [Android]自定义view
  • 线程和进程,以及GCD的简单使用
  • C++_STL和数据结构《1》_STL、STL的迭代器、c++中的模版、STL的容器、列表初始化、三个算法、链表
  • 学习日报|线程池专题学习总结
  • kubectl 报错 couldn‘t get current server API group list:
  • 求最小公倍数(GCD)和最大公约数(LCM)——原理和代码
  • 单调栈数据结构