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 d927ef1..6e1a3f8 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 10bda4b..3b65716 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 8285fcf..8deb745 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) &&                 \