aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2017-07-06 10:55:48 -0400
committerWill Deacon <will.deacon@arm.com>2017-07-20 05:30:26 -0400
commit8e517e762a826d16451fb6ffb0a8722e4265582e (patch)
tree6cc0c59ccd7770892d5bddb8c23afc381c853d8c
parent5771a8c08880cdca3bfb4a3fc6d309d6bba20877 (diff)
iommu/arm-smmu: Reintroduce locking around TLB sync operations
Commit 523d7423e21b ("iommu/arm-smmu: Remove io-pgtable spinlock") removed the locking used to serialise map/unmap calls into the io-pgtable code from the ARM SMMU driver. This is good for performance, but opens us up to a nasty race with TLB syncs because the TLB sync register is shared within a context bank (or even globally for stage-2 on SMMUv1). There are two cases to consider: 1. A CPU can be spinning on the completion of a TLB sync, take an interrupt which issues a subsequent TLB sync, and then report a timeout on return from the interrupt. 2. A CPU can be spinning on the completion of a TLB sync, but other CPUs can continuously issue additional TLB syncs in such a way that the backoff logic reports a timeout. Rather than fix this by spinning for completion of prior TLB syncs before issuing a new one (which may suffer from fairness issues on large systems), instead reintroduce locking around TLB sync operations in the ARM SMMU driver. Fixes: 523d7423e21b ("iommu/arm-smmu: Remove io-pgtable spinlock") Cc: Robin Murphy <robin.murphy@arm.com> Reported-by: Ray Jui <ray.jui@broadcom.com> Tested-by: Ray Jui <ray.jui@broadcom.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r--drivers/iommu/arm-smmu.c11
1 files changed, 10 insertions, 1 deletions
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index bc89b4d6c043..98ff462b5992 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -400,6 +400,8 @@ struct arm_smmu_device {
400 400
401 u32 cavium_id_base; /* Specific to Cavium */ 401 u32 cavium_id_base; /* Specific to Cavium */
402 402
403 spinlock_t global_sync_lock;
404
403 /* IOMMU core code handle */ 405 /* IOMMU core code handle */
404 struct iommu_device iommu; 406 struct iommu_device iommu;
405}; 407};
@@ -436,7 +438,7 @@ struct arm_smmu_domain {
436 struct arm_smmu_cfg cfg; 438 struct arm_smmu_cfg cfg;
437 enum arm_smmu_domain_stage stage; 439 enum arm_smmu_domain_stage stage;
438 struct mutex init_mutex; /* Protects smmu pointer */ 440 struct mutex init_mutex; /* Protects smmu pointer */
439 spinlock_t cb_lock; /* Serialises ATS1* ops */ 441 spinlock_t cb_lock; /* Serialises ATS1* ops and TLB syncs */
440 struct iommu_domain domain; 442 struct iommu_domain domain;
441}; 443};
442 444
@@ -602,9 +604,12 @@ static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu,
602static void arm_smmu_tlb_sync_global(struct arm_smmu_device *smmu) 604static void arm_smmu_tlb_sync_global(struct arm_smmu_device *smmu)
603{ 605{
604 void __iomem *base = ARM_SMMU_GR0(smmu); 606 void __iomem *base = ARM_SMMU_GR0(smmu);
607 unsigned long flags;
605 608
609 spin_lock_irqsave(&smmu->global_sync_lock, flags);
606 __arm_smmu_tlb_sync(smmu, base + ARM_SMMU_GR0_sTLBGSYNC, 610 __arm_smmu_tlb_sync(smmu, base + ARM_SMMU_GR0_sTLBGSYNC,
607 base + ARM_SMMU_GR0_sTLBGSTATUS); 611 base + ARM_SMMU_GR0_sTLBGSTATUS);
612 spin_unlock_irqrestore(&smmu->global_sync_lock, flags);
608} 613}
609 614
610static void arm_smmu_tlb_sync_context(void *cookie) 615static void arm_smmu_tlb_sync_context(void *cookie)
@@ -612,9 +617,12 @@ static void arm_smmu_tlb_sync_context(void *cookie)
612 struct arm_smmu_domain *smmu_domain = cookie; 617 struct arm_smmu_domain *smmu_domain = cookie;
613 struct arm_smmu_device *smmu = smmu_domain->smmu; 618 struct arm_smmu_device *smmu = smmu_domain->smmu;
614 void __iomem *base = ARM_SMMU_CB(smmu, smmu_domain->cfg.cbndx); 619 void __iomem *base = ARM_SMMU_CB(smmu, smmu_domain->cfg.cbndx);
620 unsigned long flags;
615 621
622 spin_lock_irqsave(&smmu_domain->cb_lock, flags);
616 __arm_smmu_tlb_sync(smmu, base + ARM_SMMU_CB_TLBSYNC, 623 __arm_smmu_tlb_sync(smmu, base + ARM_SMMU_CB_TLBSYNC,
617 base + ARM_SMMU_CB_TLBSTATUS); 624 base + ARM_SMMU_CB_TLBSTATUS);
625 spin_unlock_irqrestore(&smmu_domain->cb_lock, flags);
618} 626}
619 627
620static void arm_smmu_tlb_sync_vmid(void *cookie) 628static void arm_smmu_tlb_sync_vmid(void *cookie)
@@ -1925,6 +1933,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
1925 1933
1926 smmu->num_mapping_groups = size; 1934 smmu->num_mapping_groups = size;
1927 mutex_init(&smmu->stream_map_mutex); 1935 mutex_init(&smmu->stream_map_mutex);
1936 spin_lock_init(&smmu->global_sync_lock);
1928 1937
1929 if (smmu->version < ARM_SMMU_V2 || !(id & ID0_PTFS_NO_AARCH32)) { 1938 if (smmu->version < ARM_SMMU_V2 || !(id & ID0_PTFS_NO_AARCH32)) {
1930 smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_L; 1939 smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_L;