本文共 3772 字,大约阅读时间需要 12 分钟。
在iommu.c 中有定义一个变量,让iommu默认就设置的是dma_strictstatic bool iommu_dma_strict __read_mostly = true;那什么是dma_strict的呢?struct iommu_group *iommu_group_get_for_dev(struct device *dev){ if (!group->default_domain) { struct iommu_domain *dom; dom = __iommu_domain_alloc(dev->bus, iommu_def_domain_type); if (!dom && iommu_def_domain_type != IOMMU_DOMAIN_DMA) { dev_warn(dev, "failed to allocate default IOMMU domain of type %u; falling back to IOMMU_DOMAIN_DMA", iommu_def_domain_type); dom = __iommu_domain_alloc(dev->bus, IOMMU_DOMAIN_DMA); } group->default_domain = dom; if (!group->domain) group->domain = dom;#可以看到这个变量是在这里被利用的 if (dom && !iommu_dma_strict) { int attr = 1; iommu_domain_set_attr(dom, DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE, &attr); } }}iommu_domain_set_attr的实现如下:int iommu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr attr, void *data){ int ret = 0; switch (attr) { default: if (domain->ops->domain_set_attr == NULL) return -EINVAL;#这里的domain_set_attr的实现在具体的驱动中 ret = domain->ops->domain_set_attr(domain, attr, data); } return ret;}driver/iommu/arm_iommu-v3.cstatic int arm_smmu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr attr, void *data){ int ret = 0; struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); mutex_lock(&smmu_domain->init_mutex); switch (domain->type) { case IOMMU_DOMAIN_DMA: switch(attr) {#从上面一路调下来,这里看到这里的data其实等于1. case DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE: smmu_domain->non_strict = *(int *)data; break; default: ret = -ENODEV; } break; default: ret = -EINVAL; }}driver/iommu/arm_iommu-v3.cstatic int arm_smmu_domain_finalise(struct iommu_domain *domain){ #从这里可以看到又将non_strict转成IO_PGTABLE_QUIRK_NON_STRICT,及以后要判断这个flag if (smmu_domain->non_strict) pgtbl_cfg.quirks |= IO_PGTABLE_QUIRK_NON_STRICT;}static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *data, unsigned long iova, size_t size, int lvl, arm_v7s_iopte *ptep){ /* If the size matches this level, we're in the right place */ if (num_entries) { size_t blk_size = ARM_V7S_BLOCK_SIZE(lvl); __arm_v7s_set_pte(ptep, 0, num_entries, &iop->cfg); for (i = 0; i < num_entries; i++) { if (ARM_V7S_PTE_IS_TABLE(pte[i], lvl)) { /* Also flush any partial walks */ io_pgtable_tlb_add_flush(iop, iova, blk_size, ARM_V7S_BLOCK_SIZE(lvl + 1), false); io_pgtable_tlb_sync(iop); ptep = iopte_deref(pte[i], lvl); __arm_v7s_free_table(ptep, lvl + 1, data);#从这里看到所谓的no-strict就是不刷新iommu的tlb } else if (iop->cfg.quirks & IO_PGTABLE_QUIRK_NON_STRICT) { /* * Order the PTE update against queueing the IOVA, to * guarantee that a flush callback from a different CPU * has observed it before the TLBIALL can be issued. */ smp_wmb(); } else { io_pgtable_tlb_add_flush(iop, iova, blk_size, blk_size, true); } iova += blk_size; } return size;}那具体是在什么时候刷新tlb呢?原来是在下面的的函数中建立了一个timer来定时刷新tlbint init_iova_flush_queue(struct iova_domain *iovad, iova_flush_cb flush_cb, iova_entry_dtor entry_dtor){ timer_setup(&iovad->fq_timer, fq_flush_timeout, 0); atomic_set(&iovad->fq_timer_on, 0); return 0;}fq_flush_timeout->iova_domain_flushstatic void iova_domain_flush(struct iova_domain *iovad){ atomic64_inc(&iovad->fq_flush_start_cnt);#这里调用flush_cb来flush tlb iovad->flush_cb(iovad); atomic64_inc(&iovad->fq_flush_finish_cnt);}int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size, struct device *dev){ if (!cookie->fq_domain && !iommu_domain_get_attr(domain, DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE, &attr) && attr) { cookie->fq_domain = domain; init_iova_flush_queue(iovad, iommu_dma_flush_iotlb_all, NULL); }}从上面的函数可以看到flush_cb是iommu_dma_flush_iotlb_all所以iommu的non-strict 模式就是不刷新tlb,而是通过起一个timer来刷tlb,来提高性能
转载地址:http://vvnmi.baihongyu.com/