[PATCH RFC v5 6/6] iommu/arm-smmu-v3: Wire up shared-SID STE ordering and feature gating
flat view
COOLING7d
From: Peng Fan (OSS) <hidden>
Date: 2026-10-06 12:15:51
Also in:
linux-iommu, linux-tegra, lkml
Subsystem:
arm smmu drivers, arm smmu sva support, iommu subsystem, the rest · Maintainers:
Will Deacon, Joerg Roedel, Linus Torvalds
From: Peng Fan <peng.fan@nxp.com> For shared SIDs, only the first master to attach writes the STE (tracked by ste_installed under streams_mutex); subsequent masters skip the write. On teardown, skip the ABORT STE write while other masters still share the SID. Fault events on shared SIDs cannot be attributed to a specific master, so arm_smmu_find_master() returns NULL when the stream's shared_masters list is non-empty. Disable SVA, IOPF/stall, and vSMMU nesting for shared-SID masters since all three require unambiguous SID-to-device mapping. Clear stall_enabled at probe time so the STE is not programmed with the stall bit, which would cause unrecoverable bus lockups since CMD_RESUME cannot be issued without a unique SID-to-device mapping. Assisted-by: LLM Signed-off-by: Peng Fan <peng.fan@nxp.com> --- .../iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 2 +- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c | 3 ++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 47 ++++++++++++++++++++-- 3 files changed, 47 insertions(+), 5 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
index 0934a6bbd3e08..9f13154f832c1 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c@@ -309,7 +309,7 @@ static int arm_vsmmu_vdevice_init(struct iommufd_vdevice *vdev) * arm_vsmmu_vsid_to_sid() maps a vSID to master->streams[0] alone, so * more streams would leave the rest stale and none reads out of bounds. */ - if (master->num_streams != 1) + if (master->num_streams != 1 || !list_empty(&master->shared_masters_elm)) return -EOPNOTSUPP; return 0; }
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c
index 0a429c64fbf3e..54a0a65669ac2 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c@@ -271,6 +271,9 @@ static int arm_smmu_sva_set_dev_pasid(struct iommu_domain *domain, if (!(master->smmu->features & ARM_SMMU_FEAT_SVA)) return -EOPNOTSUPP; + if (!list_empty(&master->shared_masters_elm)) + return -EOPNOTSUPP; + /* Prevent arm_smmu_mm_release from being called while we are attaching */ if (!mmget_not_zero(domain->mm)) return -EINVAL;
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 c4cfe609ae93d..bf09c02538381 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c@@ -2977,11 +2977,36 @@ arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid) } } +static bool arm_smmu_is_shared_sid(struct arm_smmu_master *master) +{ + return !list_empty(&master->shared_masters_elm); +} + +static bool arm_smmu_skip_shared_ste(struct arm_smmu_device *smmu, + u32 sid, bool is_abort) +{ + struct arm_smmu_stream *stream; + + lockdep_assert_held(&smmu->streams_mutex); + + stream = arm_smmu_find_stream(smmu, sid); + if (!stream || list_empty(&stream->shared_masters)) + return false; + + if (is_abort) + return true; + if (stream->ste_installed) + return true; + stream->ste_installed = true; + return false; +} + void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master, const struct arm_smmu_ste *target) { int i, j; struct arm_smmu_device *smmu = master->smmu; + bool is_abort; master->cd_table.in_ste = FIELD_GET(STRTAB_STE_0_CFG, le64_to_cpu(target->data[0])) ==
@@ -2990,10 +3015,12 @@ void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master, FIELD_GET(STRTAB_STE_1_EATS, le64_to_cpu(target->data[1])) == STRTAB_STE_1_EATS_TRANS; + is_abort = FIELD_GET(STRTAB_STE_0_CFG, le64_to_cpu(target->data[0])) == + STRTAB_STE_0_CFG_ABORT; + for (i = 0; i < master->num_streams; ++i) { u32 sid = master->streams[i]->id; - struct arm_smmu_ste *step = - arm_smmu_get_step_for_sid(smmu, sid); + struct arm_smmu_ste *step; /* Bridged PCI devices may end up with duplicated IDs */ for (j = 0; j < i; j++)
@@ -3002,6 +3029,16 @@ void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master, if (j < i) continue; + if (arm_smmu_is_shared_sid(master)) { + mutex_lock(&smmu->streams_mutex); + if (arm_smmu_skip_shared_ste(smmu, sid, is_abort)) { + mutex_unlock(&smmu->streams_mutex); + continue; + } + mutex_unlock(&smmu->streams_mutex); + } + + step = arm_smmu_get_step_for_sid(smmu, sid); arm_smmu_write_ste(master, sid, step, target); } }
@@ -3149,8 +3186,7 @@ static int arm_smmu_enable_iopf(struct arm_smmu_master *master, if (!master->stall_enabled) return 0; - /* We're not keeping track of SIDs in fault events */ - if (master->num_streams != 1) + if (master->num_streams != 1 || arm_smmu_is_shared_sid(master)) return -EOPNOTSUPP; if (master->iopf_refcount) {
@@ -4331,6 +4367,9 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev) smmu->features & ARM_SMMU_FEAT_STALL_FORCE) master->stall_enabled = true; + if (arm_smmu_is_shared_sid(master)) + master->stall_enabled = false; + ret = arm_smmu_master_prepare_ats(master); if (ret) goto err_disable_pasid;
--
2.34.1