[PATCH v3 22/22] iommu/amd: Assign per-vIOMMU translate device ID

From: Suravee Suthikulpanit

Date: Mon Jun 29 2026 - 11:49:09 EST


Allocate one translate-device-id per IOMMUFD vIOMMU instance from
the per-segment pool. Program translation DTE and VFctrl TransDevID
on init; clear both on error and destroy.

Each vIOMMU owns its trans_devid independently.

Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@xxxxxxx>
---
drivers/iommu/amd/amd_iommu.h | 2 +
drivers/iommu/amd/amd_iommu_types.h | 1 +
drivers/iommu/amd/iommufd.c | 25 +++++++++++++
drivers/iommu/amd/trans_devid.c | 58 +++++++++++++++++++++++++++++
drivers/iommu/amd/viommu.c | 2 +
5 files changed, 88 insertions(+)

diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 7300f07e7a39..f0293d636718 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -225,6 +225,8 @@ 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);
+int amd_iommu_trans_devid_alloc(struct amd_iommu_pci_seg *pci_seg);
+void amd_iommu_trans_devid_free(struct amd_iommu_pci_seg *pci_seg, u16 id);
#else
static inline void
amd_iommu_pci_seg_trans_devid_init(struct amd_iommu_pci_seg *pci_seg) { }
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index cd10e33c1317..1090d7796ce5 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -557,6 +557,7 @@ struct amd_iommu_viommu {

u64 *devid_table;
u64 *domid_table;
+ u16 trans_devid;

/* Offset for mmap() of guest VF MMIO; set after iommufd_viommu_alloc_mmap(). */
unsigned long vfmmio_mmap_offset;
diff --git a/drivers/iommu/amd/iommufd.c b/drivers/iommu/amd/iommufd.c
index 8b569b469a07..0d80eaeee662 100644
--- a/drivers/iommu/amd/iommufd.c
+++ b/drivers/iommu/amd/iommufd.c
@@ -48,6 +48,9 @@ int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *
int ret;
phys_addr_t page_base;
unsigned long flags;
+ u16 trans_devid;
+ bool trans_devid_allocated = false;
+ bool trans_dte_set = false;
struct iommu_viommu_amd data = {};
struct protection_domain *pdom = to_pdomain(parent);
struct amd_iommu *iommu = container_of(viommu->iommu_dev, struct amd_iommu, iommu);
@@ -81,9 +84,22 @@ int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *

data.out_vfmmio_mmap_offset = aviommu->vfmmio_mmap_offset;

+ ret = amd_iommu_trans_devid_alloc(iommu->pci_seg);
+ if (ret < 0)
+ goto err_trans_devid;
+ trans_devid = ret;
+ trans_devid_allocated = true;
+ aviommu->trans_devid = trans_devid;
+
/* Reset vIOMMU MMIOs to initialize the vIOMMU */
iommu_reset_vmmio(iommu, aviommu->gid);

+ ret = amd_iommu_set_translate_dte(iommu, pdom, aviommu->gid, trans_devid);
+ if (ret)
+ goto err_init;
+ trans_dte_set = true;
+ amd_iommu_update_vfctrl_mmio_translate_devid(iommu, aviommu->gid, trans_devid);
+
ret = amd_viommu_init_one(iommu, aviommu);
if (ret)
goto err_init;
@@ -102,6 +118,13 @@ int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *

return 0;
err_init:
+ if (trans_dte_set) {
+ amd_iommu_update_vfctrl_mmio_translate_devid(iommu, aviommu->gid, 0);
+ amd_iommu_clear_translate_dte(iommu, trans_devid);
+ }
+ if (trans_devid_allocated)
+ amd_iommu_trans_devid_free(iommu->pci_seg, trans_devid);
+err_trans_devid:
iommufd_viommu_destroy_mmap(&aviommu->core, aviommu->vfmmio_mmap_offset);
err_mmap:
amd_iommu_gid_free(iommu, aviommu->gid);
@@ -124,6 +147,8 @@ static void amd_iommufd_viommu_destroy(struct iommufd_viommu *viommu)
xa_destroy(&aviommu->gdomid_array);
iommufd_viommu_destroy_mmap(&aviommu->core, aviommu->vfmmio_mmap_offset);
amd_viommu_uninit_one(iommu, aviommu);
+ amd_iommu_clear_translate_dte(iommu, aviommu->trans_devid);
+ amd_iommu_trans_devid_free(iommu->pci_seg, aviommu->trans_devid);
amd_iommu_gid_free(iommu, aviommu->gid);
}

diff --git a/drivers/iommu/amd/trans_devid.c b/drivers/iommu/amd/trans_devid.c
index 7c57087bfa10..6dd291d63607 100644
--- a/drivers/iommu/amd/trans_devid.c
+++ b/drivers/iommu/amd/trans_devid.c
@@ -126,3 +126,61 @@ int amd_iommu_trans_devid_reserve_pci_aliases(struct amd_iommu *iommu,
return pci_for_each_dma_alias(pdev, reserve_trans_devid_each_dma_alias,
pci_seg);
}
+
+/**
+ * amd_iommu_trans_devid_alloc - allocate a translate-device-id for @pci_seg
+ *
+ * The trans_devid is allocated from the highest id to the lowest id.
+ * Generally, the PCI devices enumerated from the beginning of the bus range.
+ * Therefore, ids in the high range are likely to not be used.
+ *
+ * Each vIOMMU receives its own translate-device-id from the per-segment pool.
+ *
+ * Return: allocated id on success, negative errno on failure.
+ */
+int amd_iommu_trans_devid_alloc(struct amd_iommu_pci_seg *pci_seg)
+{
+ int id;
+
+ mutex_lock(&pci_seg->trans_devid_mutex);
+ for (id = U16_MAX; id >= 0; id--) {
+ void *entry, *old;
+
+ entry = xa_load(&pci_seg->trans_devid_xa, id);
+ if (entry)
+ continue;
+
+ old = xa_store(&pci_seg->trans_devid_xa, id,
+ trans_devid_xa_mk_state(TRANS_DEVID_ALLOCATED), GFP_KERNEL);
+ if (xa_is_err(old)) {
+ int err = xa_err(old);
+
+ mutex_unlock(&pci_seg->trans_devid_mutex);
+ return err;
+ }
+ WARN_ON_ONCE(old);
+ mutex_unlock(&pci_seg->trans_devid_mutex);
+ pr_debug("%s: Allocated trans_devid %#x (seg %#x)\n", __func__, id,
+ pci_seg->id);
+ return id;
+ }
+ pr_err("%s: No free trans_devid found (seg %#x)\n", __func__, pci_seg->id);
+ mutex_unlock(&pci_seg->trans_devid_mutex);
+ return -ENOSPC;
+}
+
+/**
+ * amd_iommu_trans_devid_free - return @id to the per-segment pool
+ */
+void amd_iommu_trans_devid_free(struct amd_iommu_pci_seg *pci_seg, u16 id)
+{
+ void *old;
+
+ mutex_lock(&pci_seg->trans_devid_mutex);
+ old = xa_erase(&pci_seg->trans_devid_xa, id);
+ if (WARN_ON_ONCE(!old || trans_devid_xa_get_state(old) == TRANS_DEVID_FREE))
+ goto out;
+ pr_debug("%s: Freed trans_devid %#x (seg %#x)\n", __func__, id, pci_seg->id);
+out:
+ mutex_unlock(&pci_seg->trans_devid_mutex);
+}
diff --git a/drivers/iommu/amd/viommu.c b/drivers/iommu/amd/viommu.c
index 7b3127d829c5..e38d919dcec3 100644
--- a/drivers/iommu/amd/viommu.c
+++ b/drivers/iommu/amd/viommu.c
@@ -509,6 +509,8 @@ void amd_viommu_uninit_one(struct amd_iommu *iommu, struct amd_iommu_viommu *avi
VIOMMU_DOMID_MAPPING_BASE,
VIOMMU_DOMID_MAPPING_ENTRY_SIZE,
aviommu->gid);
+
+ amd_iommu_update_vfctrl_mmio_translate_devid(iommu, aviommu->gid, 0);
viommu_clear_mapping(iommu, aviommu);
}

--
2.34.1