AMD IOMMU与Linux (3) -- DMA

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.
 */

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 掌握DMA(直接内存访问)和IOMMU(输入/输出内存管理单元)APIs是非常重要的。DMA是在计算机系统中实现高速数据传输的机制,它允许外设(如硬盘、网络适配器)直接访问系统内存,而无需通过CPU的干预。这样可以大大提高数据传输的效率和速度。然而,DMA的使用需要正确地配置和管理,否则可能会导致数据丢失或损坏。 而IOMMU是一种硬件设备,用于管理和映射DMA请求,以保护系统的安全性。它允许对DMA请求进行地址转换和访问权限控制,以避免恶意设备对系统或其他设备的干扰。掌握IOMMU的APIs可以帮助开发人员正确地配置和管理IOMMU,确保系统的安全性和可靠性。 要掌握DMAIOMMU的APIs,首先需要了解其基本概念和原理。还需要熟悉相关的编程语言和操作系统的API文档,以了解如何使用和调用这些APIs。掌握正确的API使用方法和参数配置对于正确、高效和安全地使用DMAIOMMU是至关重要的。 实践是掌握这些APIs的关键。开发人员可以通过编写示例代码和进行实验来熟练掌握API的使用方法。同时,也可以参考其他开发者的经验和文档,从中学习和总结最佳实践。 最后,密切关注DMAIOMMU相关的安全漏洞和更新也是非常重要的。由于这些APIs涉及系统的核心组件和数据传输,任何漏洞或错误都可能导致严重的安全问题。因此,及时更新和修复相关的补丁是掌握DMAIOMMU APIs的重要一环。 ### 回答2: 掌握 DMA(直接内存访问)和 IOMMU(输入/输出内存管理单元)的 API 是非常重要的。 DMA 是一种直接访问内存的技术,允许外设(如网络适配器、磁盘控制器)直接与系统内存进行数据传输,而无需通过 CPU 的干预。掌握 DMA 的 API,可以使我们能够更好地理解和利用这种技术。通过使用 DMA,我们可以提高数据传输的效率,减少 CPU 的负载,从而提高系统性能。 IOMMU 则是一种硬件设备,用于管理输入和输出设备对于内存的访问。IOMMU 的主要作用是提供安全性和隔离性,防止外设读写不安全的内存区域。掌握 IOMMU 的 API,可以帮助我们更好地管理和控制设备对内存的访问权限,确保系统的安全性和稳定性。 掌握 DMAIOMMU 的 API,可以让我们更好地理解和控制系统与外设之间的数据传输和内存访问。这对于开发高效的设备驱动程序和操作系统非常重要。同时,了解和使用这些 API 还可以帮助我们更好地解决与外设和输入/输出相关的问题,提高系统的稳定性和可靠性。总之,掌握 DMAIOMMU 的 API 对于我们进行系统开发和维护是非常有益的。 ### 回答3: 掌握DMA(直接内存访问)和IOMMU(输入/输出内存管理单元)API是非常重要的。 DMA是一种技术,允许外部设备(如网卡、显卡等)直接访问系统内存,而不需要CPU的干预。通过使用DMA技术,设备可以像访问自己的内存一样快速高效地读写系统内存。掌握DMA API意味着我们能够编写程序来管理设备的DMA传输,包括启动传输、传输数据和处理传输完成的通知等。 另一方面,IOMMU是一种硬件设备,可以提供地址转换和访问控制功能,能够增强系统的安全性和性能。通过IOMMU,系统可以将设备的物理地址转换成虚拟地址,并且可以对设备的访问进行权限控制,以确保设备只能访问其被授权的内存区域。掌握IOMMU API意味着我们可以编写程序来管理和配置IOMMU,包括设置地址转换规则、配置访问权限和处理IOMMU事件等。 掌握DMAIOMMU API对于开发者来说至关重要。通过使用这些API,我们可以更好地管理系统的内存资源和设备访问,提高程序的性能和安全性。同时,以这些API为基础,我们还可以构建更复杂的系统、驱动程序和应用程序,以满足不同的需求和应用场景。因此,深入理解和熟练运用DMAIOMMU API对于系统开发和驱动程序开发人员来说是非常重要的技能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值