@@ -111,6 +111,8 @@ static const struct iommu_regset iommu_regs_64[] = {
IOMMU_REGSET_ENTRY(VCRSP),
};
+static struct dentry *intel_iommu_debug;
+
static int iommu_regset_show(struct seq_file *m, void *unused)
{
struct dmar_drhd_unit *drhd;
@@ -673,16 +675,12 @@ static const struct file_operations dmar_perf_latency_fops = {
void __init intel_iommu_debugfs_init(void)
{
- struct dentry *intel_iommu_debug = debugfs_create_dir("intel",
- iommu_debugfs_dir);
+ intel_iommu_debug = debugfs_create_dir("intel", iommu_debugfs_dir);
debugfs_create_file("iommu_regset", 0444, intel_iommu_debug, NULL,
&iommu_regset_fops);
debugfs_create_file("dmar_translation_struct", 0444, intel_iommu_debug,
NULL, &dmar_translation_struct_fops);
- debugfs_create_file("domain_translation_struct", 0444,
- intel_iommu_debug, NULL,
- &domain_translation_struct_fops);
debugfs_create_file("invalidation_queue", 0444, intel_iommu_debug,
NULL, &invalidation_queue_fops);
#ifdef CONFIG_IRQ_REMAP
@@ -692,3 +690,100 @@ void __init intel_iommu_debugfs_init(void)
debugfs_create_file("dmar_perf_latency", 0644, intel_iommu_debug,
NULL, &dmar_perf_latency_fops);
}
+
+/* Create a debugfs directory for each device. */
+void intel_iommu_debugfs_create_dev(struct device *dev)
+{
+ struct device_domain_info *info = dev_iommu_priv_get(dev);
+
+ info->debugfs_dentry = debugfs_create_dir(dev_name(dev),
+ intel_iommu_debug);
+}
+
+/* Remove the device debugfs directory. */
+void intel_iommu_debugfs_remove_dev(struct device *dev)
+{
+ struct device_domain_info *info = dev_iommu_priv_get(dev);
+
+ if (!info || IS_ERR_OR_NULL(info->debugfs_dentry))
+ return;
+
+ debugfs_remove_recursive(info->debugfs_dentry);
+}
+
+/*
+ * Create a debugfs directory per pair of {device, pasid},
+ * then create the corresponding debugfs file in this directory
+ * for users to dump its page table. e.g.
+ * /sys/kernel/debug/iommu/intel/0000:00:01.0/1/domain_translation_struct
+ * For the default domain of the device, create a debugfs file in the
+ * debugfs device directory for users to dump its page table. e.g.
+ * /sys/kernel/debug/iommu/intel/0000:00:01.0/domain_translation_struct
+ */
+void intel_iommu_debugfs_create_dev_pasid(struct iommu_domain *domain,
+ struct device_domain_info *info,
+ struct dev_pasid_info *dev_pasid)
+{
+ struct dentry *parent;
+ void *data;
+
+ if (!info || IS_ERR_OR_NULL(info->debugfs_dentry))
+ return;
+
+ /*
+ * The debugfs only dumps the page tables whose mappings are created
+ * and destroyed by the iommu_map/unmap() interfaces. Check the
+ * mapping type of the domain before creating debugfs directory.
+ */
+ if (!domain ||
+ !(domain->type & __IOMMU_DOMAIN_PAGING))
+ return;
+
+ if (dev_pasid) {
+ char dir_name[10];
+
+ sprintf(dir_name, "%x", dev_pasid->pasid);
+ dev_pasid->debugfs_dentry = debugfs_create_dir(dir_name,
+ info->debugfs_dentry);
+ if (IS_ERR(dev_pasid->debugfs_dentry))
+ return;
+
+ data = dev_pasid;
+ parent = dev_pasid->debugfs_dentry;
+ } else {
+ data = info;
+ parent = info->debugfs_dentry;
+ }
+
+ debugfs_create_file("domain_translation_struct", 0444, parent,
+ data, &domain_translation_struct_fops);
+}
+
+/*
+ * Remove the corresponding debugfs directory and file.
+ * If a pair {device, pasid} is specified, remove the debugfs
+ * directory of the specified pasid.
+ * If they are not specified, remove the debugfs file of the
+ * default domain.
+ */
+void intel_iommu_debugfs_remove_dev_pasid(struct device *dev,
+ struct dev_pasid_info *dev_pasid)
+{
+ struct device_domain_info *info = dev_iommu_priv_get(dev);
+
+ if (!info || IS_ERR_OR_NULL(info->debugfs_dentry))
+ return;
+
+ if (dev_pasid)
+ debugfs_remove_recursive(dev_pasid->debugfs_dentry);
+ else {
+ struct dentry *dentry;
+
+ dentry = debugfs_lookup("domain_translation_struct",
+ info->debugfs_dentry);
+ if (dentry) {
+ debugfs_remove(dentry);
+ dput(dentry);
+ }
+ }
+}
@@ -2488,6 +2488,8 @@ static int dmar_domain_attach_device(struct dmar_domain *domain,
iommu_enable_pci_caps(info);
+ intel_iommu_debugfs_create_dev_pasid(&domain->domain, info, NULL);
+
return 0;
}
@@ -3997,6 +3999,8 @@ static void device_block_translation(struct device *dev)
domain_detach_iommu(info->domain, iommu);
info->domain = NULL;
+
+ intel_iommu_debugfs_remove_dev_pasid(dev, NULL);
}
static int md_domain_init(struct dmar_domain *domain, int guest_width)
@@ -4424,6 +4428,8 @@ static struct iommu_device *intel_iommu_probe_device(struct device *dev)
}
}
+ intel_iommu_debugfs_create_dev(dev);
+
return &iommu->iommu;
}
@@ -4433,6 +4439,7 @@ static void intel_iommu_release_device(struct device *dev)
dmar_remove_one_dev_info(dev);
intel_pasid_free_table(dev);
+ intel_iommu_debugfs_remove_dev(dev);
dev_iommu_priv_set(dev, NULL);
kfree(info);
set_dma_ops(dev, NULL);
@@ -4725,6 +4732,8 @@ static void intel_iommu_remove_dev_pasid(struct device *dev, ioasid_t pasid)
spin_unlock_irqrestore(&dmar_domain->lock, flags);
domain_detach_iommu(dmar_domain, iommu);
+
+ intel_iommu_debugfs_remove_dev_pasid(dev, dev_pasid);
kfree(dev_pasid);
out_tear_down:
intel_pasid_tear_down_entry(iommu, dev, pasid, false);
@@ -4777,6 +4786,8 @@ static int intel_iommu_set_dev_pasid(struct iommu_domain *domain,
list_add(&dev_pasid->link_domain, &dmar_domain->dev_pasids);
spin_unlock_irqrestore(&dmar_domain->lock, flags);
+ intel_iommu_debugfs_create_dev_pasid(domain, info, dev_pasid);
+
return 0;
out_detach_iommu:
domain_detach_iommu(dmar_domain, iommu);
@@ -716,12 +716,14 @@ struct device_domain_info {
struct intel_iommu *iommu; /* IOMMU used by this device */
struct dmar_domain *domain; /* pointer to domain */
struct pasid_table *pasid_table; /* pasid table */
+ struct dentry *debugfs_dentry; /* pointer to device directory dentry */
};
struct dev_pasid_info {
struct list_head link_domain; /* link to domain siblings */
struct device *dev;
ioasid_t pasid;
+ struct dentry *debugfs_dentry; /* pointer to pasid directory dentry */
};
static inline void __iommu_flush_cache(
@@ -883,8 +885,24 @@ static inline void intel_svm_remove_dev_pasid(struct device *dev, ioasid_t pasid
#ifdef CONFIG_INTEL_IOMMU_DEBUGFS
void intel_iommu_debugfs_init(void);
+void intel_iommu_debugfs_create_dev(struct device *dev);
+void intel_iommu_debugfs_remove_dev(struct device *dev);
+void intel_iommu_debugfs_create_dev_pasid(struct iommu_domain *domain,
+ struct device_domain_info *info,
+ struct dev_pasid_info *dev_pasid);
+void intel_iommu_debugfs_remove_dev_pasid(struct device *dev,
+ struct dev_pasid_info *dev_pasid);
#else
static inline void intel_iommu_debugfs_init(void) {}
+static inline void intel_iommu_debugfs_create_dev(struct device *dev) {}
+static inline void intel_iommu_debugfs_remove_dev(struct device *dev) {}
+static inline
+void intel_iommu_debugfs_create_dev_pasid(struct iommu_domain *domain,
+ struct device_domain_info *info,
+ struct dev_pasid_info *dev_pasid){}
+static inline
+void intel_iommu_debugfs_remove_dev_pasid(struct device *dev,
+ struct dev_pasid_info *dev_pasid){}
#endif /* CONFIG_INTEL_IOMMU_DEBUGFS */
extern const struct attribute_group *intel_iommu_groups[];