diff options
author | Will Deacon <will.deacon@arm.com> | 2017-07-06 10:55:48 -0400 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2017-07-20 05:30:26 -0400 |
commit | 8e517e762a826d16451fb6ffb0a8722e4265582e (patch) | |
tree | 6cc0c59ccd7770892d5bddb8c23afc381c853d8c | |
parent | 5771a8c08880cdca3bfb4a3fc6d309d6bba20877 (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.c | 11 |
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, | |||
602 | static void arm_smmu_tlb_sync_global(struct arm_smmu_device *smmu) | 604 | static 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 | ||
610 | static void arm_smmu_tlb_sync_context(void *cookie) | 615 | static 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 | ||
620 | static void arm_smmu_tlb_sync_vmid(void *cookie) | 628 | static 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; |