[PATCH 3.13 138/172] iommu/arm-smmu: really fix page table locking

From: Greg Kroah-Hartman
Date: Tue Mar 04 2014 - 15:57:52 EST


3.13-stable review patch. If anyone has any objections, please let me know.

------------------

From: Will Deacon <will.deacon@xxxxxxx>

commit c9d09e2748eaa55cac2af274574baa6368189bc1 upstream.

Commit a44a9791e778 ("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.

Reported-by: Andreas Herrmann <andreas.herrmann@xxxxxxxxxxx>
Signed-off-by: Will Deacon <will.deacon@xxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>

---
drivers/iommu/arm-smmu.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)

--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -392,7 +392,7 @@ struct arm_smmu_domain {
struct arm_smmu_cfg root_cfg;
phys_addr_t output_mask;

- struct mutex lock;
+ spinlock_t lock;
};

static DEFINE_SPINLOCK(arm_smmu_devices_lock);
@@ -900,7 +900,7 @@ static int arm_smmu_domain_init(struct i
goto out_free_domain;
smmu_domain->root_cfg.pgd = pgd;

- mutex_init(&smmu_domain->lock);
+ spin_lock_init(&smmu_domain->lock);
domain->priv = smmu_domain;
return 0;

@@ -1137,7 +1137,7 @@ static int arm_smmu_attach_dev(struct io
* Sanity check the domain. We don't currently support domains
* that cross between different SMMU chains.
*/
- mutex_lock(&smmu_domain->lock);
+ spin_lock(&smmu_domain->lock);
if (!smmu_domain->leaf_smmu) {
/* Now that we have a master, we can finalise the domain */
ret = arm_smmu_init_domain_context(domain, dev);
@@ -1152,7 +1152,7 @@ static int arm_smmu_attach_dev(struct io
dev_name(device_smmu->dev));
goto err_unlock;
}
- mutex_unlock(&smmu_domain->lock);
+ spin_unlock(&smmu_domain->lock);

/* Looks ok, so add the device to the domain */
master = find_smmu_master(smmu_domain->leaf_smmu, dev->of_node);
@@ -1162,7 +1162,7 @@ static int arm_smmu_attach_dev(struct io
return arm_smmu_domain_add_master(smmu_domain, master);

err_unlock:
- mutex_unlock(&smmu_domain->lock);
+ spin_unlock(&smmu_domain->lock);
return ret;
}

@@ -1209,7 +1209,7 @@ static int arm_smmu_alloc_init_pte(struc

if (pmd_none(*pmd)) {
/* Allocate a new set of tables */
- pgtable_t table = alloc_page(PGALLOC_GFP);
+ pgtable_t table = alloc_page(GFP_ATOMIC|__GFP_ZERO);
if (!table)
return -ENOMEM;

@@ -1314,7 +1314,7 @@ static int arm_smmu_alloc_init_pmd(struc

#ifndef __PAGETABLE_PMD_FOLDED
if (pud_none(*pud)) {
- pmd = pmd_alloc_one(NULL, addr);
+ pmd = (pmd_t *)get_zeroed_page(GFP_ATOMIC);
if (!pmd)
return -ENOMEM;

@@ -1346,7 +1346,7 @@ static int arm_smmu_alloc_init_pud(struc

#ifndef __PAGETABLE_PUD_FOLDED
if (pgd_none(*pgd)) {
- pud = pud_alloc_one(NULL, addr);
+ pud = (pud_t *)get_zeroed_page(GFP_ATOMIC);
if (!pud)
return -ENOMEM;

@@ -1400,7 +1400,7 @@ static int arm_smmu_handle_mapping(struc
if (paddr & ~output_mask)
return -ERANGE;

- mutex_lock(&smmu_domain->lock);
+ spin_lock(&smmu_domain->lock);
pgd += pgd_index(iova);
end = iova + size;
do {
@@ -1416,7 +1416,7 @@ static int arm_smmu_handle_mapping(struc
} while (pgd++, iova != end);

out_unlock:
- mutex_unlock(&smmu_domain->lock);
+ spin_unlock(&smmu_domain->lock);

/* Ensure new page tables are visible to the hardware walker */
if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK)


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/