iommu 的dma_strict

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tiantao2012/article/details/88050429
在iommu.c 中有定义一个变量,让iommu默认就设置的是dma_strict
static 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.c
static 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.c
static 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来定时刷新tlb
int 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_flush
static 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,来提高性能

猜你喜欢

转载自blog.csdn.net/tiantao2012/article/details/88050429
DMA