AMD IOMMU与Linux (3) -- DMA

2023-05-16

Linux中DMA会使用硬件IOMMU如AMD IOMMU, INTEL VT-D, 也会使用软件的SWIOTLB

这篇梳理一下LINUX内核在有AMD IOMMU的情况下,是如何做DMA的,内容包括如下

1. struct iommu_ops amd_iommu_ops

2. struct dma_map_ops iommu_dma_ops

3. DMA struct dma_map_ops 与 struct iommu_ops的关系

    Consistent, Streaming

4. struct io_pgtable_ops, 及与struct iommu_ops amd_iommu_ops的关系

1. 两处会设置struct iommu_ops amd_iommu_ops;

const struct iommu_ops amd_iommu_ops = {
    .capable = amd_iommu_capable,
    .domain_alloc = amd_iommu_domain_alloc,  // 分配一个iommu_domain
    .domain_free  = amd_iommu_domain_free,
    .attach_dev = amd_iommu_attach_device, //针对独立设备(即所在Group里只有自己),将设备所在Group与domain进行绑定
    .detach_dev = amd_iommu_detach_device,
    .map = amd_iommu_map, //用于映射domain内的iova,将长度为sizeiova为起始地址的iova区域映射到以paddr为起始地址的物理地址。该函数只能用于UNMANAGED类型和DMA类型的domain
    .iotlb_sync_map    = amd_iommu_iotlb_sync_map,
    .unmap = amd_iommu_unmap,
    .iova_to_phys = amd_iommu_iova_to_phys, // 将iova转换成物理地址
    .probe_device = amd_iommu_probe_device,
    .release_device = amd_iommu_release_device,
    .probe_finalize = amd_iommu_probe_finalize,
    .device_group = amd_iommu_device_group,
    .get_resv_regions = amd_iommu_get_resv_regions,
    .put_resv_regions = generic_iommu_put_resv_regions,
    .is_attach_deferred = amd_iommu_is_attach_deferred,
    .pgsize_bitmap    = AMD_IOMMU_PGSIZES,
    .flush_iotlb_all = amd_iommu_flush_iotlb_all,
    .iotlb_sync = amd_iommu_iotlb_sync,
    .def_domain_type = amd_iommu_def_domain_type,
};

一处在struct iommu_device的iommu ops;

另一处在struct bus_type的iommu ops;

amd_iommu_init ->

        iommu_go_to_state ->

                state_next ->

                        amd_iommu_init_pci ->

                                iommu_init_pci ->

                                        iommu_device_register

struct iommu_device {
    struct list_head list;
    const struct iommu_ops *ops;
    struct fwnode_handle *fwnode;
    struct device *dev;
};

/*
 * Structure where we save information about one hardware AMD IOMMU in the
 * system.
 */

struct amd_iommu {

        ...

        struct iommu_device iommu;/* Handle for IOMMU core code */

        ...

}

amd_iommu_init ->

        iommu_go_to_state ->

                state_next ->

                        amd_iommu_init_pci ->

                                amd_iommu_init_api ->

                                        bus_set_iommu ->  //将自身挂入到 对应总线中

struct bus_type {

        ...

        const struct iommu_ops *iommu_ops;

        ...
};

2. 设置struct dma_map_ops iommu_dma_ops

amd_iommu_ops.probe_finalize = amd_iommu_probe_finalize ->

        iommu_setup_dma_ops->

                struct device *dev->dma_ops = &iommu_dma_ops;

struct device {
        ...
    const struct dma_map_ops *dma_ops; // DMA mapping operations for this device
        ...
};

static const struct dma_map_ops iommu_dma_ops = {
    .alloc            = iommu_dma_alloc,
    .free            = iommu_dma_free,
    .alloc_pages        = dma_common_alloc_pages,
    .free_pages        = dma_common_free_pages,
#ifdef CONFIG_DMA_REMAP
    .alloc_noncontiguous    = iommu_dma_alloc_noncontiguous,
    .free_noncontiguous    = iommu_dma_free_noncontiguous,
#endif
    .mmap            = iommu_dma_mmap,
    .get_sgtable        = iommu_dma_get_sgtable,
    .map_page        = iommu_dma_map_page,
    .unmap_page        = iommu_dma_unmap_page,
    .map_sg            = iommu_dma_map_sg,
    .unmap_sg        = iommu_dma_unmap_sg,
    .sync_single_for_cpu    = iommu_dma_sync_single_for_cpu,
    .sync_single_for_device    = iommu_dma_sync_single_for_device,
    .sync_sg_for_cpu    = iommu_dma_sync_sg_for_cpu,
    .sync_sg_for_device    = iommu_dma_sync_sg_for_device,
    .map_resource        = iommu_dma_map_resource,
    .unmap_resource        = iommu_dma_unmap_resource,
    .get_merge_boundary    = iommu_dma_get_merge_boundary,
};

iommu_dma_alloc ->

        void *cpu_addr = iommu_dma_alloc_pages

        dma_addr_t iova = __iommu_dma_map (-> iommu_dma_alloc_iova)

3. DMA -- struct dma_map_ops 与 struct iommu_ops

如果写过LINUX DMA驱动,会接触过以下几个函数:

Consistent

void * dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag)

Streaming

dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, size_t size, enum dma_data_direction direction)

dma_map_page

dma_map_sg

其中dma_alloc_coherent,的调用栈为:

dma_alloc_coherent ->

        dma_alloc_attrs ->

                struct dma_map_ops *ops->alloc == iommu_dma_alloc ->

                        __iommu_dma_map ->

                                iommu_map_atomic ->

                                        _iommu_map->

                                                __iommu_map->

                                                        __iommu_map_pages->

                                                                struct iommu_ops *ops->map_pages/map

这里的iommu_ops.map 在使用AMD IOMMU的时候,就是.map = amd_iommu_map,

                        

#define dma_map_single(d, a, s, r) dma_map_single_attrs(d, a, s, r, 0)
#define dma_unmap_single(d, a, s, r) dma_unmap_single_attrs(d, a, s, r, 0)
#define dma_map_sg(d, s, n, r) dma_map_sg_attrs(d, s, n, r, 0)
#define dma_unmap_sg(d, s, n, r) dma_unmap_sg_attrs(d, s, n, r, 0)
#define dma_map_page(d, p, o, s, r) dma_map_page_attrs(d, p, o, s, r, 0)
#define dma_unmap_page(d, a, s, r) dma_unmap_page_attrs(d, a, s, r, 0)

对应到dma_map_single的调用栈为:

dma_map_single_attrs->

        dma_map_page_attrs->

                struct dma_map_ops *ops->map_page == iommu_dma_map_page ->

                        __iommu_dma_map_swiotlb ->

                                __iommu_dma_map -> 

                                        之后与dma_alloc_coherent相同

                        

        

4.  struct io_pgtable_ops

amd_iommu_map最终的实现在struct io_pgtable_ops

amd_iommu_map ->

        struct io_pgtable_ops *ops->map

        

struct io_pgtable_ops {
    int (*map)(struct io_pgtable_ops *ops, unsigned long iova,
           phys_addr_t paddr, size_t size, int prot, gfp_t gfp);
    int (*map_pages)(struct io_pgtable_ops *ops, unsigned long iova,
             phys_addr_t paddr, size_t pgsize, size_t pgcount,
             int prot, gfp_t gfp, size_t *mapped);
    size_t (*unmap)(struct io_pgtable_ops *ops, unsigned long iova,
            size_t size, struct iommu_iotlb_gather *gather);
    size_t (*unmap_pages)(struct io_pgtable_ops *ops, unsigned long iova,
                  size_t pgsize, size_t pgcount,
                  struct iommu_iotlb_gather *gather);
    phys_addr_t (*iova_to_phys)(struct io_pgtable_ops *ops,
                    unsigned long iova);
};

struct io_pgtable {
    enum io_pgtable_fmt    fmt;
    void            *cookie;
    struct io_pgtable_cfg    cfg;
    struct io_pgtable_ops    ops;
};

struct amd_io_pgtable {
    struct io_pgtable_cfg    pgtbl_cfg;
    struct io_pgtable    iop;
        ...
};

struct protection_domain {
        ...
    struct iommu_domain domain; /* generic domain handle used by
                       iommu core code */
    struct amd_io_pgtable iop;
        ...
};

struct amd_io_pgtable *pgtable

    pgtable->iop.ops.map          = iommu_v1_map_page; //maps a physical address into a DMA
address space. It allocates the page table pages if necessary, 建立DMA addr与paddr的page table

    pgtable->iop.ops.unmap        = iommu_v1_unmap_page;
    pgtable->iop.ops.iova_to_phys = iommu_v1_iova_to_phys;

5. Summary

系统中的调用关系如下:

struct dma_map_ops iommu_dma_ops ->

        struct iommu_ops amd_iommu_op ->

                struct io_pgtable_ops

iommu是实现在dma mapping api下层的驱动,所以我们只需要使用dma mapping的相关api,不需要直接调用iommu接口

AMD IOMMU驱动实现了自己的struct io_pgtable_ops

类似在内核中的还有ARM SMMU与Apple DART等,详见include/linux/io-pgtable.h文件

Reference:

[1] 

​​​​​​dma_map_ops 实现的三种方式_jason的笔记-CSDN博客_dma map​​​​​​

[2]

​​​​​​kernel是如何选择iommu的呢?_jason的笔记-CSDN博客[

[3]

iommu_dma_mmap + mmap - tycoon3 - 博客园 (cnblogs.com)

【4】

Documentation/core-api/dma-api-howto.rst

Documentation/core-api/dma-api.rst

Notes:

1. 处于同一个domain中的设备使用同一套映射做地址转换, 就是独立的页表

2. Group中default_domain和domain的概念:domain指group当前所在的domain,而default_domain指Group默认应该在的domain

进行attach操作时,会检查default_domain是否与domain相同,以此判断该Group是否已经attach到别的domain上了

如果Group有自己的default_domain,那么该函数iommu_detach_device在detach完成之后会重新attach到default_domain上

3. PCIE是一个点对点的协议,如果一个多function设备挂到了一个不支持ACS的bridge下,那么这两个function可以通过该bridge进行通信。这样的通信直接由bridge进行转发而无需通过Root Complex,自然也就无需通过IOMMU。这种情况下,这两个function的IOVA无法完全通过IOMMU隔离开,所以他们需要分到同一个Group中。同一个Group的设备应该是共用一个domain的

4. 每一个domain即代表一个iommu映射地址空间,即一个page table。一个Group逻辑上是需要与domain进行绑定的,即一个Group中的所有设备都位于一个domain中

Questions:

The difference between IOMMU_DOMAIN_UNMANAGED  & IOMMU_DOMAIN_DMA?  

/*
 * This are the possible domain-types
 *
 *    IOMMU_DOMAIN_BLOCKED    - All DMA is blocked, can be used to isolate
 *                  devices
 *    IOMMU_DOMAIN_IDENTITY    - DMA addresses are system physical addresses
 *    IOMMU_DOMAIN_UNMANAGED    - DMA mappings managed by IOMMU-API user, used
 *                  for VMs
 *    IOMMU_DOMAIN_DMA    - Internally used for DMA-API implementations.
 *                  This flag allows IOMMU drivers to implement
 *                  certain optimizations for these domains
 *    IOMMU_DOMAIN_DMA_FQ    - As above, but definitely using batched TLB
 *                  invalidation.
 */

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

AMD IOMMU与Linux (3) -- DMA 的相关文章

  • 如何在Linux上用C/C++编写Web服务器[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在考虑在 Linux 平台上开发一个小型 阅读 初级 Web 服务器 但我不知道从哪里开始 我希望它能够做的是 监听特定端口 接受
  • Linux中如何避免sleep调用因信号而中断?

    我在 Linux 中使用实时信号来通知串行端口中新数据的到达 不幸的是 这会导致睡眠呼叫在有信号时被中断 有人知道避免这种行为的方法吗 我尝试使用常规信号 SIGUSR1 但我不断得到相同的行为 来自 nanosleep 联机帮助页 nan
  • 对于任何真实数据集,数据压缩比的最小可能值是多少

    我在写信ZLIB类似于嵌入式硬件压缩器的 API 它使用 deflate 算法来压缩给定的输入流 在进一步讨论之前 我想解释一下数据压缩率 数据压缩率定义为未压缩大小与压缩大小之间的比率 压缩比通常大于一 这意味着压缩数据通常比未压缩数据小
  • 使用 shell 脚本发送 HTML 邮件

    如何使用 shell 脚本发送 HTML 电子邮件 首先 您需要撰写消息 最低限度由这两个标头组成 MIME Version 1 0 Content Type text html 以及适当的消息正文 p Hello world p 获得后
  • 在本地主机上使用相同的 IP 和端口创建套接字

    我在 Linux 上看到奇怪的行为 我看到远程端和本地端都显示相同的 IP 和端口组合 以下是 netstat 输出 netstat anp 网络统计grep 6102 tcp 0 0 139 185 44 123 61020 0 0 0
  • 在 Linux 中重新启动时,新创建的文件变为 0 kb(数据被覆盖为空)

    我遇到了一个奇怪的问题 这让我发疯 当前的任务是在 root 用户第一次登录时启动一组文件 并在同一用户第二次登录时启动另一组文件 我决定使用 profile 和 bashrc 文件 并在第一次登录期间发生的任务结束时重新加载 bashrc
  • 如何从 Bash 命令行在后台 Vim 打开另一个文件?

    我正在从使用 Gvim 过渡到使用控制台 Vim 我在 Vim 中打开一个文件 然后暂停 Vim 在命令行上运行一些命令 然后想返回到 Vim Ctrl Z 在正常模式下 暂停 Vim 并返回到控制台 fg可用于将焦点返回到 Vim job
  • Crontab 每 5 分钟一次 [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我如何告诉 crontab 每 5 分钟运行一次 但从每小时的第二分钟开始 换句话说 我想在以下时间执行我的脚本minute 5 2 例如 我的脚本应
  • 为 Linux 编译 Objective-C 应用程序(API 覆盖范围)

    我可能在这里问一些奇怪的问题 但我不确定从哪里开始 问题是我正在考虑使用 Obj C 和 Foundation 类在 Mac 上编写一个命令行工具 但存在一个非常大的风险 那就是我希望能够为不同的 Linux 发行版编译它 以便将来作为服务
  • ssh 连接超时

    我无法在 git 中 ssh 到 github bitbucket 或 gitlab 我通常会收到以下错误消息 如何避免它 输出 ssh T email protected cdn cgi l email protection i ssh
  • vmsplice() 和 TCP

    在原来的vmsplice 执行 有人建议 http lwn net Articles 181169 如果您的用户态缓冲区是管道中可容纳的最大页面数的 2 倍 则缓冲区后半部分成功的 vmsplice 将保证内核使用缓冲区的前半部分完成 但事
  • ioctl 命令的用户权限检查

    我正在实现 char 驱动程序 Linux 并且我的驱动程序中有某些 IOCTL 命令仅需要由 ADMIN 执行 我的问题是如何在 ioctl 命令实现下检查用户权限并限制非特权用户访问 IOCTL 您可以使用bool capable in
  • 批量删除文件名中包含 BASH 中特殊字符的子字符串

    我的目录中有一个文件列表 opencv calib3d so2410 so opencv contrib so2410 so opencv core so2410 so opencv features2d so2410 so opencv
  • 为什么 Linux 原始套接字的 RX 环大小限制为 4GB?

    背景 我试图mmap 我的原始套接字的 RX 环形缓冲区64 bitLinux 应用程序 我的环由 4096 个块组成 每个块大小为 1MB 总共 4GB 请注意 每个 1MB 块中可以有许多帧 如果您好奇 请参阅此文档了解背景信息 htt
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • 如何使用 JSch 将多行命令输出存储到变量中

    所以 我有一段很好的代码 我很难理解 它允许我向我的服务器发送命令 并获得一行响应 该代码有效 但我想从服务器返回多行 主要类是 JSch jSch new JSch MyUserInfo ui new MyUserInfo String
  • linux下无法创建僵尸进程

    嗯 我有一个奇怪的问题 我无法在我的项目中创建僵尸进程 但我可以在其他文件中创建僵尸进程 有简单的说明 int main if fork 0 printf Some instructions n else sleep 10 wait 0 r
  • 调用 printf 系统子例程在汇编代码中输出整数错误[重复]

    这个问题在这里已经有答案了 来回 在windows7控制台窗口中运行gcc s2 asm 然后生成一个exe文件 运行a exe 然后崩溃 为什么 s2 asm 代码由以下源代码生成 int m m 1 iprint m s2 asm请参考
  • docker 非 root 绑定安装权限,WITH --userns-remap

    all 尝试让绑定安装权限正常工作 我的目标是在容器中绑定安装卷 以便 a 容器不以 root 用户身份运行入口点 二 docker daemon 配置了 userns remap 这样容器 主机上没有 root c 我可以绑定挂载和读 写
  • 在 .gitconfig 中隐藏 GitHub 令牌

    我想将所有点文件存储在 GitHub 上 包括 gitconfig 这需要我将 GitHub 令牌隐藏在 gitconfig 中 为此 我有一个 gitconfig hidden token 文件 这是我打算编辑并放在隐藏令牌的 git 下

随机推荐

  • 【Cmake】在Windows下用Cmake构建C++工程项目——cmake 的 helloworld——1

    参考资料 xff1a Cmake 实践 Cmake Practice Cjacker Cmake 实践 xff0c 是在Linux系统环境下讲解的 xff0c 有些地方在Windows下看不到该有的效果 xff0c 而且第一节因为给出的代码
  • OpenCV矩阵cv::Mat通道和位深的整理(CV_8UC1,CV_16UC1,CV_32FC1等等)

    一 数据类型说明 矩阵数据类型 CV lt bit depth gt S U F C lt number of channels gt 1bit depth 比特数 代表8bit 16bit 32bit 64bit2S U F S 代表 s
  • github如何修改repository(仓库)名字

    首先登录你的github 查看你的仓库目录页 xff0c 点击你需要更改的仓库 进入此仓库管理页面 xff0c 点击Settings 进入到设置页面 xff0c 如果你是想要更改名称的话 xff0c 直接在这里改 xff0c 然后点击Ren
  • ROS中tf树,frame未连接的问题。

    今天调节双舵轮的AGV xff0c 一开始无法导航 xff0c 由于学生有些忙 xff0c 没做urdf 我就直接发布静态坐标变换 xff0c odom到basefootprint是里程计自己编写 xff0c 问题是加了laser base
  • The POM for xxxx is missing, no dependency information available

    很久以前用Maven的时候基本都是一个工程 xff0c 后来感觉太落伍了 xff0c 就根据geoserver源码开始分模块对功能进行优化 后来有个新来的同事也碰到了这个问题 xff0c 我就给他解决一下 xff0c 顺便把以前的心得记录一
  • 无人机开发-介绍Mavlink协议的消息组成、如何看懂繁杂的mavlink官网介绍、简单介绍地面站与飞控的通讯流程

    这篇博客主要介绍了mavlink的消息组成和如何看懂繁杂的mavlink官网介绍以及简单介绍了下地面站与飞控的通讯流程 前面已经提到了在mavlink消息帧里最重要的两个东西 xff0c 一个是msgid xff1b 一个是payload
  • 无人机开发-介绍MAVLink代码的大概结构

    可以看到 xff0c 里面有多个文件夹和几个头文件 pixhawk xff0c ardupilotmega xff08 apm xff09 xff0c matrixpilot这类的文件夹里都是各个飞控自己定义的mavlink消息类型 xff
  • 无人机开发-图传技术浅析

    2016年 xff0c 是中国无人机市场的元年 xff0c 无人机能够一跃进入大众视野 xff0c 并迅速在大众市场火热发展 xff0c 是很多人始料未及的 从刚开始的空中摄录 xff0c 到后来的实时摄录 xff0c 方便的无人机图传功能
  • Ubuntu18.04安装ROS+gazebo9+PIX4仿真

    本文仅作安装过程记录之用 1 安装ros Ubuntu18 04选择ROS Melodic 教程网址 xff1a http wiki ros org cn melodic Installation Ubuntu 1 1配置 Ubuntu 软
  • PX4+gazebo仿真给无人机添加摄像头

    1 启动仿真 xff1a cd到Firmware文件夹 xff0c 执行以下代码 roslaunch px4 mavros posix sitl launch 如果启动过程卡住或者很慢 xff0c 下载该链接的压缩包https bitbuc
  • 最全Pycharm教程(10)——Pycharm调试器总篇

    如果觉得这篇文章对您有所启发 xff0c 欢迎关注我的公众号 xff0c 我会尽可能积极和大家交流 xff0c 谢谢 最全Pycharm教程 xff08 1 xff09 定制外观 最全Pycharm教程 xff08 2 xff09 代码风格
  • 关于嵌入式

    学习方向 首先要学习下基础课程单片机 xff0c 汇编和C语言等等 xff0c 然后再学习嵌入式 xff0c 如果说你要想水平高的话 xff0c 最好学习下操作系统 xff0c 数据结构 xff0c 算法及一些硬件方面的知识等等 看你是想在
  • make_unique的使用

    关于make unique的构造及使用例程 xff0c MSDN的讲解非常详细 xff08 https msdn microsoft com zh cn library dn439780 aspx xff09 使用过程中 xff0c 我的理
  • C#学习记录——C#编写串口程序

    因为电气自动化专业出差太多 xff0c 考虑学点其他的看能不能实现转行 xff0c 也没太清晰的路线 xff0c 看网上好多推荐电气自动化转C 上位机开发的 xff0c 也抽时间学习了解下C xff0c 因为非软件专业 xff0c 对计算机
  • the working directory ‘XXX’ does not exist

    积累点滴 今天在idea上重新建了一个项目 xff0c 结果一运行就报了 the working directory XXX does not exist 的错误 明明上一个项目都运行好好的 xff0c 怎么新建一个就出问题了呢 xff1f
  • Git 子模块(Submodule)

    提示 xff1a Git 子模块 Submodule 操作 文章目录 一 Git 子模块 Submodule 是什么 xff1f 二 使用步骤1 创建子仓库2 clone 带有子仓库的git项目 三 子仓库代码的修改和更新 一 Git 子模
  • Java Web项目开发项目经验总结

    一 学会如何读一个JavaWeb项目源代码 步骤 xff1a 表结构 gt web xml gt mvc gt db gt spring ioc gt log gt 代码 1 先了解项目数据库的表结构 xff0c 这个方面是最容易忘记的 x
  • React + TS + Mobx 示例

    一 创建项目 方式一 xff1a create react app todo React ts demo scripts version 61 react scripts ts cd todo React ts demo npm start
  • AMD IOMMU与Linux (2) -- IVRS及AMD IOMMU硬件初始化

    介绍AMD IOMMU driver基于IVRS的硬件初始化情况 1 I O Virtualization ACPI table 2 drivers iommu amd init c 1 I O Virtualization ACPI ta
  • AMD IOMMU与Linux (3) -- DMA

    Linux中DMA会使用硬件IOMMU如AMD IOMMU INTEL VT D xff0c 也会使用软件的SWIOTLB 这篇梳理一下LINUX内核在有AMD IOMMU的情况下 xff0c 是如何做DMA的 xff0c 内容包括如下 1