mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH RFC v3 0/3] iommu/arm-smmu-v3: Support shared Stream IDs
@ 2026-09-30 11:25 Peng Fan (OSS)
  2026-09-30 11:25 ` [PATCH RFC v3 1/3] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master Peng Fan (OSS)
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Peng Fan (OSS) @ 2026-09-30 11:25 UTC (permalink / raw)
  To: Will Deacon, Robin Murphy, Joerg Roedel (AMD),
	Jean-Philippe Brucker, Nicolin Chen, Jason Gunthorpe
  Cc: linux-arm-kernel, iommu, linux-kernel, Peng Fan

Some SoCs have a limited number of IOMMU Stream IDs (SIDs) and
hardware that inherently shares them. For example, the NXP i.MX95
only has 64 SIDs, only 6 bits for SID in the system chip bus.
However there are many device IPs, such as SDHC, PCIE, NETC, GPU,
NPU, Display, VPU, ISP, ISI, JPEG, SATA, DMA and etc. Each Kind IP has
mutilple instances inside the SoC. So I introudce this patchset to
support shared SID.

The current ARM SMMUv3 driver rejects this with:
  "Aliasing StreamID unsupported, expect DMA to be broken"

I took comments in [1] and implement this patchset and only want to
support a very simple case that multiple platform devices share one SID.

[1] https://lore.kernel.org/linux-iommu/DU0PR04MB94172CB3F138AD39E9B6DEEF887F9@DU0PR04MB9417.eurprd04.prod.outlook.com/

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
Changes in v3:
- Drop the conversion to use xarray
- Only SID-sharing for single SID device
- Follow Nicolin's suggestion https://lore.kernel.org/linux-iommu/arG6hmng3NddGEHm@nvidia.com/
- Link to v2: https://lore.kernel.org/linux-iommu/20260921-smmu-shared-sid-v2-0-4b656ce68178@nxp.com/
Changes in v2:
- Only update cover-letter to make it clear about why introducing shared
  SID
- Link to v1: https://patch.msgid.link/20260916-smmu-shared-sid-v1-0-517384504aee@nxp.com

---
Peng Fan (3):
      iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master
      iommu/arm-smmu-v3: Group aliasing devices into the same IOMMU group
      iommu/arm-smmu-v3: Wire up shared-SID STE ordering and feature gating

 .../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        | 153 +++++++++++++++++----
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h        |   9 ++
 4 files changed, 137 insertions(+), 30 deletions(-)
---
base-commit: 86fb14622b7ccaac8916e5541bebc4d70f64331b
change-id: 20260916-smmu-shared-sid-e488f6c4d4f0

Best regards,
--  
Peng Fan <peng.fan@nxp.com>


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH RFC v3 1/3] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master
  2026-09-30 11:25 [PATCH RFC v3 0/3] iommu/arm-smmu-v3: Support shared Stream IDs Peng Fan (OSS)
@ 2026-09-30 11:25 ` Peng Fan (OSS)
  2026-09-30 18:11   ` Nicolin Chen
  2026-09-30 11:25 ` [PATCH RFC v3 2/3] iommu/arm-smmu-v3: Group aliasing devices into the same IOMMU group Peng Fan (OSS)
  2026-09-30 11:25 ` [PATCH RFC v3 3/3] iommu/arm-smmu-v3: Wire up shared-SID STE ordering and feature gating Peng Fan (OSS)
  2 siblings, 1 reply; 5+ messages in thread
From: Peng Fan (OSS) @ 2026-09-30 11:25 UTC (permalink / raw)
  To: Will Deacon, Robin Murphy, Joerg Roedel (AMD),
	Jean-Philippe Brucker, Nicolin Chen, Jason Gunthorpe
  Cc: linux-arm-kernel, iommu, linux-kernel, Peng Fan

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

Some SoCs have a limited number of IOMMU Stream IDs and hardware
that inherently shares them.  For example, the NXP i.MX95 has only
64 SIDs (6-bit internal bus) serving 64+ DMA initiators across
separate IP blocks - MMC, SD, NET, PCI, DMA, NPU, DSP, DISPLAY
and more.  Genuinely distinct platform devices share a SID by
hardware design because the SID space is exhausted.

When arm_smmu_insert_master() encounters a SID already owned by a
different single-SID master, instead of failing with -ENODEV, link
the new master into the existing stream's shared_masters list.
The stream structure is shared directly: the RB tree entry remains
the same, and all sharing masters are tracked via list_head.

On removal, if the departing master owns the canonical stream,
transfer ownership to the next sharer.

Sharing is restricted to single-SID masters (num_streams == 1)
since multi-SID sharing would require more complex tracking.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 76 +++++++++++++++++++++++------
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |  9 ++++
 2 files changed, 70 insertions(+), 15 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 5732f3ba0122d..940f34465c0fa 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2044,8 +2044,8 @@ static int arm_smmu_streams_cmp_node(struct rb_node *lhs,
 		&rb_entry(lhs, struct arm_smmu_stream, node)->id, rhs);
 }
 
-static struct arm_smmu_master *
-arm_smmu_find_master(struct arm_smmu_device *smmu, u32 sid)
+static struct arm_smmu_stream *
+arm_smmu_find_stream(struct arm_smmu_device *smmu, u32 sid)
 {
 	struct rb_node *node;
 
@@ -2054,7 +2054,20 @@ arm_smmu_find_master(struct arm_smmu_device *smmu, u32 sid)
 	node = rb_find(&sid, &smmu->streams, arm_smmu_streams_cmp_key);
 	if (!node)
 		return NULL;
-	return rb_entry(node, struct arm_smmu_stream, node)->master;
+	return rb_entry(node, struct arm_smmu_stream, node);
+}
+
+static struct arm_smmu_master *
+arm_smmu_find_master(struct arm_smmu_device *smmu, u32 sid)
+{
+	struct arm_smmu_stream *stream;
+
+	stream = arm_smmu_find_stream(smmu, sid);
+	if (!stream)
+		return NULL;
+	if (!list_empty(&stream->shared_masters))
+		return NULL;
+	return stream->master;
 }
 
 /* IRQ and event handlers */
@@ -4115,7 +4128,10 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
 
 		new_stream->id = fwspec->ids[i];
 		new_stream->master = master;
+		new_stream->ste_installed = false;
+		INIT_LIST_HEAD(&new_stream->shared_masters);
 	}
+	INIT_LIST_HEAD(&master->shared_masters_elm);
 
 	/* Put the ids into order for sorted to_merge/to_unref arrays */
 	sort(master->streams, master->num_streams,
@@ -4136,23 +4152,29 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
 		existing = rb_find_add(&new_stream->node, &smmu->streams,
 				       arm_smmu_streams_cmp_node);
 		if (existing) {
-			struct arm_smmu_master *existing_master =
-				rb_entry(existing, struct arm_smmu_stream, node)
-					->master;
+			struct arm_smmu_stream *existing_stream =
+				rb_entry(existing, struct arm_smmu_stream, node);
 
 			/* Bridged PCI devices may end up with duplicated IDs */
-			if (existing_master == master)
+			if (existing_stream->master == master)
 				continue;
 
-			dev_warn(master->dev,
-				 "Aliasing StreamID 0x%x (from %s) unsupported, expect DMA to be broken\n",
-				 sid, dev_name(existing_master->dev));
-			ret = -ENODEV;
-			break;
+			if (master->num_streams != 1) {
+				dev_warn(master->dev,
+					 "Shared StreamID 0x%x not supported for multi-SID masters\n",
+					 sid);
+				ret = -ENODEV;
+				break;
+			}
+
+			list_add_tail(&master->shared_masters_elm,
+				      &existing_stream->shared_masters);
+			continue;
 		}
 	}
 
 	if (ret) {
+		list_del_init(&master->shared_masters_elm);
 		for (i--; i >= 0; i--)
 			rb_erase(&master->streams[i].node, &smmu->streams);
 		kfree(master->streams);
@@ -4167,14 +4189,38 @@ static void arm_smmu_remove_master(struct arm_smmu_master *master)
 {
 	int i;
 	struct arm_smmu_device *smmu = master->smmu;
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev);
 
 	if (!smmu || !master->streams)
 		return;
 
 	mutex_lock(&smmu->streams_mutex);
-	for (i = 0; i < fwspec->num_ids; i++)
-		rb_erase(&master->streams[i].node, &smmu->streams);
+	list_del_init(&master->shared_masters_elm);
+	for (i = 0; i < master->num_streams; i++) {
+		u32 sid = master->streams[i].id;
+		struct arm_smmu_stream *stream;
+
+		if (i > 0 && master->streams[i - 1].id == sid)
+			continue;
+
+		stream = arm_smmu_find_stream(smmu, sid);
+		if (!stream)
+			continue;
+
+		if (stream->master != master)
+			continue;
+
+		if (list_empty(&stream->shared_masters)) {
+			rb_erase(&stream->node, &smmu->streams);
+		} else {
+			struct arm_smmu_master *next;
+
+			next = list_first_entry(&stream->shared_masters,
+					struct arm_smmu_master,
+					shared_masters_elm);
+			list_del_init(&next->shared_masters_elm);
+			stream->master = next;
+		}
+	}
 	mutex_unlock(&smmu->streams_mutex);
 
 	kfree(master->streams);
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 dd2fee2f560e6..ea0aa345fb673 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -969,6 +969,10 @@ struct arm_smmu_stream {
 	u32				id;
 	struct arm_smmu_master		*master;
 	struct rb_node			node;
+	/* Set when the STE has been programmed for this stream. */
+	bool				ste_installed;
+	/* List of masters sharing this SID.  Empty unless shared. */
+	struct list_head		shared_masters;
 };
 
 struct arm_smmu_vmaster {
@@ -1017,6 +1021,11 @@ struct arm_smmu_master {
 	bool				ste_ats_enabled : 1;
 	bool				stall_enabled;
 	bool				ats_always_on;
+	/*
+	 * Links into arm_smmu_stream.shared_masters. Supports num_streams == 1
+	 * only. Empty if SID is not shared.
+	 */
+	struct list_head		shared_masters_elm;
 	unsigned int			ssid_bits;
 	unsigned int			iopf_refcount;
 };

-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH RFC v3 2/3] iommu/arm-smmu-v3: Group aliasing devices into the same IOMMU group
  2026-09-30 11:25 [PATCH RFC v3 0/3] iommu/arm-smmu-v3: Support shared Stream IDs Peng Fan (OSS)
  2026-09-30 11:25 ` [PATCH RFC v3 1/3] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master Peng Fan (OSS)
@ 2026-09-30 11:25 ` Peng Fan (OSS)
  2026-09-30 11:25 ` [PATCH RFC v3 3/3] iommu/arm-smmu-v3: Wire up shared-SID STE ordering and feature gating Peng Fan (OSS)
  2 siblings, 0 replies; 5+ messages in thread
From: Peng Fan (OSS) @ 2026-09-30 11:25 UTC (permalink / raw)
  To: Will Deacon, Robin Murphy, Joerg Roedel (AMD),
	Jean-Philippe Brucker, Nicolin Chen, Jason Gunthorpe
  Cc: linux-arm-kernel, iommu, linux-kernel, Peng Fan

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

Devices sharing a SID must share a single IOMMU domain (and
therefore a single cd_table) because the STE can only point to one
cd_table at a time.

Detect SID aliasing at group-assignment time by looking up the RB
tree for existing owners.  arm_smmu_device_group() is called before
arm_smmu_insert_master(), so any RB tree hit belongs to a
different, already-probed master.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 33 ++++++++++++++++++++---------
 1 file changed, 23 insertions(+), 10 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 940f34465c0fa..2e13cf0cb8882 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -4363,19 +4363,32 @@ static int arm_smmu_set_dirty_tracking(struct iommu_domain *domain,
 
 static struct iommu_group *arm_smmu_device_group(struct device *dev)
 {
-	struct iommu_group *group;
+	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+	struct arm_smmu_device *smmu = master->smmu;
+	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+	struct iommu_group *group = NULL;
+	int i;
+
+	mutex_lock(&smmu->streams_mutex);
+	for (i = 0; i < fwspec->num_ids; i++) {
+		struct arm_smmu_stream *stream;
+
+		stream = arm_smmu_find_stream(smmu, fwspec->ids[i]);
+		if (!stream)
+			continue;
+
+		group = iommu_group_get(stream->master->dev);
+		break;
+	}
+	mutex_unlock(&smmu->streams_mutex);
+
+	if (group)
+		return group;
 
-	/*
-	 * We don't support devices sharing stream IDs other than PCI RID
-	 * aliases, since the necessary ID-to-device lookup becomes rather
-	 * impractical given a potential sparse 32-bit stream ID space.
-	 */
 	if (dev_is_pci(dev))
-		group = pci_device_group(dev);
-	else
-		group = generic_device_group(dev);
+		return pci_device_group(dev);
 
-	return group;
+	return generic_device_group(dev);
 }
 
 static int arm_smmu_of_xlate(struct device *dev,

-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH RFC v3 3/3] iommu/arm-smmu-v3: Wire up shared-SID STE ordering and feature gating
  2026-09-30 11:25 [PATCH RFC v3 0/3] iommu/arm-smmu-v3: Support shared Stream IDs Peng Fan (OSS)
  2026-09-30 11:25 ` [PATCH RFC v3 1/3] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master Peng Fan (OSS)
  2026-09-30 11:25 ` [PATCH RFC v3 2/3] iommu/arm-smmu-v3: Group aliasing devices into the same IOMMU group Peng Fan (OSS)
@ 2026-09-30 11:25 ` Peng Fan (OSS)
  2 siblings, 0 replies; 5+ messages in thread
From: Peng Fan (OSS) @ 2026-09-30 11:25 UTC (permalink / raw)
  To: Will Deacon, Robin Murphy, Joerg Roedel (AMD),
	Jean-Philippe Brucker, Nicolin Chen, Jason Gunthorpe
  Cc: linux-arm-kernel, iommu, linux-kernel, Peng Fan

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.

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        | 44 ++++++++++++++++++++--
 3 files changed, 44 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 ab1078a97d801..73fb5fad1c181 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 2e13cf0cb8882..b192efa679561 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) {

-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH RFC v3 1/3] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master
  2026-09-30 11:25 ` [PATCH RFC v3 1/3] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master Peng Fan (OSS)
@ 2026-09-30 18:11   ` Nicolin Chen
  0 siblings, 0 replies; 5+ messages in thread
From: Nicolin Chen @ 2026-09-30 18:11 UTC (permalink / raw)
  To: Peng Fan (OSS)
  Cc: Will Deacon, Robin Murphy, Joerg Roedel (AMD),
	Jean-Philippe Brucker, Jason Gunthorpe, linux-arm-kernel, iommu,
	linux-kernel, Peng Fan

On Wed, Sep 30, 2026 at 07:25:01PM +0800, Peng Fan (OSS) wrote:
> @@ -4115,7 +4128,10 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
>  
>  		new_stream->id = fwspec->ids[i];
>  		new_stream->master = master;
> +		new_stream->ste_installed = false;

ste_installed is zero-ed.

> @@ -4136,23 +4152,29 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
>  		existing = rb_find_add(&new_stream->node, &smmu->streams,
>  				       arm_smmu_streams_cmp_node);
>  		if (existing) {
> -			struct arm_smmu_master *existing_master =
> -				rb_entry(existing, struct arm_smmu_stream, node)
> -					->master;
> +			struct arm_smmu_stream *existing_stream =
> +				rb_entry(existing, struct arm_smmu_stream, node);
>  
>  			/* Bridged PCI devices may end up with duplicated IDs */
> -			if (existing_master == master)
> +			if (existing_stream->master == master)
>  				continue;
>  
> -			dev_warn(master->dev,
> -				 "Aliasing StreamID 0x%x (from %s) unsupported, expect DMA to be broken\n",
> -				 sid, dev_name(existing_master->dev));
> -			ret = -ENODEV;
> -			break;
> +			if (master->num_streams != 1) {
> +				dev_warn(master->dev,
> +					 "Shared StreamID 0x%x not supported for multi-SID masters\n",
> +					 sid);
> +				ret = -ENODEV;
> +				break;
> +			}
> +
> +			list_add_tail(&master->shared_masters_elm,
> +				      &existing_stream->shared_masters);

This version still uses the duplicated stream structures, not the
shared stream structure that I pointed out here:
https://lore.kernel.org/linux-iommu/arG6hmng3NddGEHm@nvidia.com/
?

This will have problems.

  master_b->stream[0] --> new shared stream (SID=X) {shared_masters}
           |
           ---------------- shared_masters_elm ----------|
                                                         v
  master_a->stream[0] --> existing stream (SID=X) {shared_masters}
           |
           ---------------- shared_masters_elm -----> {NULL}

First, the owner (master_a) doesn't add its shared_masters_elm to
any shared_masters list. This would fail the test on the owner:
    !list_empty(&master->shared_masters_elm)

Then, ...

> @@ -4167,14 +4189,38 @@ static void arm_smmu_remove_master(struct arm_smmu_master *master)
>  {
>  	int i;
>  	struct arm_smmu_device *smmu = master->smmu;
> -	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev);
>  
>  	if (!smmu || !master->streams)
>  		return;
>  
>  	mutex_lock(&smmu->streams_mutex);
> -	for (i = 0; i < fwspec->num_ids; i++)
> -		rb_erase(&master->streams[i].node, &smmu->streams);
> +	list_del_init(&master->shared_masters_elm);
> +	for (i = 0; i < master->num_streams; i++) {
> +		u32 sid = master->streams[i].id;
> +		struct arm_smmu_stream *stream;
> +
> +		if (i > 0 && master->streams[i - 1].id == sid)
> +			continue;
> +
> +		stream = arm_smmu_find_stream(smmu, sid);
> +		if (!stream)
> +			continue;
> +
> +		if (stream->master != master)
> +			continue;
> +
> +		if (list_empty(&stream->shared_masters)) {
> +			rb_erase(&stream->node, &smmu->streams);
> +		} else {
> +			struct arm_smmu_master *next;
> +
> +			next = list_first_entry(&stream->shared_masters,
> +					struct arm_smmu_master,
> +					shared_masters_elm);
> +			list_del_init(&next->shared_masters_elm);
> +			stream->master = next;
> +		}
> +	}
>  	mutex_unlock(&smmu->streams_mutex);
>  
>  	kfree(master->streams);

.. when the owner stream gets freed with the master_a, the shared
stream would UAF:

  master_b->stream[0] --> new shared stream (SID=X) {shared_masters}
           |
           ---------------- shared_masters_elm ----------|
                                                         v
                                                      {freed}

This should be changed to the model that I suggested in v2:

      |---------------------------------------------------------|
      |                                                         |
      |     |----------- shared_masters_elm --------|           |
      v     |                                       |           |
  master_a->stream[0] --|                           v           |
                        |--> stream (SID=X) {shared_masters; master;}
  master_b->stream[0] --|                           ^
            |                                       |
            |----------- shared_masters_elm --------|

When any master is removed:
 * Delink its shared_masters_elm
 * Free the shared stream when its shared_masters is empty

If shared_masters isn't empty but the owner is removed:
 * Give the ownership (stream->master pointer) to the next master

Nicolin

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-30 18:12 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-30 11:25 [PATCH RFC v3 0/3] iommu/arm-smmu-v3: Support shared Stream IDs Peng Fan (OSS)
2026-09-30 11:25 ` [PATCH RFC v3 1/3] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master Peng Fan (OSS)
2026-09-30 18:11   ` Nicolin Chen
2026-09-30 11:25 ` [PATCH RFC v3 2/3] iommu/arm-smmu-v3: Group aliasing devices into the same IOMMU group Peng Fan (OSS)
2026-09-30 11:25 ` [PATCH RFC v3 3/3] iommu/arm-smmu-v3: Wire up shared-SID STE ordering and feature gating Peng Fan (OSS)

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®