[PATCH v9 08/12] iommu/arm-smmu-v3-kdump: Reserve crashed kernel's ASIDs and VMIDs
From: Nicolin Chen
Date: Tue Jul 21 2026 - 16:11:17 EST
The adopted stream table keeps translating in-flight DMA, so the SMMU keeps
caching TLB entries tagged with the crashed kernel's ASIDs and VMIDs. If
this kernel handed one of those IDs to its own domain, the new domain's DMA
could hit the crashed kernel's cached translations, e.g. a stale entry left
behind by an invalidation that the crash cut short.
Scan the adopted stream table at adoption time, reserving every ID in use
via arm_smmu_kexec_scan_and_resv_ids(), under the arm_smmu_kexec_resv_lock
so that a failing scan can atomically roll back all of its reservations
via arm_smmu_kexec_unresv_ids().
Nested STE's guest-owned CD table is in IPA space and left alone: its ASIDs
only pair with the nonzero S2VMID being reserved, so they cannot alias this
kernel's VMID-0 or EL2 stage-1 domains.
Note that, on an E2H/VHE host, the kernel's stage-1 domains are tagged by
the EL2 ASID, and the TLBI_EL2_* commands take no VMID. So isolating this
kernel by a reserved VMID alone would not work. Reserving the ASIDs covers
both the E2H and the NSEL1 cases.
Reservations are never released: a kdump kernel reboots after it saves the
vmcore, and the full-reset fallback flushes the entire TLB, which turns any
stale reservation into a merely unused ID.
If the scan finds any inconsistent structure, toss the entire adoption and
fall back to the full reset.
Suggested-by: Jason Gunthorpe <jgg@xxxxxxxxxx>
Assisted-by: Claude:claude-fable-5
Signed-off-by: Nicolin Chen <nicolinc@xxxxxxxxxx>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kdump.c | 15 ++++++++++++++-
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 1 +
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kdump.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kdump.c
index a074d59ce3445..3e0d544c6bc53 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kdump.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kdump.c
@@ -205,16 +205,29 @@ int arm_smmu_kdump_adopt_strtab(struct arm_smmu_device *smmu)
goto err;
}
+ mutex_lock(&arm_smmu_kexec_resv_lock);
+ ret = arm_smmu_kexec_scan_and_resv_ids(smmu);
+ if (ret) {
+ dev_warn(smmu->dev, "failed to reserve in-use ASIDs/VMIDs\n");
+ arm_smmu_kdump_adopt_cleanup(smmu);
+ goto err_unresv;
+ }
+
ret = devm_add_action_or_reset(smmu->dev, arm_smmu_kdump_adopt_cleanup,
smmu);
/* devm_add_action_or_reset ran the cleanup upon failure */
if (ret) {
dev_warn(smmu->dev, "failed to set up cleanup action\n");
- goto err;
+ goto err_unresv;
}
+ mutex_unlock(&arm_smmu_kexec_resv_lock);
return 0;
+err_unresv:
+ /* The full reset will flush the entire TLB, so release everything */
+ arm_smmu_kexec_unresv_ids(smmu);
+ mutex_unlock(&arm_smmu_kexec_resv_lock);
err:
dev_warn(smmu->dev, "falling back to full reset\n");
/*
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 b768080c04db4..9d9fd11d26efa 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -4516,6 +4516,7 @@ static int arm_smmu_init_strtab(struct arm_smmu_device *smmu)
{
int ret;
+ /* Init first, as a kdump adoption reserves in-use VMIDs in the ida */
ida_init(&smmu->vmid_map);
ret = devm_add_action_or_reset(smmu->dev, arm_smmu_deinit_strtab, smmu);
if (ret)
--
2.43.0