[PATCH v2 14/18] iommu/arm-smmu-v3: Support domains with shared CDs

From: Michael Shavit
Date: Tue Jun 06 2023 - 08:11:31 EST


SVA may attach a CD to masters that have different upstream SMMU
devices. The arm_smmu_domain structure can only be attached to a single
upstream SMMU device however. To work around this limitation, we propose
an ARM_SMMU_DOMAIN_S1_SHARED domain type for domains that attach a CD
shared across with arm_smmu_domains (each attached to a different
upstream SMMU device).

Signed-off-by: Michael Shavit <mshavit@xxxxxxxxxx>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 26 ++++++++++++++++-----
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 2 ++
2 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index b7f834dde85d1..69b1d09fd0284 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -965,6 +965,20 @@ void arm_smmu_tlb_inv_asid(struct arm_smmu_device *smmu, u16 asid)
arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd);
}

+static struct arm_smmu_ctx_desc *arm_smmu_get_cd(struct arm_smmu_domain *domain)
+{
+ if (domain->stage == ARM_SMMU_DOMAIN_S1_SHARED_CD)
+ return domain->shared_cd;
+ else
+ return &domain->cd;
+}
+
+static bool arm_smmu_is_s1_domain(struct arm_smmu_domain *domain)
+{
+ return domain->stage == ARM_SMMU_DOMAIN_S1_SHARED_CD ||
+ domain->stage == ARM_SMMU_DOMAIN_S1;
+}
+
/* master may be null */
static void arm_smmu_sync_cd(struct arm_smmu_master *master,
int ssid, bool leaf)
@@ -1887,8 +1901,8 @@ static void arm_smmu_tlb_inv_context(void *cookie)
* insertion to guarantee those are observed before the TLBI. Do be
* careful, 007.
*/
- if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
- arm_smmu_tlb_inv_asid(smmu, smmu_domain->cd.asid);
+ if (arm_smmu_is_s1_domain(smmu_domain)) {
+ arm_smmu_tlb_inv_asid(smmu, arm_smmu_get_cd(smmu_domain)->asid);
} else {
cmd.opcode = CMDQ_OP_TLBI_S12_VMALL;
cmd.tlbi.vmid = smmu_domain->s2_cfg.vmid;
@@ -1968,10 +1982,10 @@ static void arm_smmu_tlb_inv_range_domain(unsigned long iova, size_t size,
},
};

- if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+ if (arm_smmu_is_s1_domain(smmu_domain)) {
cmd.opcode = smmu_domain->smmu->features & ARM_SMMU_FEAT_E2H ?
CMDQ_OP_TLBI_EL2_VA : CMDQ_OP_TLBI_NH_VA;
- cmd.tlbi.asid = smmu_domain->cd.asid;
+ cmd.tlbi.asid = arm_smmu_get_cd(smmu_domain)->asid;
} else {
cmd.opcode = CMDQ_OP_TLBI_S2_IPA;
cmd.tlbi.vmid = smmu_domain->s2_cfg.vmid;
@@ -2549,7 +2563,7 @@ static int arm_smmu_set_dev_pasid(struct iommu_domain *domain,
return -ENODEV;
}

- if (smmu_domain->stage != ARM_SMMU_DOMAIN_S1) {
+ if (!arm_smmu_is_s1_domain(smmu_domain)) {
dev_err(dev, "set_dev_pasid only supports stage 1 domains\n");
return -EINVAL;
}
@@ -2575,7 +2589,7 @@ static int arm_smmu_set_dev_pasid(struct iommu_domain *domain,
*/
mutex_lock(&arm_smmu_asid_lock);
ret = arm_smmu_write_ctx_desc(master->smmu, master->s1_cfg, master,
- pasid, &smmu_domain->cd);
+ pasid, arm_smmu_get_cd(smmu_domain));
if (ret) {
mutex_unlock(&arm_smmu_asid_lock);
kfree(attached_domain);
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 3525d60668c23..4ac69427abf1c 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -713,6 +713,7 @@ struct arm_smmu_master {
/* SMMU private data for an IOMMU domain */
enum arm_smmu_domain_stage {
ARM_SMMU_DOMAIN_S1 = 0,
+ ARM_SMMU_DOMAIN_S1_SHARED_CD,
ARM_SMMU_DOMAIN_S2,
ARM_SMMU_DOMAIN_NESTED,
ARM_SMMU_DOMAIN_BYPASS,
@@ -728,6 +729,7 @@ struct arm_smmu_domain {
enum arm_smmu_domain_stage stage;
union {
struct arm_smmu_ctx_desc cd;
+ struct arm_smmu_ctx_desc *shared_cd;
struct arm_smmu_s2_cfg s2_cfg;
};

--
2.41.0.rc0.172.g3f132b7071-goog