iommu/vt-d: debugfs: Remove device_domain_lock usage
authorLu Baolu <baolu.lu@linux.intel.com>
Tue, 12 Jul 2022 00:08:52 +0000 (08:08 +0800)
committerJoerg Roedel <jroedel@suse.de>
Fri, 15 Jul 2022 08:21:33 +0000 (10:21 +0200)
The domain_translation_struct debugfs node is used to dump the DMAR page
tables for the PCI devices. It potentially races with setting domains to
devices. The existing code uses the global spinlock device_domain_lock to
avoid the races.

This removes the use of device_domain_lock outside of iommu.c by replacing
it with the group mutex lock. Using the group mutex lock is cleaner and
more compatible to following cleanups.

Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Link: https://lore.kernel.org/r/20220706025524.2904370-2-baolu.lu@linux.intel.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
drivers/iommu/intel/debugfs.c
drivers/iommu/intel/iommu.c
drivers/iommu/intel/iommu.h

index d927ef10641b30bcd86c8699a2d42900a4b68649..6e1a3f88abc8c8010bd21e73db97c1d8851b81fc 100644 (file)
@@ -342,13 +342,13 @@ static void pgtable_walk_level(struct seq_file *m, struct dma_pte *pde,
        }
 }
 
-static int show_device_domain_translation(struct device *dev, void *data)
+static int __show_device_domain_translation(struct device *dev, void *data)
 {
-       struct device_domain_info *info = dev_iommu_priv_get(dev);
-       struct dmar_domain *domain = info->domain;
+       struct dmar_domain *domain;
        struct seq_file *m = data;
        u64 path[6] = { 0 };
 
+       domain = to_dmar_domain(iommu_get_domain_for_dev(dev));
        if (!domain)
                return 0;
 
@@ -359,20 +359,39 @@ static int show_device_domain_translation(struct device *dev, void *data)
        pgtable_walk_level(m, domain->pgd, domain->agaw + 2, 0, path);
        seq_putc(m, '\n');
 
-       return 0;
+       /* Don't iterate */
+       return 1;
 }
 
-static int domain_translation_struct_show(struct seq_file *m, void *unused)
+static int show_device_domain_translation(struct device *dev, void *data)
 {
-       unsigned long flags;
-       int ret;
+       struct iommu_group *group;
 
-       spin_lock_irqsave(&device_domain_lock, flags);
-       ret = bus_for_each_dev(&pci_bus_type, NULL, m,
-                              show_device_domain_translation);
-       spin_unlock_irqrestore(&device_domain_lock, flags);
+       group = iommu_group_get(dev);
+       if (group) {
+               /*
+                * The group->mutex is held across the callback, which will
+                * block calls to iommu_attach/detach_group/device. Hence,
+                * the domain of the device will not change during traversal.
+                *
+                * All devices in an iommu group share a single domain, hence
+                * we only dump the domain of the first device. Even though,
+                * this code still possibly races with the iommu_unmap()
+                * interface. This could be solved by RCU-freeing the page
+                * table pages in the iommu_unmap() path.
+                */
+               iommu_group_for_each_dev(group, data,
+                                        __show_device_domain_translation);
+               iommu_group_put(group);
+       }
 
-       return ret;
+       return 0;
+}
+
+static int domain_translation_struct_show(struct seq_file *m, void *unused)
+{
+       return bus_for_each_dev(&pci_bus_type, NULL, m,
+                               show_device_domain_translation);
 }
 DEFINE_SHOW_ATTRIBUTE(domain_translation_struct);
 
index 10bda4bec8fe2e5877cd8874eefb533e43ba3b6e..3b6571681bdd939695cbee48f83272ebb9b8da57 100644 (file)
@@ -314,7 +314,7 @@ static int iommu_skip_te_disable;
 #define IDENTMAP_GFX           2
 #define IDENTMAP_AZALIA                4
 
-DEFINE_SPINLOCK(device_domain_lock);
+static DEFINE_SPINLOCK(device_domain_lock);
 static LIST_HEAD(device_domain_list);
 
 const struct iommu_ops intel_iommu_ops;
index 8285fcfa5fea792b0ff0476102d69c6428b3ddb2..8deb745d8b364e78cc43fee0795abf067823852e 100644 (file)
@@ -480,7 +480,6 @@ enum {
 #define VTD_FLAG_SVM_CAPABLE           (1 << 2)
 
 extern int intel_iommu_sm;
-extern spinlock_t device_domain_lock;
 
 #define sm_supported(iommu)    (intel_iommu_sm && ecap_smts((iommu)->ecap))
 #define pasid_supported(iommu) (sm_supported(iommu) &&                 \