@@ -2375,6 +2375,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,
@@ -2418,12 +2432,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];
@@ -2934,7 +2948,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);
}
@@ -3131,7 +3145,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_iommu_priv_get(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,
@@ -3168,26 +3197,43 @@ static void arm_smmu_mm_clear(struct device *dev, int pasid, void *entry)
* for this ASID, so we need to do it manually.
*/
arm_smmu_tlb_inv_asid(smmu_domain->smmu, cd->asid);
-
- /* TODO: invalidate ATC */
+ arm_smmu_atc_inv_domain(smmu_domain, pasid, 0, 0);
}
static void arm_smmu_mm_detach(struct device *dev, int pasid, void *entry,
bool detach_domain, bool cleared)
{
struct arm_smmu_ctx_desc *cd = entry;
+ struct arm_smmu_master *master = dev_iommu_priv_get(dev);
struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
- if (detach_domain) {
+ if (detach_domain)
arm_smmu_write_ctx_desc(smmu_domain, pasid, NULL);
- if (!cleared)
- /* See comment in arm_smmu_mm_clear() */
- arm_smmu_tlb_inv_asid(smmu_domain->smmu, cd->asid);
- }
+ /*
+ * If we went through clear(), we've already invalidated, and no new TLB
+ * entry can have been formed.
+ */
+ if (cleared)
+ return;
+
+ if (detach_domain) {
+ /* See comment in arm_smmu_mm_clear() */
+ 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) {
+ /*
+ * There are more devices bound with this PASID in this domain,
+ * so we cannot yet clear the PASID entry, and this device could
+ * create new ATC entries. Invalidate the ATC for the sake of
+ * it. On unbinding the last device we'll properly invalidate
+ * all ATCs in the domain. Alternatively, an early detach_dev()
+ * on this device will also flush the ATC.
+ */
+ arm_smmu_atc_inv_master(master, pasid);
+ }
}
static void *arm_smmu_mm_alloc(struct mm_struct *mm)
iommu-sva calls us when an mm is modified. Perform the required ATC invalidations. Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org> --- v4->v5: more comments --- drivers/iommu/arm-smmu-v3.c | 70 ++++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 12 deletions(-)