[PATCH 3/7] iommu/vt-d: Handle DID reservation errors when copying context tables
From: Lu Baolu
Date: Wed Sep 09 2026 - 04:20:21 EST
When kdump reuses old translation tables, all old domain IDs (DIDs) must
be reserved in the new kernel before new domains are created. Today
copy_context_table() ignores ida_alloc_range() return values, so a real
-ENOMEM can be missed and an ID may stay unreserved.
That can allow DID reuse while stale hardware cache entries still exist,
risking domain aliasing.
Fix this by moving DID reservation into a helper that:
- treats duplicate reservations (-ENOSPC) as expected success,
- skips out-of-range IDs as success,
- propagates real allocation failures (like -ENOMEM), and
- normalize successful return values.
On failure, unwind as in existing copy-allocation failure paths.
Fixes: f93b4ac5929a ("iommu/vt-d: Use ida to manage domain id")
Signed-off-by: Lu Baolu <baolu.lu@xxxxxxxxxxxxxxx>
---
drivers/iommu/intel/iommu.c | 39 +++++++++++++++++++++++++++++++++----
1 file changed, 35 insertions(+), 4 deletions(-)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 38e2a670df9a..ab46058d76c5 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -1452,12 +1452,39 @@ static void intel_iommu_init_qi(struct intel_iommu *iommu)
}
}
+/*
+ * Reserve a domain ID inherited from the previous kernel so that it is not
+ * handed out again while the copied translation structures are still live.
+ *
+ * Returns 0 when the ID is reserved, was already reserved, or cannot be
+ * re-assigned, and a negative errno for a genuine allocation failure.
+ */
+static int reserve_domain_id(struct intel_iommu *iommu, int did)
+{
+ int ret;
+
+ if (did < 0 || did >= iommu->max_domain_id)
+ return 0;
+
+ ret = ida_alloc_range(&iommu->domain_ida, did, did, GFP_KERNEL);
+ /*
+ * Devices sharing a domain share its ID, so the same ID is seen in
+ * more than one context entry; -ENOSPC merely reports that it is
+ * already reserved. On success the allocated ID is returned, which
+ * is not an error either.
+ */
+ if (ret == -ENOSPC || ret >= 0)
+ return 0;
+
+ return ret;
+}
+
static int copy_context_table(struct intel_iommu *iommu,
struct root_entry *old_re,
struct context_entry **tbl,
int bus, bool ext)
{
- int tbl_idx, tbl_slot = 0, idx, devfn, ret = 0, did;
+ int tbl_idx, tbl_slot = 0, idx, devfn, ret = 0;
struct context_entry *new_ce = NULL, ce;
struct context_entry *old_ce = NULL;
struct root_entry re;
@@ -1520,9 +1547,13 @@ static int copy_context_table(struct intel_iommu *iommu,
if (!context_present(&ce))
continue;
- did = context_domain_id(&ce);
- if (did >= 0 && did < iommu->max_domain_id)
- ida_alloc_range(&iommu->domain_ida, did, did, GFP_KERNEL);
+ ret = reserve_domain_id(iommu, context_domain_id(&ce));
+ if (ret) {
+ /* Not yet published through @tbl, so free it here. */
+ iommu_free_pages(new_ce);
+ new_ce = NULL;
+ goto out_unmap;
+ }
set_context_copied(iommu, bus, devfn);
new_ce[idx] = ce;
--
2.43.0