mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH RFC v4 0/4] iommu/arm-smmu-v3: Support shared Stream IDs
@ 2026-10-01 12:49 Peng Fan (OSS)
  2026-10-01 12:49 ` [PATCH RFC v4 1/4] iommu/arm-smmu-v3: Skip duplicate SIDs when erasing streams from RB tree Peng Fan (OSS)
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Peng Fan (OSS) @ 2026-10-01 12:49 UTC (permalink / raw)
  To: Will Deacon, Robin Murphy, Joerg Roedel (AMD),
	Nicolin Chen, Jason Gunthorpe, Jean-Philippe Brucker
  Cc: linux-arm-kernel, iommu, linux-kernel, Peng Fan

Some SoCs have a limited number of IOMMU Stream IDs and hardware
that inherently shares them.  The NXP i.MX95, for example, has
only 64 SIDs -- the internal bus carries just 6 bits of requester
ID -- serving 64+ DMA initiators spread across separate IP blocks:
MMC, SD, NET, PCI, DMA, NPU, DSP, DISPLAY and more.  Because the
SID space is exhausted, genuinely distinct platform devices (with
their own DT nodes and drivers) share a SID by hardware design.

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

This series adds support for multiple single-SID masters to share
a Stream ID, allowing such constrained SoCs to function with
IOMMU translation enabled.

Patch 1 fixes a pre-existing bug where rb_erase() is called on
duplicate SID entries that were never inserted into the RB tree,
which can corrupt the tree.  Bridged PCI devices may produce
duplicated Stream IDs; the insertion path already skips them but
the removal and error rollback paths did not.

Patch 2 adds shared-SID tracking so that when a second single-SID
master registers an already-owned SID, it joins the existing
stream rather than being rejected.  The stream structure is shared
directly between masters via a linked list. On removal, ownership
transfers to the next sharer.

Patch 3 places devices that share a SID into the same IOMMU
group, ensuring they share a single IOMMU domain (required
because one STE can only point to one cd_table).

Patch 4 wires up STE write ordering (first master writes, last
master tears down) and gates features that require unambiguous
SID-to-device mapping: SVA, IOPF/stall, and vSMMU nesting are
disabled for shared-SID masters.

Changes since RFC v3:
- Added a standalone bugfix for duplicate-SID rb_erase (new patch 1).
- Stream structures are now shared directly between masters
  instead of duplicating them (Nicolin).  No ref_count field;
  sharing is detected via list_empty(&stream->shared_masters).
- On owner removal, use rb_replace_node() + list_splice_init()
  to transfer the RB tree entry and shared_masters list to the
  next sharer, avoiding use-after-free.
- INIT_LIST_HEAD(&stream->shared_masters) moved after sort() to
  avoid list pointer corruption from bitwise memory swaps.
- Both sides of the sharing pair are checked for num_streams == 1.

Changes since RFC v2:
- Dropped the RB tree to XArray conversion patch (Nicolin).
  The RB tree is retained as-is; a new arm_smmu_find_stream()
  helper provides stream-level lookup.
- No per-master shared_sid bool; replaced by
  list_empty(&master->shared_masters_elm) (Nicolin).
- 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 since RFC v1:
- Revised cover letter to drop eDMA channel example (Jason).

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
Peng Fan (4):
      iommu/arm-smmu-v3: Skip duplicate SIDs when erasing streams from RB tree
      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        | 174 +++++++++++++++++----
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h        |   3 +
 4 files changed, 150 insertions(+), 32 deletions(-)
---
base-commit: 6474fa070f2b8013b4b87350b775b8c3be6e8aac
change-id: 20260930-smmu-shared-sid-ver3-96807c4f4d93

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


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

* [PATCH RFC v4 1/4] iommu/arm-smmu-v3: Skip duplicate SIDs when erasing streams from RB tree
  2026-10-01 12:49 [PATCH RFC v4 0/4] iommu/arm-smmu-v3: Support shared Stream IDs Peng Fan (OSS)
@ 2026-10-01 12:49 ` Peng Fan (OSS)
  2026-10-01 16:07   ` Nicolin Chen
  2026-10-01 12:49 ` [PATCH RFC v4 2/4] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master Peng Fan (OSS)
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 7+ messages in thread
From: Peng Fan (OSS) @ 2026-10-01 12:49 UTC (permalink / raw)
  To: Will Deacon, Robin Murphy, Joerg Roedel (AMD),
	Nicolin Chen, Jason Gunthorpe, Jean-Philippe Brucker
  Cc: linux-arm-kernel, iommu, linux-kernel, Peng Fan

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

Bridged PCI devices may end up with duplicated Stream IDs in
master->streams[].  During insertion, rb_find_add() silently
drops the duplicate node when it collides with an already-inserted
entry from the same master, leaving the duplicate's rb_node in an
uninitialized state.

However, both arm_smmu_remove_master() and the error rollback path
in arm_smmu_insert_master() unconditionally call rb_erase() on
every element of the streams array, including duplicates that were
never inserted.  Calling rb_erase() on an uninitialized rb_node
corrupts the RB tree.

The streams array is sorted by SID before insertion, so duplicates
are always adjacent.  Skip them during erasure to match the
insertion behavior.

Fixes: b00d24997a11c ("iommu/arm-smmu-v3: Fix iommu_device_probe bug due to duplicated stream ids")
Assisted-by: LLM
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 10 ++++++++--
 1 file changed, 8 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 5732f3ba0122d..a79f2250a4886 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -4153,8 +4153,11 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
 	}
 
 	if (ret) {
-		for (i--; i >= 0; i--)
+		for (i--; i >= 0; i--) {
+			if (i > 0 && master->streams[i - 1].id == master->streams[i].id)
+				continue;
 			rb_erase(&master->streams[i].node, &smmu->streams);
+		}
 		kfree(master->streams);
 		kfree(master->build_invs);
 	}
@@ -4173,8 +4176,11 @@ static void arm_smmu_remove_master(struct arm_smmu_master *master)
 		return;
 
 	mutex_lock(&smmu->streams_mutex);
-	for (i = 0; i < fwspec->num_ids; i++)
+	for (i = 0; i < fwspec->num_ids; i++) {
+		if (i > 0 && master->streams[i - 1].id == master->streams[i].id)
+			continue;
 		rb_erase(&master->streams[i].node, &smmu->streams);
+	}
 	mutex_unlock(&smmu->streams_mutex);
 
 	kfree(master->streams);

-- 
2.34.1


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

* [PATCH RFC v4 2/4] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master
  2026-10-01 12:49 [PATCH RFC v4 0/4] iommu/arm-smmu-v3: Support shared Stream IDs Peng Fan (OSS)
  2026-10-01 12:49 ` [PATCH RFC v4 1/4] iommu/arm-smmu-v3: Skip duplicate SIDs when erasing streams from RB tree Peng Fan (OSS)
@ 2026-10-01 12:49 ` Peng Fan (OSS)
  2026-10-01 17:17   ` Nicolin Chen
  2026-10-01 12:50 ` [PATCH RFC v4 3/4] iommu/arm-smmu-v3: Group aliasing devices into the same IOMMU group Peng Fan (OSS)
  2026-10-01 12:50 ` [PATCH RFC v4 4/4] iommu/arm-smmu-v3: Wire up shared-SID STE ordering and feature gating Peng Fan (OSS)
  3 siblings, 1 reply; 7+ messages in thread
From: Peng Fan (OSS) @ 2026-10-01 12:49 UTC (permalink / raw)
  To: Will Deacon, Robin Murphy, Joerg Roedel (AMD),
	Nicolin Chen, Jason Gunthorpe, Jean-Philippe Brucker
  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.

Suggested-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/linux-iommu/arG6hmng3NddGEHm@nvidia.com/
Assisted-by: LLM
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 94 +++++++++++++++++++++++------
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |  3 +
 2 files changed, 78 insertions(+), 19 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 a79f2250a4886..9d98d1b2758f7 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2037,15 +2037,14 @@ static int arm_smmu_streams_cmp_key(const void *lhs, const struct rb_node *rhs)
 	return 0;
 }
 
-static int arm_smmu_streams_cmp_node(struct rb_node *lhs,
-				     const struct rb_node *rhs)
+static int arm_smmu_streams_cmp_node(struct rb_node *lhs, const struct rb_node *rhs)
 {
 	return arm_smmu_streams_cmp_key(
 		&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 +2053,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 */
@@ -4116,6 +4128,7 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
 		new_stream->id = fwspec->ids[i];
 		new_stream->master = master;
 	}
+	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,
@@ -4128,6 +4141,8 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
 		struct rb_node *existing;
 		u32 sid = new_stream->id;
 
+		INIT_LIST_HEAD(&new_stream->shared_masters);
+
 		ret = arm_smmu_init_sid_strtab(smmu, sid);
 		if (ret)
 			break;
@@ -4136,23 +4151,33 @@ 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 ||
+			    existing_stream->master->num_streams != 1) {
+				dev_warn(master->dev,
+					 "Shared StreamID 0x%x not supported for multi-SID masters\n",
+					 sid);
+				ret = -ENODEV;
+				break;
+			}
+
+			if (list_empty(&existing_stream->shared_masters))
+				list_add_tail(&existing_stream->master->shared_masters_elm,
+					      &existing_stream->shared_masters);
+			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--) {
 			if (i > 0 && master->streams[i - 1].id == master->streams[i].id)
 				continue;
@@ -4170,16 +4195,47 @@ 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++) {
-		if (i > 0 && master->streams[i - 1].id == master->streams[i].id)
-			continue;
-		rb_erase(&master->streams[i].node, &smmu->streams);
+	if (!list_empty(&master->shared_masters_elm)) {
+		struct arm_smmu_stream *stream;
+
+		list_del_init(&master->shared_masters_elm);
+		stream = arm_smmu_find_stream(smmu, master->streams[0].id);
+		if (stream && stream->master == master) {
+			struct arm_smmu_master *next;
+			struct arm_smmu_stream *next_stream;
+
+			next = list_first_entry(&stream->shared_masters,
+					struct arm_smmu_master,
+					shared_masters_elm);
+			next_stream = &next->streams[0];
+			next_stream->ste_installed = stream->ste_installed;
+			INIT_LIST_HEAD(&next_stream->shared_masters);
+			list_splice_init(&stream->shared_masters,
+					 &next_stream->shared_masters);
+			rb_replace_node(&stream->node, &next_stream->node,
+					&smmu->streams);
+			stream = next_stream;
+		}
+
+		if (stream && list_is_singular(&stream->shared_masters)) {
+			struct arm_smmu_master *last;
+
+			last = list_first_entry(&stream->shared_masters,
+						struct arm_smmu_master,
+						shared_masters_elm);
+			list_del_init(&last->shared_masters_elm);
+		}
+	} else {
+		for (i = 0; i < master->num_streams; i++) {
+			if (i > 0 && master->streams[i - 1].id == master->streams[i].id)
+				continue;
+			rb_erase(&master->streams[i].node, &smmu->streams);
+		}
 	}
 	mutex_unlock(&smmu->streams_mutex);
 
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..89177f3e7dabe 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,8 @@ struct arm_smmu_stream {
 	u32				id;
 	struct arm_smmu_master		*master;
 	struct rb_node			node;
+	bool				ste_installed;
+	struct list_head		shared_masters;
 };
 
 struct arm_smmu_vmaster {
@@ -1017,6 +1019,7 @@ struct arm_smmu_master {
 	bool				ste_ats_enabled : 1;
 	bool				stall_enabled;
 	bool				ats_always_on;
+	struct list_head		shared_masters_elm;
 	unsigned int			ssid_bits;
 	unsigned int			iopf_refcount;
 };

-- 
2.34.1


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

* [PATCH RFC v4 3/4] iommu/arm-smmu-v3: Group aliasing devices into the same IOMMU group
  2026-10-01 12:49 [PATCH RFC v4 0/4] iommu/arm-smmu-v3: Support shared Stream IDs Peng Fan (OSS)
  2026-10-01 12:49 ` [PATCH RFC v4 1/4] iommu/arm-smmu-v3: Skip duplicate SIDs when erasing streams from RB tree Peng Fan (OSS)
  2026-10-01 12:49 ` [PATCH RFC v4 2/4] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master Peng Fan (OSS)
@ 2026-10-01 12:50 ` Peng Fan (OSS)
  2026-10-01 12:50 ` [PATCH RFC v4 4/4] iommu/arm-smmu-v3: Wire up shared-SID STE ordering and feature gating Peng Fan (OSS)
  3 siblings, 0 replies; 7+ messages in thread
From: Peng Fan (OSS) @ 2026-10-01 12:50 UTC (permalink / raw)
  To: Will Deacon, Robin Murphy, Joerg Roedel (AMD),
	Nicolin Chen, Jason Gunthorpe, Jean-Philippe Brucker
  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.

Assisted-by: LLM
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 9d98d1b2758f7..0f7e90af5823d 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -4379,19 +4379,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] 7+ messages in thread

* [PATCH RFC v4 4/4] iommu/arm-smmu-v3: Wire up shared-SID STE ordering and feature gating
  2026-10-01 12:49 [PATCH RFC v4 0/4] iommu/arm-smmu-v3: Support shared Stream IDs Peng Fan (OSS)
                   ` (2 preceding siblings ...)
  2026-10-01 12:50 ` [PATCH RFC v4 3/4] iommu/arm-smmu-v3: Group aliasing devices into the same IOMMU group Peng Fan (OSS)
@ 2026-10-01 12:50 ` Peng Fan (OSS)
  3 siblings, 0 replies; 7+ messages in thread
From: Peng Fan (OSS) @ 2026-10-01 12:50 UTC (permalink / raw)
  To: Will Deacon, Robin Murphy, Joerg Roedel (AMD),
	Nicolin Chen, Jason Gunthorpe, Jean-Philippe Brucker
  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.

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        | 43 ++++++++++++++++++++--
 3 files changed, 44 insertions(+), 4 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 0f7e90af5823d..ae33660ca79e7 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2976,11 +2976,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])) ==
@@ -2989,10 +3014,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++)
@@ -3001,6 +3028,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,7 +3186,7 @@ static int arm_smmu_enable_iopf(struct arm_smmu_master *master,
 		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] 7+ messages in thread

* Re: [PATCH RFC v4 1/4] iommu/arm-smmu-v3: Skip duplicate SIDs when erasing streams from RB tree
  2026-10-01 12:49 ` [PATCH RFC v4 1/4] iommu/arm-smmu-v3: Skip duplicate SIDs when erasing streams from RB tree Peng Fan (OSS)
@ 2026-10-01 16:07   ` Nicolin Chen
  0 siblings, 0 replies; 7+ messages in thread
From: Nicolin Chen @ 2026-10-01 16:07 UTC (permalink / raw)
  To: Peng Fan (OSS)
  Cc: Will Deacon, Robin Murphy, Joerg Roedel (AMD),
	Jason Gunthorpe, Jean-Philippe Brucker, linux-arm-kernel, iommu,
	linux-kernel, Peng Fan

On Thu, Oct 01, 2026 at 08:49:58PM +0800, Peng Fan (OSS) wrote:
> From: Peng Fan <peng.fan@nxp.com>
> 
> Bridged PCI devices may end up with duplicated Stream IDs in
> master->streams[].  During insertion, rb_find_add() silently
> drops the duplicate node when it collides with an already-inserted
> entry from the same master, leaving the duplicate's rb_node in an
> uninitialized state.

There is already a cleaner fix:
https://lore.kernel.org/linux-iommu/76c5f9dde30269995ef842a12a3a5e1ebaa3e6df.1790188510.git.nicolinc@nvidia.com/

Hopefully Will can take it soon.

Nicolin

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

* Re: [PATCH RFC v4 2/4] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master
  2026-10-01 12:49 ` [PATCH RFC v4 2/4] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master Peng Fan (OSS)
@ 2026-10-01 17:17   ` Nicolin Chen
  0 siblings, 0 replies; 7+ messages in thread
From: Nicolin Chen @ 2026-10-01 17:17 UTC (permalink / raw)
  To: Peng Fan (OSS)
  Cc: Will Deacon, Robin Murphy, Joerg Roedel (AMD),
	Jason Gunthorpe, Jean-Philippe Brucker, linux-arm-kernel, iommu,
	linux-kernel, Peng Fan

On Thu, Oct 01, 2026 at 08:49:59PM +0800, Peng Fan (OSS) wrote:
> -static int arm_smmu_streams_cmp_node(struct rb_node *lhs,
> -				     const struct rb_node *rhs)
> +static int arm_smmu_streams_cmp_node(struct rb_node *lhs, const struct rb_node *rhs)

Why is this changed?

> @@ -4136,23 +4151,33 @@ 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 ||
> +			    existing_stream->master->num_streams != 1) {
> +				dev_warn(master->dev,
> +					 "Shared StreamID 0x%x not supported for multi-SID masters\n",
> +					 sid);
> +				ret = -ENODEV;
> +				break;
> +			}
> +
> +			if (list_empty(&existing_stream->shared_masters))
> +				list_add_tail(&existing_stream->master->shared_masters_elm,
> +					      &existing_stream->shared_masters);
> +			list_add_tail(&master->shared_masters_elm,
> +				      &existing_stream->shared_masters);

Masters still hold duplicated streams for the shared SID. And their
ste_installed flags are out of sync..

    master_a->stream[0]->a's stream0 {ste_installed=true}
    master_b->stream[0]->b's stream0 {ste_installed=false}

What I have been suggesting is to have a shared stream:
    master_a->stream[0]-->shared stream0 {ste_installed=true}
    master_b->stream[0]-|

So, the driver would need a rework first changing master:
-       struct arm_smmu_stream          *streams;
+       struct arm_smmu_stream          **streams;

Also, arm_smmu_insert_master() needs to reverse its allocation:
	for (i = 0; i < fwspec->num_ids; i++) {
		stream = arm_smmu_find_stream(smmu, sid);
		if (stream) {
			// link to the existing stream
		} else {
			// allocate new stream
		}
		master->streams[i] = stream;
	}
	if (!ret)
		sort();

Nicolin

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

end of thread, other threads:[~2026-10-01 17:18 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-01 12:49 [PATCH RFC v4 0/4] iommu/arm-smmu-v3: Support shared Stream IDs Peng Fan (OSS)
2026-10-01 12:49 ` [PATCH RFC v4 1/4] iommu/arm-smmu-v3: Skip duplicate SIDs when erasing streams from RB tree Peng Fan (OSS)
2026-10-01 16:07   ` Nicolin Chen
2026-10-01 12:49 ` [PATCH RFC v4 2/4] iommu/arm-smmu-v3: Support shared SIDs in insert/remove_master Peng Fan (OSS)
2026-10-01 17:17   ` Nicolin Chen
2026-10-01 12:50 ` [PATCH RFC v4 3/4] iommu/arm-smmu-v3: Group aliasing devices into the same IOMMU group Peng Fan (OSS)
2026-10-01 12:50 ` [PATCH RFC v4 4/4] 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®