在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,来提高性能