[PATCH v4 18/26] iommu/arm-smmu-v3: Hook up ATC invalidation to mm ops

Jean-Philippe Brucker
2020-02-24T18:29:47Z

From: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>

The core calls us when an mm is modified. Perform the required ATC
invalidations.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
drivers/iommu/arm-smmu-v3.c | 44 ++++++++++++++++++++++++++++++++-----
1 file changed, 38 insertions(+), 6 deletions(-)

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 3973f7222864..95b4caceae1a 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -2354,6 +2354,20 @@ arm_smmu_atc_inv_to_cmd(int ssid, unsigned long iova, size_t size,
size_t inval_grain_shift = 12;
unsigned long page_start, page_end;

+ /*
+ * ATS and PASID:
+ *
+ * If substream_valid is clear, the PCIe TLP is sent without a PASID
+ * prefix. In that case all ATC entries within the address range are
+ * invalidated, including those that were requested with a PASID! There
+ * is no way to invalidate only entries without PASID.
+ *
+ * When using STRTAB_STE_1_S1DSS_SSID0 (reserving CD 0 for non-PASID
+ * traffic), translation requests without PASID create ATC entries
+ * without PASID, which must be invalidated with substream_valid clear.
+ * This has the unpleasant side-effect of invalidating all PASID-tagged
+ * ATC entries within the address range.
+ */
*cmd = (struct arm_smmu_cmdq_ent) {
.opcode = CMDQ_OP_ATC_INV,
.substream_valid = !!ssid,
@@ -2397,12 +2411,12 @@ arm_smmu_atc_inv_to_cmd(int ssid, unsigned long iova, size_t size,
cmd->atc.size = log2_span;
}

-static int arm_smmu_atc_inv_master(struct arm_smmu_master *master)
+static int arm_smmu_atc_inv_master(struct arm_smmu_master *master, int ssid)
{
int i;
struct arm_smmu_cmdq_ent cmd;

- arm_smmu_atc_inv_to_cmd(0, 0, 0, &cmd);
+ arm_smmu_atc_inv_to_cmd(ssid, 0, 0, &cmd);

for (i = 0; i < master->num_sids; i++) {
cmd.atc.sid = master->sids[i];
@@ -2874,7 +2888,7 @@ static void arm_smmu_disable_ats(struct arm_smmu_master *master)
* ATC invalidation via the SMMU.
*/
wmb();
- arm_smmu_atc_inv_master(master);
+ arm_smmu_atc_inv_master(master, 0);
atomic_dec(&smmu_domain->nr_ats_masters);
}

@@ -3065,7 +3079,22 @@ arm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
static void arm_smmu_mm_invalidate(struct device *dev, int pasid, void *entry,
unsigned long iova, size_t size)
{
- /* TODO: Invalidate ATC */
+ int i;
+ struct arm_smmu_cmdq_ent cmd;
+ struct arm_smmu_cmdq_batch cmds = {};
+ struct arm_smmu_master *master = dev_to_master(dev);
+
+ if (!master->ats_enabled)
+ return;
+
+ arm_smmu_atc_inv_to_cmd(pasid, iova, size, &cmd);
+
+ for (i = 0; i < master->num_sids; i++) {
+ cmd.atc.sid = master->sids[i];
+ arm_smmu_cmdq_batch_add(master->smmu, &cmds, &cmd);
+ }
+
+ arm_smmu_cmdq_batch_submit(master->smmu, &cmds);
}

static int arm_smmu_mm_attach(struct device *dev, int pasid, void *entry,
@@ -3089,6 +3118,7 @@ static void arm_smmu_mm_detach(struct device *dev, int pasid, void *entry,
bool detach_domain)
{
struct arm_smmu_ctx_desc *cd = entry;
+ struct arm_smmu_master *master = dev_to_master(dev);
struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);

@@ -3102,9 +3132,11 @@ static void arm_smmu_mm_detach(struct device *dev, int pasid, void *entry,
* invalidation.
*/
arm_smmu_tlb_inv_asid(smmu_domain->smmu, cd->asid);
- }
+ arm_smmu_atc_inv_domain(smmu_domain, pasid, 0, 0);

- /* TODO: invalidate ATC */
+ } else if (master->ats_enabled) {
+ arm_smmu_atc_inv_master(master, pasid);
+ }
}

static void *arm_smmu_mm_alloc(struct mm_struct *mm)
--
2.25.0