mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Peng Fan (OSS)" <peng.fan@oss.nxp.com>
To: Will Deacon <will@kernel.org>,
	Robin Murphy <robin.murphy@arm.com>,
	 "Joerg Roedel (AMD)" <joro@8bytes.org>,
	 Jean-Philippe Brucker <jpb@kernel.org>,
	Nicolin Chen <nicolinc@nvidia.com>,
	 Jason Gunthorpe <jgg@ziepe.ca>,
	Thierry Reding <thierry.reding@kernel.org>,
	 Krishna Reddy <vdumpa@nvidia.com>,
	Jonathan Hunter <jonathanh@nvidia.com>,
	 Jason Gunthorpe <jgg@ziepe.ca>
Cc: linux-arm-kernel@lists.infradead.org, iommu@lists.linux.dev,
	 linux-kernel@vger.kernel.org, linux-tegra@vger.kernel.org,
	 Peng Fan <peng.fan@nxp.com>
Subject: [PATCH RFC v8 7/9] iommu/arm-smmu-v3: Wire up shared-SID STE ordering and feature gating
Date: Fri, 13 Nov 2026 21:27:19 +0800	[thread overview]
Message-ID: <20261113-b4-smmu-shared-sid-ver8-v8-7-587307a77f3a@nxp.com> (raw)
In-Reply-To: <20261113-b4-smmu-shared-sid-ver8-v8-0-587307a77f3a@nxp.com>

From: Peng Fan <peng.fan@nxp.com>

With the per-domain CD table, every non-PASID sharer of a SID builds the
same normal-domain STE: same S1ContextPtr, S1DSS and S1CDMAX. Disable
PASID and ATS for shared SIDs as well, so the remaining STE fields are
common too, and let any sharer program the STE. This also handles the
case where one device's driver attaches before the stream owner's driver:
the first attach now installs a valid STE instead of leaving the boot or
firmware STE in place.

A per-device release must not abort a shared STE while other sharers
remain, otherwise it would break their ongoing DMA. Use a dedicated
release domain instead of inferring release from the STE contents:
group-wide BLOCKED attaches still program the abort STE, while the
release domain skips writes until the last master no longer shares the
SID.

Disable SVA, PASID, vSMMU nesting, ATS and IOPF/stall for shared-SID
masters since all of them require an unambiguous SID-to-device mapping.
Reject sharing at insert_master() time if either master wants stalling,
if the incoming master requires ATS, if the existing master has PASID,
ATS or vSMMU state enabled, or if a PCI and a non-PCI master would share
the SID. Other ATS-capable shared devices have their ATS capability
prepared, but ATS is not enabled while the SID is shared.

Resolve stall_enabled before insert_master(), and use master->stall_enabled
directly for CD.S, STE.S1STALLD and STE.S2S. A shared master can never
have stalling enabled.

Add arm_smmu_master_shares_sid() as an inline helper in arm-smmu-v3.h so
that arm-smmu-v3-sva.c and arm-smmu-v3-iommufd.c can use it without
repeating the list_empty() pattern.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 .../iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c    |   7 +-
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c    |   3 +
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c   |   5 +
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c        | 236 ++++++++++++++++-----
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h        |   5 +
 drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c     |   2 +-
 6 files changed, 200 insertions(+), 58 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 853276dd5453..1fa512a655d2 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
@@ -168,6 +168,8 @@ static int arm_smmu_attach_dev_nested(struct iommu_domain *domain,
 
 	if (nested_domain->vsmmu->smmu != master->smmu)
 		return -EINVAL;
+	if (arm_smmu_master_shares_sid(master))
+		return -EOPNOTSUPP;
 	if (arm_smmu_ssids_in_use(&master->cd_table))
 		return -EBUSY;
 
@@ -310,7 +312,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 || arm_smmu_master_shares_sid(master))
 		return -EOPNOTSUPP;
 	return 0;
 }
@@ -545,6 +547,9 @@ size_t arm_smmu_get_viommu_size(struct device *dev,
 	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
 	struct arm_smmu_device *smmu = master->smmu;
 
+	if (arm_smmu_master_shares_sid(master))
+		return 0;
+
 	if (!(smmu->features & ARM_SMMU_FEAT_NESTING))
 		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 7f89bb7a2e9c..f65c60652c0d 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
@@ -281,6 +281,9 @@ static int arm_smmu_sva_set_dev_pasid(struct iommu_domain *domain,
 	if (!(master->smmu->features & ARM_SMMU_FEAT_SVA))
 		return -EOPNOTSUPP;
 
+	if (arm_smmu_master_shares_sid(master))
+		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-test.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c
index 864d9f2350bc..a3fbcd88675a 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c
@@ -215,6 +215,7 @@ static void arm_smmu_test_make_cdtable_ste(struct arm_smmu_ste *ste,
 		.stall_enabled = stall_enabled,
 	};
 
+	INIT_LIST_HEAD(&master.shared_masters_elm);
 	arm_smmu_make_cdtable_ste(ste, &master, &master.cd_table, ats_enabled,
 				  s1dss);
 	if (feat & ARM_SMMU_MASTER_TEST_NESTED) {
@@ -351,6 +352,7 @@ static void arm_smmu_test_make_s2_ste(struct arm_smmu_ste *ste,
 		.pgtbl_ops = &io_pgtable.ops,
 	};
 
+	INIT_LIST_HEAD(&master.shared_masters_elm);
 	io_pgtable.cfg.arm_lpae_s2_cfg.vttbr = 0xdaedbeefdeadbeefULL;
 	io_pgtable.cfg.arm_lpae_s2_cfg.vtcr.ps = 1;
 	io_pgtable.cfg.arm_lpae_s2_cfg.vtcr.tg = 2;
@@ -506,6 +508,7 @@ static void arm_smmu_test_make_s1_cd(struct arm_smmu_cd *cd, unsigned int asid)
 		},
 	};
 
+	INIT_LIST_HEAD(&master.shared_masters_elm);
 	io_pgtable.cfg.arm_lpae_s1_cfg.ttbr = 0xdaedbeefdeadbeefULL;
 	io_pgtable.cfg.arm_lpae_s1_cfg.tcr.ips = 1;
 	io_pgtable.cfg.arm_lpae_s1_cfg.tcr.tg = 2;
@@ -549,6 +552,7 @@ static void arm_smmu_test_make_sva_cd(struct arm_smmu_cd *cd, unsigned int asid)
 		.smmu = &smmu,
 	};
 
+	INIT_LIST_HEAD(&master.shared_masters_elm);
 	arm_smmu_make_sva_cd(cd, &master, &sva_mm, asid);
 }
 
@@ -559,6 +563,7 @@ static void arm_smmu_test_make_sva_release_cd(struct arm_smmu_cd *cd,
 		.smmu = &smmu,
 	};
 
+	INIT_LIST_HEAD(&master.shared_masters_elm);
 	arm_smmu_make_sva_cd(cd, &master, NULL, asid);
 }
 
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 20cbad032846..790a6e54c24a 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3209,28 +3209,18 @@ arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
 	}
 }
 
-void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master,
-				  const struct arm_smmu_ste *target)
+static void
+arm_smmu_install_ste_for_dev_flags(struct arm_smmu_master *master,
+				   const struct arm_smmu_ste *target,
+				   bool skip_shared)
 {
 	int i, j;
+	bool written = false;
 	struct arm_smmu_device *smmu = master->smmu;
-	u64 ste = le64_to_cpu(target->data[0]);
-
-	master->cd_table.in_ste =
-		FIELD_GET(STRTAB_STE_0_CFG, ste) ==
-			STRTAB_STE_0_CFG_S1_TRANS &&
-		(ste & STRTAB_STE_0_S1CTXPTR_MASK) ==
-			(master->cd_table.cdtab_dma &
-			 STRTAB_STE_0_S1CTXPTR_MASK);
-	master->ste_ats_enabled =
-		FIELD_GET(STRTAB_STE_1_EATS,
-			  le64_to_cpu(target->data[1])) ==
-		STRTAB_STE_1_EATS_TRANS;
 
 	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++)
@@ -3239,11 +3229,43 @@ void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master,
 		if (j < i)
 			continue;
 
+		/*
+		 * A shared STE represents the group's domain, not a single
+		 * master. The release domain is attached to one device at a
+		 * time while the other sharers must keep translating; leave
+		 * the STE untouched until the last master is released.
+		 */
+		if (skip_shared && arm_smmu_master_shares_sid(master))
+			continue;
+
+		step = arm_smmu_get_step_for_sid(smmu, sid);
 		arm_smmu_write_ste(master, sid, step, target);
+		written = true;
+	}
+
+	if (written) {
+		u64 ste = le64_to_cpu(target->data[0]);
+
+		master->cd_table.in_ste =
+			FIELD_GET(STRTAB_STE_0_CFG, ste) ==
+				STRTAB_STE_0_CFG_S1_TRANS &&
+			(ste & STRTAB_STE_0_S1CTXPTR_MASK) ==
+				(master->cd_table.cdtab_dma &
+				 STRTAB_STE_0_S1CTXPTR_MASK);
+		master->ste_ats_enabled =
+			FIELD_GET(STRTAB_STE_1_EATS,
+				  le64_to_cpu(target->data[1])) ==
+			STRTAB_STE_1_EATS_TRANS;
 	}
 }
 
-static bool arm_smmu_ats_supported(struct arm_smmu_master *master)
+void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master,
+				  const struct arm_smmu_ste *target)
+{
+	arm_smmu_install_ste_for_dev_flags(master, target, false);
+}
+
+static bool arm_smmu_ats_capable(struct arm_smmu_master *master)
 {
 	struct device *dev = master->dev;
 	struct arm_smmu_device *smmu = master->smmu;
@@ -3258,6 +3280,18 @@ static bool arm_smmu_ats_supported(struct arm_smmu_master *master)
 	return dev_is_pci(dev) && pci_ats_supported(to_pci_dev(dev));
 }
 
+static bool arm_smmu_ats_supported(struct arm_smmu_master *master)
+{
+	return !arm_smmu_master_shares_sid(master) &&
+	       arm_smmu_ats_capable(master);
+}
+
+static bool arm_smmu_master_pasid_enabled(struct arm_smmu_master *master)
+{
+	return dev_is_pci(master->dev) &&
+	       to_pci_dev(master->dev)->pasid_enabled;
+}
+
 static void arm_smmu_enable_ats(struct arm_smmu_master *master)
 {
 	size_t stu;
@@ -3386,8 +3420,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_master_shares_sid(master))
 		return -EOPNOTSUPP;
 
 	if (master->iopf_refcount) {
@@ -3994,6 +4027,9 @@ static int arm_smmu_s1_set_dev_pasid(struct iommu_domain *domain,
 	if (smmu_domain->stage != ARM_SMMU_DOMAIN_S1)
 		return -EINVAL;
 
+	if (arm_smmu_master_shares_sid(master))
+		return -EOPNOTSUPP;
+
 	/*
 	 * We can read cd.asid outside the lock because arm_smmu_set_pasid()
 	 * will fix it
@@ -4192,11 +4228,11 @@ static int arm_smmu_blocking_set_dev_pasid(struct iommu_domain *new_domain,
 	return ret;
 }
 
-static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
-				    struct iommu_domain *old_domain,
-				    struct device *dev,
-				    struct arm_smmu_ste *ste,
-				    unsigned int s1dss)
+static int arm_smmu_attach_dev_ste(struct iommu_domain *domain,
+				   struct iommu_domain *old_domain,
+				   struct device *dev,
+				   struct arm_smmu_ste *ste,
+				   unsigned int s1dss, bool release)
 {
 	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
 	struct arm_smmu_attach_state state = {
@@ -4206,6 +4242,7 @@ static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
 	};
 	bool ats_always_on = master->ats_always_on &&
 			     s1dss != STRTAB_STE_1_S1DSS_TERMINATE;
+	int ret;
 
 	/*
 	 * Do not allow any ASID to be changed while are working on the STE,
@@ -4226,13 +4263,17 @@ static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
 		 * and allows for ATS on the RID to work.
 		 */
 		state.cd_needs_ats = true;
-		arm_smmu_attach_prepare(&state, domain);
+		ret = arm_smmu_attach_prepare(&state, domain);
+		if (ret)
+			goto out_unlock;
 		arm_smmu_make_cdtable_ste(ste, master, &master->cd_table,
 					  state.ats_enabled, s1dss);
 	} else {
-		arm_smmu_attach_prepare(&state, domain);
+		ret = arm_smmu_attach_prepare(&state, domain);
+		if (ret)
+			goto out_unlock;
 	}
-	arm_smmu_install_ste_for_dev(master, ste);
+	arm_smmu_install_ste_for_dev_flags(master, ste, release);
 	arm_smmu_attach_commit(&state);
 	mutex_unlock(&arm_smmu_asid_lock);
 	arm_smmu_attach_release(&state);
@@ -4243,6 +4284,11 @@ static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
 	 * descriptor from arm_smmu_share_asid().
 	 */
 	arm_smmu_clear_cd(master, IOMMU_NO_PASID);
+	return 0;
+
+out_unlock:
+	mutex_unlock(&arm_smmu_asid_lock);
+	return ret;
 }
 
 static int arm_smmu_attach_dev_identity(struct iommu_domain *domain,
@@ -4254,9 +4300,8 @@ static int arm_smmu_attach_dev_identity(struct iommu_domain *domain,
 
 	arm_smmu_master_clear_vmaster(master);
 	arm_smmu_make_bypass_ste(master->smmu, &ste);
-	arm_smmu_attach_dev_ste(domain, old_domain, dev, &ste,
-				STRTAB_STE_1_S1DSS_BYPASS);
-	return 0;
+	return arm_smmu_attach_dev_ste(domain, old_domain, dev, &ste,
+				       STRTAB_STE_1_S1DSS_BYPASS, false);
 }
 
 static const struct iommu_domain_ops arm_smmu_identity_ops = {
@@ -4277,9 +4322,8 @@ static int arm_smmu_attach_dev_blocked(struct iommu_domain *domain,
 
 	arm_smmu_master_clear_vmaster(master);
 	arm_smmu_make_abort_ste(&ste);
-	arm_smmu_attach_dev_ste(domain, old_domain, dev, &ste,
-				STRTAB_STE_1_S1DSS_TERMINATE);
-	return 0;
+	return arm_smmu_attach_dev_ste(domain, old_domain, dev, &ste,
+				       STRTAB_STE_1_S1DSS_TERMINATE, false);
 }
 
 static const struct iommu_domain_ops arm_smmu_blocked_ops = {
@@ -4292,6 +4336,37 @@ static struct iommu_domain arm_smmu_blocked_domain = {
 	.ops = &arm_smmu_blocked_ops,
 };
 
+static int arm_smmu_attach_dev_release(struct iommu_domain *domain,
+				       struct device *dev,
+				       struct iommu_domain *old_domain)
+{
+	struct arm_smmu_ste ste;
+	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+	bool require_direct = dev->iommu && dev->iommu->require_direct;
+	unsigned int s1dss;
+
+	arm_smmu_master_clear_vmaster(master);
+	if (require_direct) {
+		arm_smmu_make_bypass_ste(master->smmu, &ste);
+		s1dss = STRTAB_STE_1_S1DSS_BYPASS;
+	} else {
+		arm_smmu_make_abort_ste(&ste);
+		s1dss = STRTAB_STE_1_S1DSS_TERMINATE;
+	}
+
+	return arm_smmu_attach_dev_ste(domain, old_domain, dev, &ste, s1dss,
+				       true);
+}
+
+static const struct iommu_domain_ops arm_smmu_release_ops = {
+	.attach_dev = arm_smmu_attach_dev_release,
+};
+
+static struct iommu_domain arm_smmu_release_domain = {
+	.type = IOMMU_DOMAIN_BLOCKED,
+	.ops = &arm_smmu_release_ops,
+};
+
 static struct iommu_domain *
 arm_smmu_domain_alloc_paging_flags(struct device *dev, u32 flags,
 				   const struct iommu_user_data *user_data)
@@ -4537,9 +4612,40 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
 				break;
 			}
 
-			if (stream->master->stall_enabled) {
+			if (dev_is_pci(master->dev) !=
+			    dev_is_pci(stream->master->dev)) {
+				dev_warn(master->dev,
+					 "Shared StreamID 0x%x across PCI and non-PCI masters unsupported\n",
+					 sid);
+				ret = -ENODEV;
+				break;
+			}
+
+			if (dev_is_pci(master->dev) &&
+			    pci_ats_required(to_pci_dev(master->dev))) {
+				dev_warn(master->dev,
+					 "Shared StreamID 0x%x rejected: incoming master requires ATS\n",
+					 sid);
+				ret = -EBUSY;
+				break;
+			}
+
+			if (stream->master->stall_enabled ||
+			    master->stall_enabled) {
+				dev_warn(master->dev,
+					 "Shared StreamID 0x%x rejected: stall enabled\n",
+					 sid);
+				ret = -EBUSY;
+				break;
+			}
+
+			if (stream->master->ats_always_on ||
+			    stream->master->ats_enabled ||
+			    arm_smmu_master_pasid_enabled(stream->master) ||
+			    arm_smmu_ssids_in_use(&stream->master->cd_table) ||
+			    stream->master->vmaster) {
 				dev_warn(master->dev,
-					 "Shared StreamID 0x%x rejected: existing master has stall enabled\n",
+					 "Shared StreamID 0x%x rejected: existing master has PASID/ATS/vSMMU enabled\n",
 					 sid);
 				ret = -EBUSY;
 				break;
@@ -4612,7 +4718,7 @@ static int arm_smmu_master_prepare_ats(struct arm_smmu_master *master)
 		return 0;
 	pdev = to_pci_dev(master->dev);
 
-	if (!arm_smmu_ats_supported(master)) {
+	if (!arm_smmu_ats_capable(master)) {
 		if (pci_ats_required(pdev)) {
 			dev_err_once(master->dev, "SMMU doesn't support ATS\n");
 			return -EOPNOTSUPP;
@@ -4624,6 +4730,9 @@ static int arm_smmu_master_prepare_ats(struct arm_smmu_master *master)
 	if (ret || !pci_ats_required(pdev))
 		return ret;
 
+	if (arm_smmu_master_shares_sid(master))
+		return -EOPNOTSUPP;
+
 	/*
 	 * S1DSS is required for ATS to be always on for identity domain cases.
 	 * However, the S1DSS field is ignored if !IDR0_S1P or !IDR1_SSIDSIZE.
@@ -4661,32 +4770,47 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev)
 	master->smmu = smmu;
 	dev_iommu_priv_set(dev, master);
 
-	ret = arm_smmu_insert_master(smmu, master);
-	if (ret)
-		goto err_free_master;
-
-	device_property_read_u32(dev, "pasid-num-bits", &master->ssid_bits);
-	master->ssid_bits = min(smmu->ssid_bits, master->ssid_bits);
-
 	/*
-	 * Note that PASID must be enabled before, and disabled after ATS:
-	 * PCI Express Base 4.0r1.0 - 10.5.1.3 ATS Control Register
-	 *
-	 *   Behavior is undefined if this bit is Set and the value of the PASID
-	 *   Enable, Execute Requested Enable, or Privileged Mode Requested bits
-	 *   are changed.
+	 * Resolve the stall policy before inserting the master. StreamID
+	 * sharing is rejected when either device wants stalling, and the
+	 * insert path needs to know the incoming master's policy.
 	 */
-	arm_smmu_enable_pasid(master);
-
-	if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB))
-		master->ssid_bits = min_t(u8, master->ssid_bits,
-					  CTXDESC_LINEAR_CDMAX);
-
 	if ((smmu->features & ARM_SMMU_FEAT_STALLS &&
 	     device_property_read_bool(dev, "dma-can-stall")) ||
 	    smmu->features & ARM_SMMU_FEAT_STALL_FORCE)
 		master->stall_enabled = true;
 
+	ret = arm_smmu_insert_master(smmu, master);
+	if (ret)
+		goto err_free_master;
+
+	if (arm_smmu_master_shares_sid(master)) {
+		/*
+		 * A shared StreamID cannot use PASID because all sharers must
+		 * point at the same, domain-owned CD table.
+		 */
+		master->ssid_bits = 0;
+	} else {
+		device_property_read_u32(dev, "pasid-num-bits",
+					 &master->ssid_bits);
+		master->ssid_bits = min(smmu->ssid_bits, master->ssid_bits);
+
+		/*
+		 * Note that PASID must be enabled before, and disabled after
+		 * ATS:
+		 * PCI Express Base 4.0r1.0 - 10.5.1.3 ATS Control Register
+		 *
+		 *   Behavior is undefined if this bit is Set and the value of
+		 *   the PASID Enable, Execute Requested Enable, or Privileged
+		 *   Mode Requested bits are changed.
+		 */
+		arm_smmu_enable_pasid(master);
+
+		if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB))
+			master->ssid_bits = min_t(u8, master->ssid_bits,
+						  CTXDESC_LINEAR_CDMAX);
+	}
+
 	ret = arm_smmu_master_prepare_ats(master);
 	if (ret)
 		goto err_disable_pasid;
@@ -4811,7 +4935,7 @@ static int arm_smmu_def_domain_type(struct device *dev)
 static const struct iommu_ops arm_smmu_ops = {
 	.identity_domain	= &arm_smmu_identity_domain,
 	.blocked_domain		= &arm_smmu_blocked_domain,
-	.release_domain		= &arm_smmu_blocked_domain,
+	.release_domain		= &arm_smmu_release_domain,
 	.capable		= arm_smmu_capable,
 	.hw_info		= arm_smmu_hw_info,
 	.domain_alloc_sva       = arm_smmu_sva_domain_alloc,
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 444b88cc67c5..cd659590dfe5 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -1242,6 +1242,11 @@ int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
 int arm_smmu_cmdq_init(struct arm_smmu_device *smmu,
 		       struct arm_smmu_cmdq *cmdq);
 
+static inline bool arm_smmu_master_shares_sid(struct arm_smmu_master *master)
+{
+	return !list_empty(&master->shared_masters_elm);
+}
+
 static inline bool arm_smmu_master_canwbs(struct arm_smmu_master *master)
 {
 	return dev_iommu_fwspec_get(master->dev)->flags &
diff --git a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
index 8166cece943c..ffb0ac93a4f4 100644
--- a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
+++ b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
@@ -1264,7 +1264,7 @@ static int tegra241_vintf_init_vsid(struct iommufd_vdevice *vdev)
 	if (virt_sid > FIELD_MAX(VINTF_SID_MATCH_VIRT_SID))
 		return -EINVAL;
 
-	if (master->num_streams != 1)
+	if (master->num_streams != 1 || arm_smmu_master_shares_sid(master))
 		return -EOPNOTSUPP;
 
 	/* Find an empty pair of SID_REPLACE and SID_MATCH */

-- 
2.34.1


  parent reply	other threads:[~2026-11-13 13:23 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-13 13:27 [PATCH RFC v8 0/9] iommu/arm-smmu-v3: Support shared Stream IDs Peng Fan (OSS)
2026-11-13 13:27 ` [PATCH RFC v8 1/9] iommu/arm-smmu-v3: Allocate streams individually Peng Fan (OSS)
2026-11-13 13:27 ` [PATCH RFC v8 2/9] iommu/arm-smmu-v3: Delay stream allocation to inside the mutex Peng Fan (OSS)
2026-11-13 13:27 ` [PATCH RFC v8 3/9] iommu/arm-smmu-v3: Plumb the CD table into the CD helpers Peng Fan (OSS)
2026-11-13 13:27 ` [PATCH RFC v8 4/9] iommu/arm-smmu-v3: Use a per-domain CD table for non-PASID streams Peng Fan (OSS)
2026-11-13 13:27 ` [PATCH RFC v8 5/9] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master Peng Fan (OSS)
2026-11-13 13:27 ` [PATCH RFC v8 6/9] iommu/arm-smmu-v3: Group aliasing devices into the same IOMMU group Peng Fan (OSS)
2026-11-13 13:27 ` Peng Fan (OSS) [this message]
2026-11-13 13:27 ` [PATCH RFC v8 8/9] iommu/arm-smmu-v3: Serialize shared-SID changes with attach Peng Fan (OSS)
2026-11-13 13:27 ` [PATCH RFC v8 9/9] iommu/arm-smmu-v3: Quiesce PASID before mm release unlinks Peng Fan (OSS)

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261113-b4-smmu-shared-sid-ver8-v8-7-587307a77f3a@nxp.com \
    --to=peng.fan@oss.nxp.com \
    --cc=iommu@lists.linux.dev \
    --cc=jgg@ziepe.ca \
    --cc=jonathanh@nvidia.com \
    --cc=joro@8bytes.org \
    --cc=jpb@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-tegra@vger.kernel.org \
    --cc=nicolinc@nvidia.com \
    --cc=peng.fan@nxp.com \
    --cc=robin.murphy@arm.com \
    --cc=thierry.reding@kernel.org \
    --cc=vdumpa@nvidia.com \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®