[PATCH 20/24] iommu/amd: Reserve translate-device-id for PCI requestor aliases

From: Suravee Suthikulpanit

Date: Mon Jul 27 2026 - 09:56:12 EST


The per-segment translate-device-id pool tracks which device-table
indices are reserved for real PCI functions versus available for
dynamic allocation to vIOMMU translation DTEs.
amd_iommu_attach_device() already calls amd_iommu_trans_devid_reserve()
for the struct device's own PCI BDF so that id cannot later be returned
by the allocator.

That is not sufficient on its own. The AMD IOMMU driver programs
identical DMA translation device-table entries (DTEs) for every
requestor ID that can issue DMA on behalf of the same PCI function: the
IVRS alias from alias_table[] when it is not covered by the PCI
DMA-alias walk (different bus than the device), and every alias visited
by pci_for_each_dma_alias(). Those alternate BDFs are not separate
struct device attach targets, so they never received a trans_devid
reservation and could in principle collide with a dynamically allocated
trans_devid.

Introduce amd_iommu_trans_devid_reserve_pci_aliases() in trans_devid.c
and invoke it from amd_iommu_attach_device() immediately after the
primary amd_iommu_trans_devid_reserve() succeeds. For PCI devices the
helper reserves the IVRS alias when it differs from the device BDF,
then walks pci_for_each_dma_alias() and reserves each alias BDF.
Repeated attach and overlap with the primary BDF in the PCI walk are
handled by the existing idempotency of amd_iommu_trans_devid_reserve()
(a second reserve of an already-reserved id succeeds).

Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@xxxxxxx>
---
drivers/iommu/amd/amd_iommu.h | 2 ++
drivers/iommu/amd/iommu.c | 7 +++++
drivers/iommu/amd/trans_devid.c | 45 +++++++++++++++++++++++++++++++++
3 files changed, 54 insertions(+)

diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 284830673a87..f61cc51ef9a2 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -223,6 +223,8 @@ int amd_iommu_completion_wait(struct amd_iommu *iommu);
void amd_iommu_pci_seg_trans_devid_init(struct amd_iommu_pci_seg *pci_seg);
void amd_iommu_pci_seg_trans_devid_fini(struct amd_iommu_pci_seg *pci_seg);
int amd_iommu_trans_devid_reserve(struct amd_iommu_pci_seg *pci_seg, u16 id);
+int amd_iommu_trans_devid_reserve_pci_aliases(struct amd_iommu *iommu,
+ struct device *dev);
#else
static inline void
amd_iommu_pci_seg_trans_devid_init(struct amd_iommu_pci_seg *pci_seg) { }
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 02b3d3c19936..6207879528bf 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -3079,6 +3079,13 @@ static int amd_iommu_attach_device(struct iommu_domain *dom, struct device *dev,
pr_err("%s: Failed to reserve device id %#x\n", __func__, dev_data->devid);
return ret;
}
+
+ ret = amd_iommu_trans_devid_reserve_pci_aliases(iommu, dev);
+ if (ret) {
+ pr_err("%s: Failed to reserve translate devid for alias of %#x (err %d)\n",
+ __func__, dev_data->devid, ret);
+ return ret;
+ }
#endif

if (dev_data->domain)
diff --git a/drivers/iommu/amd/trans_devid.c b/drivers/iommu/amd/trans_devid.c
index 42ab19180e1c..a40686a576ec 100644
--- a/drivers/iommu/amd/trans_devid.c
+++ b/drivers/iommu/amd/trans_devid.c
@@ -11,6 +11,7 @@
*/

#include <linux/kernel.h>
+#include <linux/pci.h>
#include <linux/xarray.h>

#include "amd_iommu.h"
@@ -76,3 +77,47 @@ int amd_iommu_trans_devid_reserve(struct amd_iommu_pci_seg *pci_seg, u16 id)
pci_seg->id);
return ret;
}
+
+static int reserve_trans_devid_each_dma_alias(struct pci_dev *pdev, u16 alias,
+ void *data)
+{
+ struct amd_iommu_pci_seg *pci_seg = data;
+
+ (void)pdev;
+ return amd_iommu_trans_devid_reserve(pci_seg, alias);
+}
+
+/**
+ * amd_iommu_trans_devid_reserve_pci_aliases - reserve translate-device-ids for
+ * PCI DMA aliases and for the IVRS alias when it is not walked as a PCI DMA
+ * alias (different bus). Idempotent for repeated attach; see
+ * amd_iommu_trans_devid_reserve().
+ *
+ * Return: 0 on success or if @dev is not PCI; otherwise an errno from
+ * amd_iommu_trans_devid_reserve() or pci_for_each_dma_alias().
+ */
+int amd_iommu_trans_devid_reserve_pci_aliases(struct amd_iommu *iommu,
+ struct device *dev)
+{
+ struct pci_dev *pdev;
+ struct amd_iommu_pci_seg *pci_seg;
+ u16 devid, ivrs_alias;
+ int ret;
+
+ if (!dev_is_pci(dev))
+ return 0;
+
+ pdev = to_pci_dev(dev);
+ pci_seg = iommu->pci_seg;
+ devid = pci_dev_id(pdev);
+
+ ivrs_alias = pci_seg->alias_table[devid];
+ if (ivrs_alias != devid) {
+ ret = amd_iommu_trans_devid_reserve(pci_seg, ivrs_alias);
+ if (ret)
+ return ret;
+ }
+
+ return pci_for_each_dma_alias(pdev, reserve_trans_devid_each_dma_alias,
+ pci_seg);
+}
--
2.34.1