提交 c9d09e27 编写于 作者: W Will Deacon

iommu/arm-smmu: really fix page table locking

Commit a44a9791 ("iommu/arm-smmu: use mutex instead of spinlock for
locking page tables") replaced the page table spinlock with a mutex, to
allow blocking allocations to satisfy lazy mapping requests.

Unfortunately, it turns out that IOMMU mappings are created from atomic
context (e.g. spinlock held during a dma_map), so this change doesn't
really help us in practice.

This patch is a partial revert of the offending commit, bringing back
the original spinlock but replacing our page table allocations for any
levels below the pgd (which is allocated during domain init) with
GFP_ATOMIC instead of GFP_KERNEL.

Cc: <stable@vger.kernel.org>
Reported-by: NAndreas Herrmann <andreas.herrmann@calxeda.com>
Signed-off-by: NWill Deacon <will.deacon@arm.com>
上级 97a64420
...@@ -393,7 +393,7 @@ struct arm_smmu_domain { ...@@ -393,7 +393,7 @@ struct arm_smmu_domain {
struct arm_smmu_cfg root_cfg; struct arm_smmu_cfg root_cfg;
phys_addr_t output_mask; phys_addr_t output_mask;
struct mutex lock; spinlock_t lock;
}; };
static DEFINE_SPINLOCK(arm_smmu_devices_lock); static DEFINE_SPINLOCK(arm_smmu_devices_lock);
...@@ -901,7 +901,7 @@ static int arm_smmu_domain_init(struct iommu_domain *domain) ...@@ -901,7 +901,7 @@ static int arm_smmu_domain_init(struct iommu_domain *domain)
goto out_free_domain; goto out_free_domain;
smmu_domain->root_cfg.pgd = pgd; smmu_domain->root_cfg.pgd = pgd;
mutex_init(&smmu_domain->lock); spin_lock_init(&smmu_domain->lock);
domain->priv = smmu_domain; domain->priv = smmu_domain;
return 0; return 0;
...@@ -1138,7 +1138,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) ...@@ -1138,7 +1138,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
* Sanity check the domain. We don't currently support domains * Sanity check the domain. We don't currently support domains
* that cross between different SMMU chains. * that cross between different SMMU chains.
*/ */
mutex_lock(&smmu_domain->lock); spin_lock(&smmu_domain->lock);
if (!smmu_domain->leaf_smmu) { if (!smmu_domain->leaf_smmu) {
/* Now that we have a master, we can finalise the domain */ /* Now that we have a master, we can finalise the domain */
ret = arm_smmu_init_domain_context(domain, dev); ret = arm_smmu_init_domain_context(domain, dev);
...@@ -1153,7 +1153,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) ...@@ -1153,7 +1153,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
dev_name(device_smmu->dev)); dev_name(device_smmu->dev));
goto err_unlock; goto err_unlock;
} }
mutex_unlock(&smmu_domain->lock); spin_unlock(&smmu_domain->lock);
/* Looks ok, so add the device to the domain */ /* Looks ok, so add the device to the domain */
master = find_smmu_master(smmu_domain->leaf_smmu, dev->of_node); master = find_smmu_master(smmu_domain->leaf_smmu, dev->of_node);
...@@ -1163,7 +1163,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) ...@@ -1163,7 +1163,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
return arm_smmu_domain_add_master(smmu_domain, master); return arm_smmu_domain_add_master(smmu_domain, master);
err_unlock: err_unlock:
mutex_unlock(&smmu_domain->lock); spin_unlock(&smmu_domain->lock);
return ret; return ret;
} }
...@@ -1210,7 +1210,7 @@ static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd, ...@@ -1210,7 +1210,7 @@ static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
if (pmd_none(*pmd)) { if (pmd_none(*pmd)) {
/* Allocate a new set of tables */ /* Allocate a new set of tables */
pgtable_t table = alloc_page(PGALLOC_GFP); pgtable_t table = alloc_page(GFP_ATOMIC|__GFP_ZERO);
if (!table) if (!table)
return -ENOMEM; return -ENOMEM;
...@@ -1317,7 +1317,7 @@ static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud, ...@@ -1317,7 +1317,7 @@ static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud,
#ifndef __PAGETABLE_PMD_FOLDED #ifndef __PAGETABLE_PMD_FOLDED
if (pud_none(*pud)) { if (pud_none(*pud)) {
pmd = pmd_alloc_one(NULL, addr); pmd = (pmd_t *)get_zeroed_page(GFP_ATOMIC);
if (!pmd) if (!pmd)
return -ENOMEM; return -ENOMEM;
...@@ -1349,7 +1349,7 @@ static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd, ...@@ -1349,7 +1349,7 @@ static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd,
#ifndef __PAGETABLE_PUD_FOLDED #ifndef __PAGETABLE_PUD_FOLDED
if (pgd_none(*pgd)) { if (pgd_none(*pgd)) {
pud = pud_alloc_one(NULL, addr); pud = (pud_t *)get_zeroed_page(GFP_ATOMIC);
if (!pud) if (!pud)
return -ENOMEM; return -ENOMEM;
...@@ -1403,7 +1403,7 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain, ...@@ -1403,7 +1403,7 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
if (paddr & ~output_mask) if (paddr & ~output_mask)
return -ERANGE; return -ERANGE;
mutex_lock(&smmu_domain->lock); spin_lock(&smmu_domain->lock);
pgd += pgd_index(iova); pgd += pgd_index(iova);
end = iova + size; end = iova + size;
do { do {
...@@ -1419,7 +1419,7 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain, ...@@ -1419,7 +1419,7 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
} while (pgd++, iova != end); } while (pgd++, iova != end);
out_unlock: out_unlock:
mutex_unlock(&smmu_domain->lock); spin_unlock(&smmu_domain->lock);
/* Ensure new page tables are visible to the hardware walker */ /* Ensure new page tables are visible to the hardware walker */
if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册