iommu: iommu-debug: Rework CONFIG_IOMMU_DEBUG_TRACKING

If CONFIG_IOMMU_DEBUG_TRACKING=y, a deadlock will occur if userspace
writes to the debugfs file "attach":

    -001|context_switch(inline)
    -001|__schedule()
    -002|__preempt_count_sub(inline)
    -002|schedule()
    -003|schedule_timeout()
    -004|do_wait_for_common(inline)
    -004|__wait_for_common(inline)
    -004|wait_for_common()
    -005|wait_for_completion()
    -006|__synchronize_srcu()
    -007|synchronize_srcu()
    -008|debugfs_remove_recursive()
    -009|iommu_debug_detach_device()
    -010|__iommu_detach_device()
    -011|__iommu_group_for_each_dev(inline)
    -011|__iommu_detach_group()
    -012|iommu_detach_device()
    -013|iommu_debug_device_profiling()
    -014|iommu_debug_device_show()
    -015|seq_read()
    -016|full_proxy_read()
    -017|__vfs_read()
    -018|vfs_read()

Since the files in the attachment directory are rarely used, remove
them. Update the data saved by this config option to work properly
with iommu groups.

Change-Id: I3f0f1d0836f37412915ce3e78d8aefe92dc8d096
Signed-off-by: Patrick Daly <pdaly@codeaurora.org>
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index bea5f03..0c49a64 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -81,330 +81,58 @@
 
 static DEFINE_MUTEX(iommu_debug_attachments_lock);
 static LIST_HEAD(iommu_debug_attachments);
-static struct dentry *debugfs_attachments_dir;
 
+/*
+ * Each group may have more than one domain; but each domain may
+ * only have one group.
+ * Used by debug tools to display the name of the device(s) associated
+ * with a particular domain.
+ */
 struct iommu_debug_attachment {
 	struct iommu_domain *domain;
-	struct device *dev;
-	struct dentry *dentry;
+	struct iommu_group *group;
 	struct list_head list;
-	unsigned long reg_offset;
 };
 
-static int iommu_debug_attachment_info_show(struct seq_file *s, void *ignored)
-{
-	struct iommu_debug_attachment *attach = s->private;
-	int secure_vmid;
-
-	seq_printf(s, "Domain: 0x%p\n", attach->domain);
-
-	seq_puts(s, "SECURE_VMID: ");
-	if (iommu_domain_get_attr(attach->domain,
-				  DOMAIN_ATTR_SECURE_VMID,
-				  &secure_vmid))
-		seq_puts(s, "(Unknown)\n");
-	else
-		seq_printf(s, "%s (0x%x)\n",
-			   msm_secure_vmid_to_string(secure_vmid), secure_vmid);
-
-	return 0;
-}
-
-static int iommu_debug_attachment_info_open(struct inode *inode,
-					    struct file *file)
-{
-	return single_open(file, iommu_debug_attachment_info_show,
-			   inode->i_private);
-}
-
-static const struct file_operations iommu_debug_attachment_info_fops = {
-	.open	 = iommu_debug_attachment_info_open,
-	.read	 = seq_read,
-	.llseek	 = seq_lseek,
-	.release = single_release,
-};
-
-static ssize_t iommu_debug_attachment_reg_offset_write(
-	struct file *file, const char __user *ubuf, size_t count,
-	loff_t *offset)
-{
-	struct iommu_debug_attachment *attach = file->private_data;
-	unsigned long reg_offset;
-
-	if (kstrtoul_from_user(ubuf, count, 0, &reg_offset)) {
-		pr_err("Invalid reg_offset format\n");
-		return -EFAULT;
-	}
-
-	attach->reg_offset = reg_offset;
-
-	return count;
-}
-
-static const struct file_operations iommu_debug_attachment_reg_offset_fops = {
-	.open	= simple_open,
-	.write	= iommu_debug_attachment_reg_offset_write,
-};
-
-static ssize_t iommu_debug_attachment_reg_read_read(
-	struct file *file, char __user *ubuf, size_t count, loff_t *offset)
-{
-	struct iommu_debug_attachment *attach = file->private_data;
-	unsigned long val;
-	char *val_str;
-	ssize_t val_str_len;
-
-	if (*offset)
-		return 0;
-
-	val = iommu_reg_read(attach->domain, attach->reg_offset);
-	val_str = kasprintf(GFP_KERNEL, "0x%lx\n", val);
-	if (!val_str)
-		return -ENOMEM;
-	val_str_len = strlen(val_str);
-
-	if (copy_to_user(ubuf, val_str, val_str_len)) {
-		pr_err("copy_to_user failed\n");
-		val_str_len = -EFAULT;
-		goto out;
-	}
-	*offset = 1;		/* non-zero means we're done */
-
-out:
-	kfree(val_str);
-	return val_str_len;
-}
-
-static const struct file_operations iommu_debug_attachment_reg_read_fops = {
-	.open	= simple_open,
-	.read	= iommu_debug_attachment_reg_read_read,
-};
-
-static ssize_t iommu_debug_attachment_reg_write_write(
-	struct file *file, const char __user *ubuf, size_t count,
-	loff_t *offset)
-{
-	struct iommu_debug_attachment *attach = file->private_data;
-	unsigned long val;
-
-	if (kstrtoul_from_user(ubuf, count, 0, &val)) {
-		pr_err("Invalid val format\n");
-		return -EFAULT;
-	}
-
-	iommu_reg_write(attach->domain, attach->reg_offset, val);
-
-	return count;
-}
-
-static const struct file_operations iommu_debug_attachment_reg_write_fops = {
-	.open	= simple_open,
-	.write	= iommu_debug_attachment_reg_write_write,
-};
-
-/* should be called with iommu_debug_attachments_lock locked */
-static int iommu_debug_attach_add_debugfs(
-	struct iommu_debug_attachment *attach)
-{
-	const char *attach_name;
-	struct device *dev = attach->dev;
-	struct iommu_domain *domain = attach->domain;
-	int is_dynamic;
-
-	if (iommu_domain_get_attr(domain, DOMAIN_ATTR_DYNAMIC, &is_dynamic))
-		is_dynamic = 0;
-
-	if (is_dynamic) {
-		uuid_le uuid;
-
-		uuid_le_gen(&uuid);
-		attach_name = kasprintf(GFP_KERNEL, "%s-%pUl", dev_name(dev),
-					uuid.b);
-		if (!attach_name)
-			return -ENOMEM;
-	} else {
-		attach_name = dev_name(dev);
-	}
-
-	attach->dentry = debugfs_create_dir(attach_name,
-					    debugfs_attachments_dir);
-	if (!attach->dentry) {
-		pr_err("Couldn't create iommu/attachments/%s debugfs directory for domain 0x%p\n",
-		       attach_name, domain);
-		if (is_dynamic)
-			kfree(attach_name);
-		return -EIO;
-	}
-
-	if (is_dynamic)
-		kfree(attach_name);
-
-	if (!debugfs_create_file(
-		    "info", S_IRUSR, attach->dentry, attach,
-		    &iommu_debug_attachment_info_fops)) {
-		pr_err("Couldn't create iommu/attachments/%s/info debugfs file for domain 0x%p\n",
-		       dev_name(dev), domain);
-		goto err_rmdir;
-	}
-
-	if (!debugfs_create_file(
-		    "reg_offset", S_IRUSR, attach->dentry, attach,
-		    &iommu_debug_attachment_reg_offset_fops)) {
-		pr_err("Couldn't create iommu/attachments/%s/reg_offset debugfs file for domain 0x%p\n",
-		       dev_name(dev), domain);
-		goto err_rmdir;
-	}
-
-	if (!debugfs_create_file(
-		    "reg_read", S_IRUSR, attach->dentry, attach,
-		    &iommu_debug_attachment_reg_read_fops)) {
-		pr_err("Couldn't create iommu/attachments/%s/reg_read debugfs file for domain 0x%p\n",
-		       dev_name(dev), domain);
-		goto err_rmdir;
-	}
-
-	if (!debugfs_create_file(
-		    "reg_write", S_IRUSR, attach->dentry, attach,
-		    &iommu_debug_attachment_reg_write_fops)) {
-		pr_err("Couldn't create iommu/attachments/%s/reg_write debugfs file for domain 0x%p\n",
-		       dev_name(dev), domain);
-		goto err_rmdir;
-	}
-
-	return 0;
-
-err_rmdir:
-	debugfs_remove_recursive(attach->dentry);
-	return -EIO;
-}
-
-void iommu_debug_domain_add(struct iommu_domain *domain)
-{
-	struct iommu_debug_attachment *attach;
-
-	mutex_lock(&iommu_debug_attachments_lock);
-
-	attach = kmalloc(sizeof(*attach), GFP_KERNEL);
-	if (!attach)
-		goto out_unlock;
-
-	attach->domain = domain;
-	attach->dev = NULL;
-	list_add(&attach->list, &iommu_debug_attachments);
-
-out_unlock:
-	mutex_unlock(&iommu_debug_attachments_lock);
-}
-
-void iommu_debug_domain_remove(struct iommu_domain *domain)
-{
-	struct iommu_debug_attachment *it;
-
-	mutex_lock(&iommu_debug_attachments_lock);
-	list_for_each_entry(it, &iommu_debug_attachments, list)
-		if (it->domain == domain && it->dev == NULL)
-			break;
-
-	if (&it->list == &iommu_debug_attachments) {
-		WARN(1, "Couldn't find debug attachment for domain=0x%p",
-				domain);
-	} else {
-		list_del(&it->list);
-		kfree(it);
-	}
-	mutex_unlock(&iommu_debug_attachments_lock);
-}
-
 void iommu_debug_attach_device(struct iommu_domain *domain,
 			       struct device *dev)
 {
 	struct iommu_debug_attachment *attach;
+	struct iommu_group *group;
+
+	group = iommu_group_get(dev);
+	if (!group)
+		return;
+
+	attach = kzalloc(sizeof(*attach), GFP_KERNEL);
+	if (!attach)
+		return;
+
+	attach->domain = domain;
+	attach->group = group;
+	INIT_LIST_HEAD(&attach->list);
 
 	mutex_lock(&iommu_debug_attachments_lock);
+	list_add(&attach->list, &iommu_debug_attachments);
+	mutex_unlock(&iommu_debug_attachments_lock);
+}
 
-	list_for_each_entry(attach, &iommu_debug_attachments, list)
-		if (attach->domain == domain && attach->dev == NULL)
-			break;
+void iommu_debug_domain_remove(struct iommu_domain *domain)
+{
+	struct iommu_debug_attachment *it, *tmp;
 
-	if (&attach->list == &iommu_debug_attachments) {
-		WARN(1, "Couldn't find debug attachment for domain=0x%p dev=%s",
-		     domain, dev_name(dev));
-	} else {
-		attach->dev = dev;
-
-		/*
-		 * we might not init until after other drivers start calling
-		 * iommu_attach_device. Only set up the debugfs nodes if we've
-		 * already init'd to avoid polluting the top-level debugfs
-		 * directory (by calling debugfs_create_dir with a NULL
-		 * parent). These will be flushed out later once we init.
-		 */
-
-		if (debugfs_attachments_dir)
-			iommu_debug_attach_add_debugfs(attach);
+	mutex_lock(&iommu_debug_attachments_lock);
+	list_for_each_entry_safe(it, tmp, &iommu_debug_attachments, list) {
+		if (it->domain != domain)
+			continue;
+		list_del(&it->list);
+		iommu_group_put(it->group);
+		kfree(it);
 	}
 
 	mutex_unlock(&iommu_debug_attachments_lock);
 }
 
-void iommu_debug_detach_device(struct iommu_domain *domain,
-			       struct device *dev)
-{
-	struct iommu_debug_attachment *it;
-
-	mutex_lock(&iommu_debug_attachments_lock);
-	list_for_each_entry(it, &iommu_debug_attachments, list)
-		if (it->domain == domain && it->dev == dev)
-			break;
-
-	if (&it->list == &iommu_debug_attachments) {
-		WARN(1, "Couldn't find debug attachment for domain=0x%p dev=%s",
-		     domain, dev_name(dev));
-	} else {
-		/*
-		 * Just remove debugfs entry and mark dev as NULL on
-		 * iommu_detach call. We would remove the actual
-		 * attachment entry from the list only on domain_free call.
-		 * This is to ensure we keep track of unattached domains too.
-		 */
-
-		debugfs_remove_recursive(it->dentry);
-		it->dev = NULL;
-	}
-	mutex_unlock(&iommu_debug_attachments_lock);
-}
-
-static int iommu_debug_init_tracking(void)
-{
-	int ret = 0;
-	struct iommu_debug_attachment *attach;
-
-	mutex_lock(&iommu_debug_attachments_lock);
-	debugfs_attachments_dir = debugfs_create_dir("attachments",
-						     iommu_debugfs_top);
-	if (!debugfs_attachments_dir) {
-		pr_err("Couldn't create iommu/attachments debugfs directory\n");
-		ret = -ENODEV;
-		goto out_unlock;
-	}
-
-	/* set up debugfs entries for attachments made during early boot */
-	list_for_each_entry(attach, &iommu_debug_attachments, list)
-		if (attach->dev)
-			iommu_debug_attach_add_debugfs(attach);
-
-out_unlock:
-	mutex_unlock(&iommu_debug_attachments_lock);
-	return ret;
-}
-
-static void iommu_debug_destroy_tracking(void)
-{
-	debugfs_remove_recursive(debugfs_attachments_dir);
-}
-#else
-static inline int iommu_debug_init_tracking(void) { return 0; }
-static inline void iommu_debug_destroy_tracking(void) { }
 #endif
 
 #ifdef CONFIG_IOMMU_TESTS
@@ -2045,9 +1773,6 @@
 
 static int iommu_debug_init(void)
 {
-	if (iommu_debug_init_tracking())
-		return -ENODEV;
-
 	if (iommu_debug_init_tests())
 		return -ENODEV;
 
@@ -2057,7 +1782,6 @@
 static void iommu_debug_exit(void)
 {
 	platform_driver_unregister(&iommu_debug_driver);
-	iommu_debug_destroy_tracking();
 	iommu_debug_destroy_tests();
 }