我们在使用dma的api时候,例如dma_alloc_coherent等,这些API中都会调用
const struct dma_map_ops *ops = get_dma_ops(dev);得到ops,在arm64 因此DMA API的底层实现主要需要实现dma_map_ops 这个结构体
目前在ARM64 这边只要有三种方式实现
static struct dma_map_ops swiotlb_dma_ops
struct dma_map_ops dummy_dma_ops
static struct dma_map_ops iommu_dma_ops

回到上面的问题,当我们通过get_dma_ops的到dma_ops时,code如下:

static inline struct dma_map_ops *get_dma_ops(struct device *dev)
{
    if (xen_initial_domain())
        return xen_dma_ops;
    else
        return __generic_dma_ops(dev);
}
在get_dma_ops中我们假定xen_initial_domain() 返回false,因此会调用__generic_dma_ops(dev)
static inline struct dma_map_ops *__generic_dma_ops(struct device *dev)
{
    if (dev && dev->archdata.dma_ops)
        return dev->archdata.dma_ops;

    /*
     * We expect no ISA devices, and all other DMA masters are expected to
     * have someone call arch_setup_dma_ops at device creation time.
     */
    return &dummy_dma_ops;
}
可见只有在dev->archdata.dma_ops 为null的时候才会返回dummy_dma_ops。
而在开机的过程中会调用
void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
            const struct iommu_ops *iommu, bool coherent)
{
    if (!dev->archdata.dma_ops)
        dev->archdata.dma_ops = &swiotlb_dma_ops;

    dev->archdata.dma_coherent = coherent;
    __iommu_setup_dma_ops(dev, dma_base, size, iommu);
}

可见如果没有设定dev->archdata.dma_ops的话,就让dev->archdata.dma_ops = &swiotlb_dma_ops;因此一般情况下dev->archdata.dma_ops不会为null,也就是说dummy_dma_ops 一般情况下都用不到.
如果开了CONFIG_IOMMU_DMA的话。在arch_setup_dma_ops 函数中首先会调用__iommu_setup_dma_ops->do_iommu_attach来重新设定
dev->archdata.dma_ops = &iommu_dma_ops;
static bool do_iommu_attach(struct device *dev, const struct iommu_ops *ops,
               u64 dma_base, u64 size)
{
    struct iommu_domain *domain = iommu_get_domain_for_dev(dev);

    /*
     * If the IOMMU driver has the DMA domain support that we require,
     * then the IOMMU core will have already configured a group for this
     * device, and allocated the default domain for that group.
     */
    if (!domain || iommu_dma_init_domain(domain, dma_base, size, dev)) {
        pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
            dev_name(dev));
        return false;
    }

    dev->archdata.dma_ops = &iommu_dma_ops;
    return true;
}
也就说在开机的过程中在smmu 使能之前,如果你用dma的话,一定用的是swiotlb_dma_ops,也就是软件smmu,性能会比硬件smmu差,这也就解释了为什么在smmu的初始化函数中可以调用dma_alloc_coherent 而不会造成递归的原因,例如下面的smmu初始化的code中arm_smmu_init_one_queue函数就会调用dma_alloc_coherent。

更多推荐

dma_map_ops 实现的三种方式