Skip to contentSkip to conversation

iommu/arm-smmu-v3: Fix device crash on kdump kernel

PATCH Superseded Nicolin Chen 4 participants 18 messages Apr 15 – Apr 25

Subsystems (3)

v2 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

v2 cover iommu/arm-smmu-v3: Fix device crash on kdump kernel

When transitioning to a kdump kernel, the primary kernel might have crashed while endpoint devices were actively bus-mastering DMA. Currently, the SMMU driver aggressively resets the hardware during probe by clearing CR0_SMMUEN and setting the Global Bypass Attribute (GBPA) to ABORT.

In a kdump scenario, this aggressive reset is highly destructive:
a) If GBPA is set to ABORT, in-flight DMA will be aborted, generating fatal PCIe AER or SErrors that may panic the kdump kernel
b) If GBPA is set to BYPASS, in-flight DMA targeting some IOVAs will bypass the SMMU and corrupt the physical memory at those 1:1 mapped IOVAs.

To safely absorb in-flight DMA, the kdump kernel must leave SMMUEN=1 intact and avoid modifying STRTAB_BASE. This allows HW to continue translating in- flight DMA using the crashed kernel's page tables until the endpoint device drivers probe and quiesce their respective hardware.

However, the ARM SMMUv3 architecture specification states that updating the SMMU_STRTAB_BASE register while SMMUEN == 1 is UNPREDICTABLE or ignored.

This leaves a kdump kernel no choice but to adopt the stream table from the crashed kernel.

In this series:
- Introduce an ARM_SMMU_OPT_KDUMP
- Skip SMMUEN and STRTAB_BASE resets in arm_smmu_device_reset()
- Map the crashed kernel's stream tables into the kdump kernel [*]
- Defer any default domain attachment to retain STEs until device drivers explicitly request it.

[*] This is implemented via memremap, which only works on a coherent SMMU.

Note that the entire series requires Jason's work that was merged in v6.12:
85196f54743d ("iommu/arm-smmu-v3: Reorganize struct arm_smmu_strtab_cfg"). I have a backported version that is verified with a v6.8 kernel. I can send if we see a strong need after this version is accepted.

This is on Github:
https://github.com/nicolinc/iommufd/commits/smmuv3_kdump-v2

Changelog
v2
* Add warning in non-coherent SMMU cases
* Keep eventq/priq disabled v.s. enabling-and-disabling-later
* Check KDUMP option in the beginning of arm_smmu_device_reset()
* Validate STRTAB format matches HW capability instead of forcing flags
v1:
https://lore.kernel.org/all/[email protected]/

Nicolin Chen (5):
iommu/arm-smmu-v3: Add arm_smmu_adopt_strtab() for kdump iommu/arm-smmu-v3: Implement is_attach_deferred() for kdump iommu/arm-smmu-v3: Retain CR0_SMMUEN during kdump device reset iommu/arm-smmu-v3: Skip EVTQ/PRIQ setup in kdump kernel iommu/arm-smmu-v3: Detect ARM_SMMU_OPT_KDUMP in
arm_smmu_device_hw_probe()

 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |   1 +
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 225 ++++++++++++++++++--
 2 files changed, 207 insertions(+), 19 deletions(-)
Signature · 1 line
2.43.0
#1 lore raw

PATCH v2 1/5 iommu/arm-smmu-v3: Add arm_smmu_adopt_strtab() for kdump

When transitioning to a kdump kernel, the primary kernel might have crashed while endpoint devices were actively bus-mastering DMA. Currently, the SMMU driver aggressively resets the hardware during probe by clearing CR0_SMMUEN and setting the Global Bypass Attribute (GBPA) to ABORT.

In a kdump scenario, this aggressive reset is highly destructive:
a) If GBPA is set to ABORT, in-flight DMA will be aborted, generating fatal PCIe AER or SErrors that may panic the kdump kernel
b) If GBPA is set to BYPASS, in-flight DMA targeting some IOVAs will bypass the SMMU and corrupt the physical memory at those 1:1 mapped IOVAs.

To safely absorb in-flight DMA, the kdump kernel must leave SMMUEN=1 intact and avoid modifying STRTAB_BASE. This allows HW to continue translating in- flight DMA using the crashed kernel's page tables until the endpoint device drivers probe and quiesce their respective hardware.

However, the ARM SMMUv3 architecture specification states that updating the SMMU_STRTAB_BASE register while SMMUEN == 1 is UNPREDICTABLE or ignored.

This leaves a kdump kernel no choice but to adopt the stream table from the crashed kernel.

Introduce ARM_SMMU_OPT_KDUMP and arm_smmu_adopt_strtab() that does memremap on all the stream tables extracted from STRTAB_BASE and STRTAB_BASE_CFG.

The option will be set in arm_smmu_device_hw_probe().

Fixes b63b3439b856 ("iommu/arm-smmu-v3: Abort all transactions if SMMU is enabled in kdump kernel")
Cc [email protected] # v6.12+
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |   1 +
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 106 +++++++++++++++++++-
 2 files changed, 106 insertions(+), 1 deletion(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +1 -0
index ef42df4753ec4..74950d98ba09f 100644
@@ -861,6 +861,7 @@ struct arm_smmu_device {
 #define ARM_SMMU_OPT_MSIPOLL		(1 << 2)
 #define ARM_SMMU_OPT_CMDQ_FORCE_SYNC	(1 << 3)
 #define ARM_SMMU_OPT_TEGRA241_CMDQV	(1 << 4)
+#define ARM_SMMU_OPT_KDUMP		(1 << 5)
 	u32				options;
 
 	struct arm_smmu_cmdq		cmdq;
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +105 -1

116 lines not on this page · raw message

Signature · 1 line
2.43.0
#2 lore raw

Re: [PATCH rc v2 1/5] iommu/arm-smmu-v3: Add arm_smmu_adopt_strtab() for kdump

Quoted 18 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +18 -0
+static int arm_smmu_adopt_strtab_2lvl(struct arm_smmu_device *smmu, u32 cfg_reg,
+				      dma_addr_t dma)
+{
+	u32 log2size = FIELD_GET(STRTAB_BASE_CFG_LOG2SIZE, cfg_reg);
+	u32 split = FIELD_GET(STRTAB_BASE_CFG_SPLIT, cfg_reg);
+	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+	u32 num_l1_ents;
+	int i;
+
+	if (log2size < split) {
+		dev_err(smmu->dev, "kdump: invalid log2size %u < split %u\n",
+			log2size, split);
+		return -EINVAL;
+	}
+
+	if (split != STRTAB_SPLIT) {
+		dev_err(smmu->dev,
+			"kdump: unsupported STRTAB_SPLIT %u (expected %u)\n",

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu) · line 4583

Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +10 -0
+			split, STRTAB_SPLIT);
+		return -EINVAL;
+	}
+
+	num_l1_ents = 1 << (log2size - split);
+	cfg->l2.l1_dma = dma;
+	cfg->l2.num_l1_ents = num_l1_ents;
+	cfg->l2.l1tab = devm_memremap(
+		smmu->dev, dma, num_l1_ents * sizeof(struct arm_smmu_strtab_l1),
+		MEMREMAP_WB);

WB shouldn't be unconditional? If the SMMU is working non-coherently we need to map it NC. Same remark everwhere

Jason

#3 lore raw

Re: [PATCH rc v2 1/5] iommu/arm-smmu-v3: Add arm_smmu_adopt_strtab() for kdump

Quoted 2 lines · Jason Gunthorpe
Quoted 1 line · Nicolin Chen

+static int arm_smmu_adopt_strtab_2lvl(struct arm_smmu_device *smmu, u32 cfg_reg,

[..]

Quoted 6 lines

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu) · line 4583

Quoted 3 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +3 -0
+	cfg->l2.l1tab = devm_memremap(
+		smmu->dev, dma, num_l1_ents * sizeof(struct arm_smmu_strtab_l1),
+		MEMREMAP_WB);

WB shouldn't be unconditional? If the SMMU is working non-coherently we need to map it NC. Same remark everwhere

Hmm, I am trying to add a coherent-only gate for the series.

MEMREMAP_WC might work. But we cannot verify that on a coherent SMMU, right?

Nicolin

#4 lore raw

Re: [PATCH rc v2 1/5] iommu/arm-smmu-v3: Add arm_smmu_adopt_strtab() for kdump

Quoted 4 lines · Nicolin Chen
Quoted 2 lines · Jason Gunthorpe
Quoted 1 line · Nicolin Chen

+static int arm_smmu_adopt_strtab_2lvl(struct arm_smmu_device *smmu, u32 cfg_reg,

[..]

Quoted 8 lines
Quoted 6 lines

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu) · line 4583

Quoted 3 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +3 -0
+	cfg->l2.l1tab = devm_memremap(
+		smmu->dev, dma, num_l1_ents * sizeof(struct arm_smmu_strtab_l1),
+		MEMREMAP_WB);

WB shouldn't be unconditional? If the SMMU is working non-coherently we need to map it NC. Same remark everwhere

Hmm, I am trying to add a coherent-only gate for the series.

OK, may just add a comment to that effect here

Quoted 2 lines

MEMREMAP_WC might work. But we cannot verify that on a coherent SMMU, right?

At most you could fake the smmu to noncoherent and check it maps the right thing and assume the arch code does it right

Jason

#5 lore raw

Re: [PATCH rc v2 1/5] iommu/arm-smmu-v3: Add arm_smmu_adopt_strtab() for kdump

Quoted 12 lines · Jason Gunthorpe
Quoted 11 lines · Nicolin Chen
Quoted 2 lines · Jason Gunthorpe
Quoted 1 line · Nicolin Chen

+static int arm_smmu_adopt_strtab_2lvl(struct arm_smmu_device *smmu, u32 cfg_reg,

[..]

Quoted 6 lines

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu) · line 4583

Quoted 3 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +3 -0
+	cfg->l2.l1tab = devm_memremap(
+		smmu->dev, dma, num_l1_ents * sizeof(struct arm_smmu_strtab_l1),
+		MEMREMAP_WB);

WB shouldn't be unconditional? If the SMMU is working non-coherently we need to map it NC. Same remark everwhere

Quoted 3 lines
Quoted 1 line

Hmm, I am trying to add a coherent-only gate for the series.

OK, may just add a comment to that effect here

Yea, I will add an assertion to the adopt functions as well.

Quoted 5 lines
Quoted 2 lines

MEMREMAP_WC might work. But we cannot verify that on a coherent SMMU, right?

At most you could fake the smmu to noncoherent and check it maps the right thing and assume the arch code does it right

I see. I tend to leave it until somebody can verify. It should be easy to make a followup change:

	unsigned long flags = coherent ? MEMREMAP_WB : MEMREMAP_WC;

Thanks
Nicolin

#6 lore raw

PATCH v2 2/5 iommu/arm-smmu-v3: Implement is_attach_deferred() for kdump

In reply to Nicolin Chen

Though the kdump kernel adopts the crashed kernel's stream table, the iommu core will still try to attach each probed device to a default domain, which overwrites the adopted STE and breaks in-flight DMA from that device.

Implement an is_attach_deferred() callback to prevent this. For each device that has STE.V=1 in the adopted table, defer the default domain attachment, until the device driver explicitly requests it.

Fixes b63b3439b856 ("iommu/arm-smmu-v3: Abort all transactions if SMMU is enabled in kdump kernel")
Cc [email protected] # v6.12+
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 28 +++++++++++++++++++++
 1 file changed, 28 insertions(+)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +28 -0
index 9a45f17200a21..d9d543eb8cecf 100644
@@ -4212,6 +4212,33 @@ static void arm_smmu_remove_master(struct arm_smmu_master *master)
 	kfree(master->build_invs);
 }
 
+static bool arm_smmu_is_attach_deferred(struct device *dev)
+{
+	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+	struct arm_smmu_device *smmu = master->smmu;
+	int i;
+
+	if (!(smmu->options & ARM_SMMU_OPT_KDUMP))
+		return false;
+
+	for (i = 0; i < master->num_streams; i++) {
+		u32 sid = master->streams[i].id;
+		struct arm_smmu_ste *step;
+
+		/* Guard against unpopulated L2 entries in the adopted table */
+		if ((smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) &&
+		    !smmu->strtab_cfg.l2.l2ptrs[arm_smmu_strtab_l1_idx(sid)])
+			continue;
+
+		step = arm_smmu_get_step_for_sid(smmu, sid);
+		/* If the STE has the Valid bit set, defer the attach */
+		if (le64_to_cpu(step->data[0]) & STRTAB_STE_0_V)
+			return true;
+	}
+
+	return false;
+}
+
 static struct iommu_device *arm_smmu_probe_device(struct device *dev)
 {
 	int ret;
@@ -4374,6 +4401,7 @@ static const struct iommu_ops arm_smmu_ops = {
 	.hw_info		= arm_smmu_hw_info,
 	.domain_alloc_sva       = arm_smmu_sva_domain_alloc,
 	.domain_alloc_paging_flags = arm_smmu_domain_alloc_paging_flags,
+	.is_attach_deferred	= arm_smmu_is_attach_deferred,
 	.probe_device		= arm_smmu_probe_device,
 	.release_device		= arm_smmu_release_device,
 	.device_group		= arm_smmu_device_group,
Signature · 1 line
2.43.0
#7 lore raw

Re: [PATCH rc v2 2/5] iommu/arm-smmu-v3: Implement is_attach_deferred() for kdump

Quoted 7 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +7 -0
+static bool arm_smmu_is_attach_deferred(struct device *dev)
+{
+	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+	struct arm_smmu_device *smmu = master->smmu;
+	int i;
+
+	if (!(smmu->options & ARM_SMMU_OPT_KDUMP))

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static void arm_smmu_remove_master(struct arm_smmu_master *master) · line 4231

Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +10 -0
+		return false;
+
+	for (i = 0; i < master->num_streams; i++) {
+		u32 sid = master->streams[i].id;
+		struct arm_smmu_ste *step;
+
+		/* Guard against unpopulated L2 entries in the adopted table */
+		if ((smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) &&
+		    !smmu->strtab_cfg.l2.l2ptrs[arm_smmu_strtab_l1_idx(sid)])
+			continue;

This can probably just call arm_smmu_init_sid_strtab()

I think it is OK to allocate another level 2 here and it also has protections for SID out of range..

Jason

#8 lore raw

Re: [PATCH rc v2 2/5] iommu/arm-smmu-v3: Implement is_attach_deferred() for kdump

Quoted 21 lines · Jason Gunthorpe
Quoted 7 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +7 -0
+static bool arm_smmu_is_attach_deferred(struct device *dev)
+{
+	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+	struct arm_smmu_device *smmu = master->smmu;
+	int i;
+
+	if (!(smmu->options & ARM_SMMU_OPT_KDUMP))

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static void arm_smmu_remove_master(struct arm_smmu_master *master) · line 4231

Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +10 -0
+		return false;
+
+	for (i = 0; i < master->num_streams; i++) {
+		u32 sid = master->streams[i].id;
+		struct arm_smmu_ste *step;
+
+		/* Guard against unpopulated L2 entries in the adopted table */
+		if ((smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) &&
+		    !smmu->strtab_cfg.l2.l2ptrs[arm_smmu_strtab_l1_idx(sid)])
+			continue;

This can probably just call arm_smmu_init_sid_strtab()

Quoted 2 lines

I think it is OK to allocate another level 2 here and it also has protections for SID out of range..

Actually, sashiko pointed out that this guard is a dead code.

arm_smmu_init_sid_strtab() is called in arm_smmu_insert_master().

Thanks
Nicolin

#9 lore raw

Re: [PATCH rc v2 2/5] iommu/arm-smmu-v3: Implement is_attach_deferred() for kdump

Quoted 22 lines · Nicolin Chen
Quoted 21 lines · Jason Gunthorpe
Quoted 7 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +7 -0
+static bool arm_smmu_is_attach_deferred(struct device *dev)
+{
+	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+	struct arm_smmu_device *smmu = master->smmu;
+	int i;
+
+	if (!(smmu->options & ARM_SMMU_OPT_KDUMP))

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static void arm_smmu_remove_master(struct arm_smmu_master *master) · line 4231

Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +10 -0
+		return false;
+
+	for (i = 0; i < master->num_streams; i++) {
+		u32 sid = master->streams[i].id;
+		struct arm_smmu_ste *step;
+
+		/* Guard against unpopulated L2 entries in the adopted table */
+		if ((smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) &&
+		    !smmu->strtab_cfg.l2.l2ptrs[arm_smmu_strtab_l1_idx(sid)])
+			continue;

This can probably just call arm_smmu_init_sid_strtab()

Quoted 6 lines
Quoted 2 lines

I think it is OK to allocate another level 2 here and it also has protections for SID out of range..

Actually, sashiko pointed out that this guard is a dead code.

arm_smmu_init_sid_strtab() is called in arm_smmu_insert_master().

Even better

Jason

#10 lore raw

PATCH v2 3/5 iommu/arm-smmu-v3: Retain CR0_SMMUEN during kdump device reset

In reply to Nicolin Chen

When ARM_SMMU_OPT_KDUMP is set, skip the GBPA/disable/CR1/CR2/STRTAB_BASE update sequence in arm_smmu_device_reset(). Those register writes are all CONSTRAINED UNPREDICTABLE while CR0_SMMUEN==1, so leaving them untouched lets in-flight DMA continue to be translated by the adopted stream table.

Initialize 'enables' to 0 so it can carry CR0_SMMUEN in kdump case. Then, preserve that when enabling the command queue.

Fixes b63b3439b856 ("iommu/arm-smmu-v3: Abort all transactions if SMMU is enabled in kdump kernel")
Cc [email protected] # v6.12+
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 29 +++++++++++++++++++--
 1 file changed, 27 insertions(+), 2 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +27 -2
index d9d543eb8cecf..b2c34713bf9f2 100644
@@ -4938,9 +4938,23 @@ static void arm_smmu_write_strtab(struct arm_smmu_device *smmu)
 static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
 {
 	int ret;
-	u32 reg, enables;
+	u32 reg, enables = 0;
 	struct arm_smmu_cmdq_ent cmd;
 
+	/*
+	 * In a kdump case, retain CR0_SMMUEN to avoid transiently aborting in-
+	 * flight DMA. According to spec, updating STRTAB_BASE, CR1, or CR2 when
+	 * CR0_SMMUEN=1 is CONSTRAINED UNPREDICTABLE. Thus, skip those register
+	 * updates and rely on the adopted stream table from the crashed kernel.
+	 */
+	if (smmu->options & ARM_SMMU_OPT_KDUMP) {
+		dev_info(smmu->dev,
+			 "kdump: retaining SMMUEN for in-flight DMA\n");
+		/* ARM_SMMU_OPT_KDUMP is only set when CR0_SMMUEN=1 */
+		enables = CR0_SMMUEN;
+		goto reset_queues;
+	}
+
 	/* Clear CR0 and sync (disables SMMU and queue processing) */
 	reg = readl_relaxed(smmu->base + ARM_SMMU_CR0);
 	if (reg & CR0_SMMUEN) {
@@ -4972,12 +4986,23 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
 	/* Stream table */
 	arm_smmu_write_strtab(smmu);
 
+reset_queues:
+	if (smmu->options & ARM_SMMU_OPT_KDUMP) {
+		/* Disable queues since arm_smmu_device_disable() was skipped */
+		ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+					      ARM_SMMU_CR0ACK);
+		if (ret) {
+			dev_err(smmu->dev, "failed to disable queues\n");
+			return ret;
+		}
+	}
+
 	/* Command queue */
 	writeq_relaxed(smmu->cmdq.q.q_base, smmu->base + ARM_SMMU_CMDQ_BASE);
 	writel_relaxed(smmu->cmdq.q.llq.prod, smmu->base + ARM_SMMU_CMDQ_PROD);
 	writel_relaxed(smmu->cmdq.q.llq.cons, smmu->base + ARM_SMMU_CMDQ_CONS);
 
-	enables = CR0_CMDQEN;
+	enables |= CR0_CMDQEN;
 	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
 				      ARM_SMMU_CR0ACK);
 	if (ret) {
Signature · 1 line
2.43.0
#11 lore raw

PATCH v2 4/5 iommu/arm-smmu-v3: Skip EVTQ/PRIQ setup in kdump kernel

In reply to Nicolin Chen

In kdump cases, the crashed kernel's CDs and page tables can be corrupted, which could trigger event spamming. Also, we cannot serve page requests.

Skip the EVTQ/PRIQ setup entirely rather than enabling then disabling them.

Also add some inline comments explaining that.

Fixes b63b3439b856 ("iommu/arm-smmu-v3: Abort all transactions if SMMU is enabled in kdump kernel")
Cc [email protected] # v6.12+
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 43 +++++++++++++--------
 1 file changed, 27 insertions(+), 16 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +27 -16
index b2c34713bf9f2..12cd148a99dc6 100644
@@ -5023,21 +5023,35 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
 	cmd.opcode = CMDQ_OP_TLBI_NSNH_ALL;
 	arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd);
 
-	/* Event queue */
-	writeq_relaxed(smmu->evtq.q.q_base, smmu->base + ARM_SMMU_EVTQ_BASE);
-	writel_relaxed(smmu->evtq.q.llq.prod, smmu->page1 + ARM_SMMU_EVTQ_PROD);
-	writel_relaxed(smmu->evtq.q.llq.cons, smmu->page1 + ARM_SMMU_EVTQ_CONS);
-
-	enables |= CR0_EVTQEN;
-	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
-				      ARM_SMMU_CR0ACK);
-	if (ret) {
-		dev_err(smmu->dev, "failed to enable event queue\n");
-		return ret;
+	/*
+	 * Event queue
+	 *
+	 * Do not enable in a kdump case, as the crashed kernel's CDs and page
+	 * tables might be corrupted, triggering event spamming.
+	 */
+	if (!is_kdump_kernel()) {
+		writeq_relaxed(smmu->evtq.q.q_base,
+			       smmu->base + ARM_SMMU_EVTQ_BASE);
+		writel_relaxed(smmu->evtq.q.llq.prod,
+			       smmu->page1 + ARM_SMMU_EVTQ_PROD);
+		writel_relaxed(smmu->evtq.q.llq.cons,
+			       smmu->page1 + ARM_SMMU_EVTQ_CONS);
+
+		enables |= CR0_EVTQEN;
+		ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+					      ARM_SMMU_CR0ACK);
+		if (ret) {
+			dev_err(smmu->dev, "failed to enable event queue\n");
+			return ret;
+		}
 	}
 
-	/* PRI queue */
-	if (smmu->features & ARM_SMMU_FEAT_PRI) {
+	/*
+	 * PRI queue
+	 *
+	 * Do not enable in a kdump case, as we cannot serve page requests.
+	 */
+	if (!is_kdump_kernel() && (smmu->features & ARM_SMMU_FEAT_PRI)) {
 		writeq_relaxed(smmu->priq.q.q_base,
 			       smmu->base + ARM_SMMU_PRIQ_BASE);
 		writel_relaxed(smmu->priq.q.llq.prod,
@@ -5070,9 +5084,6 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
 		return ret;
 	}
 
-	if (is_kdump_kernel())
-		enables &= ~(CR0_EVTQEN | CR0_PRIQEN);
-
 	/* Enable the SMMU interface */
 	enables |= CR0_SMMUEN;
 	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
Signature · 1 line
2.43.0
#12 lore raw

PATCH v2 5/5 iommu/arm-smmu-v3: Detect ARM_SMMU_OPT_KDUMP in arm_smmu_device_hw_probe()

In reply to Nicolin Chen

arm_smmu_device_hw_probe() runs before arm_smmu_init_structures(), so it's natural to decide whether the kdump kernel must adopt the crashed kernel's stream table.

Given that memremap is used to adopt the old stream table, set this option only on a coherent SMMU.

Fixes b63b3439b856 ("iommu/arm-smmu-v3: Abort all transactions if SMMU is enabled in kdump kernel")
Cc [email protected] # v6.12+
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +19 -0
index 12cd148a99dc6..5a5e0f80bbfb3 100644
@@ -5388,6 +5388,25 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
 
 	dev_info(smmu->dev, "oas %lu-bit (features 0x%08x)\n",
 		 smmu->oas, smmu->features);
+
+	/*
+	 * If SMMU is already active in kdump case, there could be in-flight DMA
+	 * from devices initiated by the crashed kernel. Mark ARM_SMMU_OPT_KDUMP
+	 * to let the init functions adopt the crashed kernel's stream table.
+	 *
+	 * Note that arm_smmu_adopt_strtab() uses memremap that can only work on
+	 * a coherent SMMU. A non-coherent SMMU has no choice but to continue to
+	 * abort any in-flight DMA.
+	 */
+	if (is_kdump_kernel() &&
+	    (readl_relaxed(smmu->base + ARM_SMMU_CR0) & CR0_SMMUEN)) {
+		if (coherent)
+			smmu->options |= ARM_SMMU_OPT_KDUMP;
+		else
+			dev_warn(smmu->dev,
+				 "kdump: in-flight DMA would be rejected\n");
+	}
+
 	return 0;
 }
 
Signature · 1 line
2.43.0
reviewer
#13 lore raw
In reply to Nicolin Chen
Quoted 10 lines · Nicolin Chen

When transitioning to a kdump kernel, the primary kernel might have crashed while endpoint devices were actively bus-mastering DMA. Currently, the SMMU driver aggressively resets the hardware during probe by clearing CR0_SMMUEN and setting the Global Bypass Attribute (GBPA) to ABORT.

In a kdump scenario, this aggressive reset is highly destructive:
a) If GBPA is set to ABORT, in-flight DMA will be aborted, generating fatal PCIe AER or SErrors that may panic the kdump kernel
b) If GBPA is set to BYPASS, in-flight DMA targeting some IOVAs will bypass the SMMU and corrupt the physical memory at those 1:1 mapped IOVAs.

But wasn't that rather the point? Th kdump kernel doesn't know the scope of how much could have gone wrong (including potentially the SMMU configuration itself), so it just blocks everything, resets and reenables the devices it cares about, and ignores whatever else might be on fire.

If AER can panic a kdump kernel, that seems like a failing of the kdump kernel itself more than anything else (especially given the likelihood that additional AER events could follow from whatever initial crash/failure triggered kdump to begin with). And frankly if some device getting a translation fault could directly SError the whole system, then I'd say that system is pretty doomed in general, kdump or not.

Thanks,
Robin.

Quoted 49 lines

To safely absorb in-flight DMA, the kdump kernel must leave SMMUEN=1 intact and avoid modifying STRTAB_BASE. This allows HW to continue translating in- flight DMA using the crashed kernel's page tables until the endpoint device drivers probe and quiesce their respective hardware.

However, the ARM SMMUv3 architecture specification states that updating the SMMU_STRTAB_BASE register while SMMUEN == 1 is UNPREDICTABLE or ignored.

This leaves a kdump kernel no choice but to adopt the stream table from the crashed kernel.

In this series:
- Introduce an ARM_SMMU_OPT_KDUMP
- Skip SMMUEN and STRTAB_BASE resets in arm_smmu_device_reset()
- Map the crashed kernel's stream tables into the kdump kernel [*]
- Defer any default domain attachment to retain STEs until device drivers explicitly request it.

[*] This is implemented via memremap, which only works on a coherent SMMU.

Note that the entire series requires Jason's work that was merged in v6.12:
85196f54743d ("iommu/arm-smmu-v3: Reorganize struct arm_smmu_strtab_cfg"). I have a backported version that is verified with a v6.8 kernel. I can send if we see a strong need after this version is accepted.

This is on Github:
https://github.com/nicolinc/iommufd/commits/smmuv3_kdump-v2

Changelog
v2
* Add warning in non-coherent SMMU cases
* Keep eventq/priq disabled v.s. enabling-and-disabling-later
* Check KDUMP option in the beginning of arm_smmu_device_reset()
* Validate STRTAB format matches HW capability instead of forcing flags
v1:
https://lore.kernel.org/all/[email protected]/

Nicolin Chen (5):
iommu/arm-smmu-v3: Add arm_smmu_adopt_strtab() for kdump iommu/arm-smmu-v3: Implement is_attach_deferred() for kdump iommu/arm-smmu-v3: Retain CR0_SMMUEN during kdump device reset iommu/arm-smmu-v3: Skip EVTQ/PRIQ setup in kdump kernel iommu/arm-smmu-v3: Detect ARM_SMMU_OPT_KDUMP in
arm_smmu_device_hw_probe()

  drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |   1 +
  drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 225 ++++++++++++++++++--
  2 files changed, 207 insertions(+), 19 deletions(-)
#14 lore raw
Quoted 12 lines · Robin Murphy
Quoted 10 lines · Nicolin Chen

When transitioning to a kdump kernel, the primary kernel might have crashed while endpoint devices were actively bus-mastering DMA. Currently, the SMMU driver aggressively resets the hardware during probe by clearing CR0_SMMUEN and setting the Global Bypass Attribute (GBPA) to ABORT.

In a kdump scenario, this aggressive reset is highly destructive:
a) If GBPA is set to ABORT, in-flight DMA will be aborted, generating fatal PCIe AER or SErrors that may panic the kdump kernel
b) If GBPA is set to BYPASS, in-flight DMA targeting some IOVAs will bypass the SMMU and corrupt the physical memory at those 1:1 mapped IOVAs.

Quoted 4 lines

But wasn't that rather the point? Th kdump kernel doesn't know the scope of how much could have gone wrong (including potentially the SMMU configuration itself), so it just blocks everything, resets and reenables the devices it cares about, and ignores whatever else might be on fire.

The purpose of kdump is to have the maximum chance to capture a dump from the blown up kernel.

Yes, on a perfect platform aborting the entire SMMU should improve the chance of getting that dump.

But sadly there are so many busted up platforms where if you start messing with the IOMMU they will explode and blow up the kdump. x86 and "firmware first" error handling systems are particularly notorious for nasty behavior like this.

Seems like there are now ARM systems too. :(

So, the iommu drivers have been preserving the IOMMU and not disrupting the DMAs on x86 for a long time. This is established kdump practice.

Quoted 4 lines

If AER can panic a kdump kernel, that seems like a failing of the kdump kernel itself more than anything else (especially given the likelihood that additional AER events could follow from whatever initial crash/failure triggered kdump to begin with).

Probably the kdump wasn't triggered by AER. You want kdump to not trigger more RAS events that might blow up the kdump while it is trying to run.. That increases the chance of success

Quoted 3 lines

And frankly if some device getting a
translation fault could directly SError the whole system, then I'd say that system is pretty doomed in general, kdump or not.

Aborting the SMMU while ATS is enabled also fails all ATS and translated requests which is a catastrophic event for a CXL type device that a correct OS should never trigger. The catastrophic explosion of the CXL device also unplugs all it's RAM from the system and the kdump kernel just cannot handle the resulting cascade of RAS failures. Plus you loose all that CXL RAM you may have wanted to dump..

Regardless, the platform has this flaw and to make kdump work it has to avoid triggering these errors like x86 does.

Jason

#15 lore raw
Quoted 34 lines

From: Jason Gunthorpe <[email protected]>
Sent: Friday, April 17, 2026 1:20 AM

Quoted 2 lines · Robin Murphy
Quoted 1 line · Nicolin Chen

When transitioning to a kdump kernel, the primary kernel might have

crashed

Quoted 1 line
Quoted 1 line

while endpoint devices were actively bus-mastering DMA. Currently, the

SMMU

Quoted 1 line
Quoted 1 line

driver aggressively resets the hardware during probe by clearing

CR0_SMMUEN

Quoted 6 lines
Quoted 6 lines

and setting the Global Bypass Attribute (GBPA) to ABORT.

In a kdump scenario, this aggressive reset is highly destructive:
a) If GBPA is set to ABORT, in-flight DMA will be aborted, generating fatal PCIe AER or SErrors that may panic the kdump kernel
b) If GBPA is set to BYPASS, in-flight DMA targeting some IOVAs will

bypass

Quoted 1 line
Quoted 1 line

the SMMU and corrupt the physical memory at those 1:1 mapped

IOVAs.

Quoted 2 lines

But wasn't that rather the point? Th kdump kernel doesn't know the scope

of

Quoted 1 line

how much could have gone wrong (including potentially the SMMU

configuration

Quoted 2 lines

itself), so it just blocks everything, resets and reenables the devices it cares about, and ignores whatever else might be on fire.

The purpose of kdump is to have the maximum chance to capture a dump from the blown up kernel.

Yes, on a perfect platform aborting the entire SMMU should improve the chance of getting that dump.

Quoted 6 lines

But sadly there are so many busted up platforms where if you start messing with the IOMMU they will explode and blow up the kdump. x86 and "firmware first" error handling systems are particularly notorious for nasty behavior like this.

Seems like there are now ARM systems too. :(

is there any report on such systems? It might be informational to include a link to the report so it's clear that this series fixes real issues instead of a preparation for coming systems...

Quoted 5 lines

So, the iommu drivers have been preserving the IOMMU and not disrupting the DMAs on x86 for a long time. This is established kdump practice.

Quoted 9 lines
Quoted 4 lines

If AER can panic a kdump kernel, that seems like a failing of the kdump kernel itself more than anything else (especially given the likelihood that additional AER events could follow from whatever initial crash/failure triggered kdump to begin with).

Probably the kdump wasn't triggered by AER. You want kdump to not trigger more RAS events that might blow up the kdump while it is trying to run.. That increases the chance of success

btw the DMA is allowed after the previous kernel is hung til the point where smmu driver blocks it. In cases where in-fly DMAs are considered dangerous to kdump, this series just make it worse instead of creating a new issue. While for majority other failures not related to DMAs, unblocking then increases the chance of success...

#16 lore raw
Quoted 3 lines · Kevin

is there any report on such systems? It might be informational to include a link to the report so it's clear that this series fixes real issues instead of a preparation for coming systems...

Yeah, we have an internal report and this was confirmed to fix it.

Quoted 5 lines

btw the DMA is allowed after the previous kernel is hung til the point where smmu driver blocks it. In cases where in-fly DMAs are considered dangerous to kdump, this series just make it worse instead of creating a new issue. While for majority other failures not related to DMAs, unblocking then increases the chance of success...

Right, exactly.

If DMA's are splattering over the kdump carve out memory its is probably dead no matter what.

Jason

#17 lore raw
In reply to Nicolin Chen
Quoted 9 lines · Nicolin Chen

This is on Github:
https://github.com/nicolinc/iommufd/commits/smmuv3_kdump-v2

Changelog
v2
* Add warning in non-coherent SMMU cases
* Keep eventq/priq disabled v.s. enabling-and-disabling-later
* Check KDUMP option in the beginning of arm_smmu_device_reset()
* Validate STRTAB format matches HW capability instead of forcing flags

https://sashiko.dev/#/patchset/cover.1776286352.git.nicolinc%40nvidia.com

Sashiko posted a few comments, mostly valid.

I am fixing them with a v3.

Thanks
Nicolin