[PATCH v2] iommu/arm-smmu-v3: Allow default substream bypass with a pasid support

From: Nicolin Chen
Date: Thu Aug 17 2023 - 00:23:29 EST


When an iommu_domain is set to IOMMU_DOMAIN_IDENTITY, the driver sets the
arm_smmu_domain->stage to ARM_SMMU_DOMAIN_BYPASS and skips the allocation
of a CD table, and then sets STRTAB_STE_0_CFG_BYPASS to the CONFIG field
of the STE. This works well for devices that only have one substream, i.e.
pasid disabled.

With a pasid-capable device, however, there could be a use case where it
allows an IDENTITY domain attachment without disabling its pasid feature.
This requires the driver to allocate a multi-entry CD table to attach the
IDENTITY domain to its default substream and to configure the S1DSS filed
of the STE to STRTAB_STE_1_S1DSS_BYPASS. So, there is a missing link here
between the STE setup and an IDENTITY domain attachment.

Add a new stage ARM_SMMU_DOMAIN_BYPASS_S1DSS to tag this configuration by
overriding the ARM_SMMU_DOMAIN_BYPASS if the device has pasid capability.
This new tag will allow the driver allocating a CD table yet skipping an
CD insertion from the IDENTITY domain, and setting up the STE accordingly.

In a use case of ARM_SMMU_DOMAIN_BYPASS_S1DSS, the SHCFG field of the STE
should be set to STRTAB_STE_1_SHCFG_INCOMING. In other cases of having a
CD table, the shareability comes from a CD, not the SHCFG field: according
to "13.5 Summary of attribute/permission configuration fields" in the spec
the SHCFG field value is irrelevant. So, always configure the SHCFG field
of the STE to STRTAB_STE_1_SHCFG_INCOMING when a CD table is present, for
simplification.

Signed-off-by: Nicolin Chen <nicolinc@xxxxxxxxxx>
---

Changelog
v2:
* Rebased on top of Michael's series reworking CD table ownership:
https://lore.kernel.org/all/20230816131925.2521220-1-mshavit@xxxxxxxxxx/
* Added a new ARM_SMMU_DOMAIN_BYPASS_S1DSS stage to tag the use case
v1: https://lore.kernel.org/all/20230627033326.5236-1-nicolinc@xxxxxxxxxx/

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 22 +++++++++++++++++++--
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 +
2 files changed, 21 insertions(+), 2 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 b27011b2bec9..860db4fbb995 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -1271,6 +1271,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
* 3. Update Config, sync
*/
u64 val = le64_to_cpu(dst[0]);
+ u8 s1dss = STRTAB_STE_1_S1DSS_SSID0;
bool ste_live = false;
struct arm_smmu_device *smmu = NULL;
struct arm_smmu_ctx_desc_cfg *cd_table = NULL;
@@ -1290,6 +1291,9 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,

if (smmu_domain) {
switch (smmu_domain->stage) {
+ case ARM_SMMU_DOMAIN_BYPASS_S1DSS:
+ s1dss = STRTAB_STE_1_S1DSS_BYPASS;
+ fallthrough;
case ARM_SMMU_DOMAIN_S1:
cd_table = &master->cd_table;
break;
@@ -1348,7 +1352,8 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,

BUG_ON(ste_live);
dst[1] = cpu_to_le64(
- FIELD_PREP(STRTAB_STE_1_S1DSS, STRTAB_STE_1_S1DSS_SSID0) |
+ FIELD_PREP(STRTAB_STE_1_S1DSS, s1dss) |
+ FIELD_PREP(STRTAB_STE_1_SHCFG, STRTAB_STE_1_SHCFG_INCOMING) |
FIELD_PREP(STRTAB_STE_1_S1CIR, STRTAB_STE_1_S1C_CACHE_WBRA) |
FIELD_PREP(STRTAB_STE_1_S1COR, STRTAB_STE_1_S1C_CACHE_WBRA) |
FIELD_PREP(STRTAB_STE_1_S1CSH, ARM_SMMU_SH_ISH) |
@@ -2435,6 +2440,16 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
} else if (smmu_domain->smmu != smmu)
ret = -EINVAL;

+ /*
+ * When attaching an IDENTITY domain to a master with pasid capability,
+ * the master can still enable SVA feature by allocating a multi-entry
+ * CD table and attaching the IDENTITY domain to its default substream
+ * that alone can be byassed using the S1DSS field of the STE.
+ */
+ if (smmu_domain->stage == ARM_SMMU_DOMAIN_BYPASS && master->ssid_bits &&
+ smmu->features & ARM_SMMU_FEAT_TRANS_S1)
+ smmu_domain->stage = ARM_SMMU_DOMAIN_BYPASS_S1DSS;
+
mutex_unlock(&smmu_domain->init_mutex);
if (ret)
return ret;
@@ -2456,7 +2471,8 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
list_add(&master->domain_head, &smmu_domain->devices);
spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);

- if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+ if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1 ||
+ smmu_domain->stage == ARM_SMMU_DOMAIN_BYPASS_S1DSS) {
if (!master->cd_table.cdtab) {
ret = arm_smmu_alloc_cd_tables(master);
if (ret) {
@@ -2464,7 +2480,9 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
goto out_list_del;
}
}
+ }

+ if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
/*
* Prevent SVA from concurrently modifying the CD or writing to
* the CD entry
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 b7a91c8e9b52..e9361f85c91c 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -714,6 +714,7 @@ enum arm_smmu_domain_stage {
ARM_SMMU_DOMAIN_S2,
ARM_SMMU_DOMAIN_NESTED,
ARM_SMMU_DOMAIN_BYPASS,
+ ARM_SMMU_DOMAIN_BYPASS_S1DSS, /* Bypass S1 default substream only */
};

struct arm_smmu_domain {

base-commit: aed5d77d0c3d55d1949db89f27cf7a3981261ef4
--
2.41.0