[RFC PATCH 3/6] iommu/amd: Introduce Guest-ID struct amd_iommu_vminfo

Suravee Suthikulpanit
2023-12-12T16:02:28Z

AMD HW-vIOMMU feature requires IOMMU driver to specify a unique 16-bit
Guest ID (GID) for each VM. This ID is used to index into various
data structures for configuring the hardware.

Introduce amd_iommu_vminfo_hash hashtable to store per-vm configuration,
which uses 16-bit GID as a hash key along with helper functions.

Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu.h | 6 +++
drivers/iommu/amd/amd_iommu_types.h | 6 +++
drivers/iommu/amd/iommu.c | 66 +++++++++++++++++++++++++++++
3 files changed, 78 insertions(+)

diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 4118129f4a24..7783a933ad14 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -182,4 +182,10 @@ void amd_iommu_domain_set_pgtable(struct protection_domain *domain,
struct dev_table_entry *get_dev_table(struct amd_iommu *iommu);

extern bool amd_iommu_snp_en;
+
+/* AMD IOMMU GID */
+int amd_iommu_vminfo_alloc(struct amd_iommu *iommu, struct amd_iommu_vminfo *vminfo);
+void amd_iommu_vminfo_free(struct amd_iommu *iommu, struct amd_iommu_vminfo *vminfo);
+struct amd_iommu_vminfo *amd_iommu_get_vminfo(int gid);
+
#endif
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index 956fd4658a4a..a00731673c50 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -16,6 +16,7 @@
#include <linux/pci.h>
#include <linux/irqreturn.h>
#include <linux/io-pgtable.h>
+#include <linux/hashtable.h>

/*
* Maximum number of IOMMUs supported
@@ -1053,6 +1054,11 @@ struct amd_irte_ops {
void (*clear_allocated)(struct irq_remap_table *, int);
};

+struct amd_iommu_vminfo {
+ u16 gid;
+ struct hlist_node hnode;
+};
+
#ifdef CONFIG_IRQ_REMAP
extern struct amd_irte_ops irte_32_ops;
extern struct amd_irte_ops irte_128_ops;
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index c41932e9f16a..d18b23ac6357 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -23,6 +23,7 @@
#include <linux/amd-iommu.h>
#include <linux/notifier.h>
#include <linux/export.h>
+#include <linux/idr.h>
#include <linux/irq.h>
#include <linux/msi.h>
#include <linux/irqdomain.h>
@@ -66,6 +67,16 @@ LIST_HEAD(acpihid_map);
const struct iommu_ops amd_iommu_ops;
static const struct iommu_dirty_ops amd_dirty_ops;

+/* VMInfo Hashtable */
+#define AMD_IOMMU_VMINFO_HASH_BITS 16
+DEFINE_HASHTABLE(amd_iommu_vminfo_hash, AMD_IOMMU_VMINFO_HASH_BITS);
+DEFINE_SPINLOCK(amd_iommu_vminfo_hash_lock);
+
+/* Global VMID */
+#define AMD_IOMMU_VMID_INVALID (-1U)
+static DEFINE_IDA(amd_iommu_global_vmid_ida);
+static u32 amd_iommu_latest_gid;
+
int amd_iommu_max_glx_val = -1;

/*
@@ -101,6 +112,61 @@ static inline bool domain_id_is_per_dev(struct protection_domain *pdom)
return (pdom && pdom->pd_mode != PD_MODE_V1);
}

+int get_vmid(void)
+{
+ int ret;
+
+ ret = ida_alloc_range(&amd_iommu_global_vmid_ida, 1, 0xFFFF, GFP_KERNEL);
+ return ret < 0 ? AMD_IOMMU_VMID_INVALID : ret;
+}
+
+int amd_iommu_vminfo_alloc(struct amd_iommu *iommu, struct amd_iommu_vminfo *vminfo)
+{
+ u32 gid;
+ unsigned long flags;
+
+ spin_lock_irqsave(&amd_iommu_vminfo_hash_lock, flags);
+ gid = amd_iommu_latest_gid = get_vmid();
+ if (gid == AMD_IOMMU_VMID_INVALID)
+ return -EINVAL;
+
+ pr_debug("%s: gid=%u\n", __func__, gid);
+ vminfo->gid = gid;
+ hash_add(amd_iommu_vminfo_hash, &vminfo->hnode, vminfo->gid);
+ spin_unlock_irqrestore(&amd_iommu_vminfo_hash_lock, flags);
+ return 0;
+}
+
+void amd_iommu_vminfo_free(struct amd_iommu *iommu,
+ struct amd_iommu_vminfo *vminfo)
+{
+ unsigned long flags;
+
+ pr_debug("%s: gid=%u\n", __func__, vminfo->gid);
+ spin_lock_irqsave(&amd_iommu_vminfo_hash_lock, flags);
+ hash_del(&vminfo->hnode);
+ ida_free(&amd_iommu_global_vmid_ida, vminfo->gid);
+ spin_unlock_irqrestore(&amd_iommu_vminfo_hash_lock, flags);
+}
+
+struct amd_iommu_vminfo *amd_iommu_get_vminfo(int gid)
+{
+ unsigned long flags;
+ struct amd_iommu_vminfo *tmp, *ptr = NULL;
+
+ spin_lock_irqsave(&amd_iommu_vminfo_hash_lock, flags);
+ hash_for_each_possible(amd_iommu_vminfo_hash, tmp, hnode, gid) {
+ if (tmp->gid == gid) {
+ ptr = tmp;
+ break;
+ }
+ }
+ if (!ptr)
+ pr_debug("%s : gid=%u not found\n", __func__, gid);
+ spin_unlock_irqrestore(&amd_iommu_vminfo_hash_lock, flags);
+ return ptr;
+}
+
static inline int get_acpihid_device_id(struct device *dev,
struct acpihid_map_entry **entry)
{
--
2.34.1