iommu/amd: Introduce per PCI segment rlookup table
authorSuravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Wed, 6 Jul 2022 11:37:54 +0000 (17:07 +0530)
committerJoerg Roedel <jroedel@suse.de>
Thu, 7 Jul 2022 07:37:34 +0000 (09:37 +0200)
This will replace global rlookup table (amd_iommu_rlookup_table).
Add helper functions to set/get rlookup table for the given device.
Also add macros to get seg/devid from sbdf.

Co-developed-by: Vasant Hegde <vasant.hegde@amd.com>
Signed-off-by: Vasant Hegde <vasant.hegde@amd.com>
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Link: https://lore.kernel.org/r/20220706113825.25582-5-vasant.hegde@amd.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
drivers/iommu/amd/amd_iommu.h
drivers/iommu/amd/amd_iommu_types.h
drivers/iommu/amd/init.c
drivers/iommu/amd/iommu.c

index 885570c..2947239 100644 (file)
@@ -19,6 +19,7 @@ extern int amd_iommu_init_devices(void);
 extern void amd_iommu_uninit_devices(void);
 extern void amd_iommu_init_notifier(void);
 extern int amd_iommu_init_api(void);
+extern void amd_iommu_set_rlookup_table(struct amd_iommu *iommu, u16 devid);
 
 #ifdef CONFIG_AMD_IOMMU_DEBUGFS
 void amd_iommu_debugfs_setup(struct amd_iommu *iommu);
index 422ea87..d0ee78c 100644 (file)
@@ -456,6 +456,9 @@ extern bool amdr_ivrs_remap_support;
 /* kmem_cache to get tables with 128 byte alignement */
 extern struct kmem_cache *amd_iommu_irq_cache;
 
+#define PCI_SBDF_TO_SEGID(sbdf)                (((sbdf) >> 16) & 0xffff)
+#define PCI_SBDF_TO_DEVID(sbdf)                ((sbdf) & 0xffff)
+
 /* Make iterating over all pci segment easier */
 #define for_each_pci_segment(pci_seg) \
        list_for_each_entry((pci_seg), &amd_iommu_pci_seg_list, list)
@@ -490,6 +493,7 @@ struct amd_iommu_fault {
 };
 
 
+struct amd_iommu;
 struct iommu_domain;
 struct irq_domain;
 struct amd_irte_ops;
@@ -554,6 +558,13 @@ struct amd_iommu_pci_seg {
         * page table root pointer.
         */
        struct dev_table_entry *dev_table;
+
+       /*
+        * The rlookup iommu table is used to find the IOMMU which is
+        * responsible for a specific device. It is indexed by the PCI
+        * device id.
+        */
+       struct amd_iommu **rlookup_table;
 };
 
 /*
index 9f26601..35863ba 100644 (file)
@@ -665,6 +665,26 @@ static inline void free_dev_table(struct amd_iommu_pci_seg *pci_seg)
        pci_seg->dev_table = NULL;
 }
 
+/* Allocate per PCI segment IOMMU rlookup table. */
+static inline int __init alloc_rlookup_table(struct amd_iommu_pci_seg *pci_seg)
+{
+       pci_seg->rlookup_table = (void *)__get_free_pages(
+                                               GFP_KERNEL | __GFP_ZERO,
+                                               get_order(rlookup_table_size));
+       if (pci_seg->rlookup_table == NULL)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static inline void free_rlookup_table(struct amd_iommu_pci_seg *pci_seg)
+{
+       free_pages((unsigned long)pci_seg->rlookup_table,
+                  get_order(rlookup_table_size));
+       pci_seg->rlookup_table = NULL;
+}
+
+
 /*
  * Allocates the command buffer. This buffer is per AMD IOMMU. We can
  * write commands to that buffer later and the IOMMU will execute them
@@ -1491,6 +1511,8 @@ static struct amd_iommu_pci_seg *__init alloc_pci_segment(u16 id)
 
        if (alloc_dev_table(pci_seg))
                return NULL;
+       if (alloc_rlookup_table(pci_seg))
+               return NULL;
 
        return pci_seg;
 }
@@ -1513,6 +1535,7 @@ static void __init free_pci_segments(void)
 
        for_each_pci_segment_safe(pci_seg, next) {
                list_del(&pci_seg->list);
+               free_rlookup_table(pci_seg);
                free_dev_table(pci_seg);
                kfree(pci_seg);
        }
index ac8f81f..b0262b2 100644 (file)
@@ -146,6 +146,50 @@ struct dev_table_entry *get_dev_table(struct amd_iommu *iommu)
        return dev_table;
 }
 
+static inline u16 get_device_segment(struct device *dev)
+{
+       u16 seg;
+
+       if (dev_is_pci(dev)) {
+               struct pci_dev *pdev = to_pci_dev(dev);
+
+               seg = pci_domain_nr(pdev->bus);
+       } else {
+               u32 devid = get_acpihid_device_id(dev, NULL);
+
+               seg = PCI_SBDF_TO_SEGID(devid);
+       }
+
+       return seg;
+}
+
+/* Writes the specific IOMMU for a device into the PCI segment rlookup table */
+void amd_iommu_set_rlookup_table(struct amd_iommu *iommu, u16 devid)
+{
+       struct amd_iommu_pci_seg *pci_seg = iommu->pci_seg;
+
+       pci_seg->rlookup_table[devid] = iommu;
+}
+
+static struct amd_iommu *__rlookup_amd_iommu(u16 seg, u16 devid)
+{
+       struct amd_iommu_pci_seg *pci_seg;
+
+       for_each_pci_segment(pci_seg) {
+               if (pci_seg->id == seg)
+                       return pci_seg->rlookup_table[devid];
+       }
+       return NULL;
+}
+
+static struct amd_iommu *rlookup_amd_iommu(struct device *dev)
+{
+       u16 seg = get_device_segment(dev);
+       u16 devid = get_device_id(dev);
+
+       return __rlookup_amd_iommu(seg, devid);
+}
+
 static struct protection_domain *to_pdomain(struct iommu_domain *dom)
 {
        return container_of(dom, struct protection_domain, domain);