[PATCH v9 08/12] iommu/arm-smmu-v3-kdump: Reserve crashed kernel's ASIDs and VMIDs
Nicolin Chen <[email protected]>
| Newsgroups | dev.linux.lists.iommu,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <28a26f13a91bf26a7b8215a0bea493627cd9c832.1784663184.git.nicolinc@nvidia.com> |
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 <[email protected]> Assisted-by: Claude:claude-fable-5 Signed-off-by: Nicolin Chen <[email protected]> --- 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