kernel是如何选择iommu的呢?

在arch/arm64/mm/dma-mapping.c 中定义了三种dma的ops,分别是:dummy_dma_ops/swiotlb_dma_ops/iommu_dma_ops 。那kernel是如何选择iommu的呢?

如果acpi表中定义了IORT 子表的话,flow如下:
acpi_iort_init->iort_init_platform_devices
static void __init iort_init_platform_devices(void)
{
        if ((iort_node->type == ACPI_IORT_NODE_SMMU) ||
            (iort_node->type == ACPI_IORT_NODE_SMMU_V3)) {

            fwnode = acpi_alloc_fwnode_static();
            if (!fwnode)
                return;

            iort_set_fwnode(iort_node, fwnode);

            ret = iort_add_smmu_platform_device(iort_node);
            if (ret) {
                iort_delete_fwnode(iort_node);
                acpi_free_fwnode_static(fwnode);
                return;
            }
        }

}
从iort_init_platform_devices 中可以知道当前kernel同时support SMMU 和SMMUV3
iort_add_smmu_platform_device->acpi_dma_configure->arch_setup_dma_ops->acpi_dma_configure
在acpi_dma_configure 中会调用arch_setup_dma_ops 这里需要注意attr == DEV_DMA_COHERENT
    arch_setup_dma_ops(dev, 0, dev->coherent_dma_mask + 1, iommu,
               attr == DEV_DMA_COHERENT);
在arch_setup_dma_ops 函数中dev->archdata.dma_ops == NULL
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 = &swiotlb_dma_ops 以防后面iommu赋值失败。这里继续调用__iommu_setup_dma_ops
static void __iommu_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
                  const struct iommu_ops *ops)
{
    struct iommu_group *group;

    if (!ops)
        return;
    /*
     * TODO: As a concession to the future, we're ready to handle being
     * called both early and late (i.e. after bus_add_device). Once all
     * the platform bus code is reworked to call us late and the notifier
     * junk above goes away, move the body of do_iommu_attach here.
     */
    group = iommu_group_get(dev);
    if (group) {
        do_iommu_attach(dev, ops, dma_base, size);
        iommu_group_put(group);
    } else {
        queue_iommu_attach(dev, ops, dma_base, size);
    }
}
在__iommu_setup_dma_ops 中ops显然部位NULL,且group也不为null,因此调用do_iommu_attach
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;
}
哈哈哈,在do_iommu_attach 中终于赋值了dev->archdata.dma_ops = &iommu_dma_ops;


你可能感兴趣的:(Linux,源码分析)