summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWill Deacon <will@kernel.org>2019-08-20 10:12:12 -0400
committerWill Deacon <will@kernel.org>2019-08-21 12:58:54 -0400
commit353e3cf8590cf182a9f42e67993de3aca91e8090 (patch)
tree8b623153a9c66e9ad540d13f8dc9831e0626f1ad
parentbfff88ec1afefb76669d29da9fc90d5ad7c53ec3 (diff)
iommu/arm-smmu-v3: Fix ATC invalidation ordering wrt main TLBs
When invalidating the ATC for an PCIe endpoint using ATS, we must take care to complete invalidation of the main SMMU TLBs beforehand, otherwise the device could immediately repopulate its ATC with stale translations. Hooking the ATC invalidation into ->unmap() as we currently do does the exact opposite: it ensures that the ATC is invalidated *before* the main TLBs, which is bogus. Move ATC invalidation into the actual (leaf) invalidation routines so that it is always called after completing main TLB invalidation. Reviewed-by: Robin Murphy <robin.murphy@arm.com> Signed-off-by: Will Deacon <will@kernel.org>
-rw-r--r--drivers/iommu/arm-smmu-v3.c16
1 files changed, 9 insertions, 7 deletions
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index d7c65dfe42dc..ca504a60312d 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1961,6 +1961,7 @@ static void arm_smmu_tlb_inv_context(void *cookie)
1961 */ 1961 */
1962 arm_smmu_cmdq_issue_cmd(smmu, &cmd); 1962 arm_smmu_cmdq_issue_cmd(smmu, &cmd);
1963 arm_smmu_cmdq_issue_sync(smmu); 1963 arm_smmu_cmdq_issue_sync(smmu);
1964 arm_smmu_atc_inv_domain(smmu_domain, 0, 0, 0);
1964} 1965}
1965 1966
1966static void arm_smmu_tlb_inv_range(unsigned long iova, size_t size, 1967static void arm_smmu_tlb_inv_range(unsigned long iova, size_t size,
@@ -1969,7 +1970,7 @@ static void arm_smmu_tlb_inv_range(unsigned long iova, size_t size,
1969{ 1970{
1970 u64 cmds[CMDQ_BATCH_ENTRIES * CMDQ_ENT_DWORDS]; 1971 u64 cmds[CMDQ_BATCH_ENTRIES * CMDQ_ENT_DWORDS];
1971 struct arm_smmu_device *smmu = smmu_domain->smmu; 1972 struct arm_smmu_device *smmu = smmu_domain->smmu;
1972 unsigned long end = iova + size; 1973 unsigned long start = iova, end = iova + size;
1973 int i = 0; 1974 int i = 0;
1974 struct arm_smmu_cmdq_ent cmd = { 1975 struct arm_smmu_cmdq_ent cmd = {
1975 .tlbi = { 1976 .tlbi = {
@@ -2001,6 +2002,12 @@ static void arm_smmu_tlb_inv_range(unsigned long iova, size_t size,
2001 } 2002 }
2002 2003
2003 arm_smmu_cmdq_issue_cmdlist(smmu, cmds, i, true); 2004 arm_smmu_cmdq_issue_cmdlist(smmu, cmds, i, true);
2005
2006 /*
2007 * Unfortunately, this can't be leaf-only since we may have
2008 * zapped an entire table.
2009 */
2010 arm_smmu_atc_inv_domain(smmu_domain, 0, start, size);
2004} 2011}
2005 2012
2006static void arm_smmu_tlb_inv_page_nosync(struct iommu_iotlb_gather *gather, 2013static void arm_smmu_tlb_inv_page_nosync(struct iommu_iotlb_gather *gather,
@@ -2420,18 +2427,13 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
2420static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, 2427static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
2421 size_t size, struct iommu_iotlb_gather *gather) 2428 size_t size, struct iommu_iotlb_gather *gather)
2422{ 2429{
2423 int ret;
2424 struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 2430 struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
2425 struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops; 2431 struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
2426 2432
2427 if (!ops) 2433 if (!ops)
2428 return 0; 2434 return 0;
2429 2435
2430 ret = ops->unmap(ops, iova, size, gather); 2436 return ops->unmap(ops, iova, size, gather);
2431 if (ret && arm_smmu_atc_inv_domain(smmu_domain, 0, iova, size))
2432 return 0;
2433
2434 return ret;
2435} 2437}
2436 2438
2437static void arm_smmu_flush_iotlb_all(struct iommu_domain *domain) 2439static void arm_smmu_flush_iotlb_all(struct iommu_domain *domain)